Proposals/Bounding boxes

From SVG

Re-defining objectBoundingBox and userSpaceOnUse

Many SVG graphical effects (gradient and pattern paint servers, filters, clipping paths, and masks) have an option, or multiple options, to use the main coordinate system for the element they effect (userSpaceOnUse units) or to use a scale relative to that element's bounding box (objectBoundingBox units). A similar switch applies to markers, but with different possible values.

This system has three main limitations:

  • The objectBoundingBox is relative to the shape fill region, and does not include strokes or markers, so it often does not provide an intuitive scale. In the case of straight horizontal/vertical lines, the object bounding box is degenerate and causes an error.
  • There is no clearly defined extension of these concepts to HTML/CSS layout, which has caused confusing and inconsistent implementations as clipping, masks, and filters are extended to all elements.
  • Because the object bounding box transformation is applied as a non-uniform scale transformation, length units and percentages are not useful. The specs explicitly re-define how percentages should be interpretted for certain attributes to make them more useful, but that just increases the confusion (why should percentages work for the size of the mask/pattern tile but not for the contents?).

This proposal outlines a way to address these limitations, by

  • Separating the definition of a reference box for scaling (which would be set at the time the effect is applied) from the scale units (which would be an intrinsic feature of the effects element);
  • Introducing a new scale units option (tentatively called objectSpace) that supports useful percentages and lengths for effects that are scaled to the referencing element.

Current Status (June 2015)

In SVG 1.1, the options for the various *Units attributes are

  • userSpaceOnUse, which should mean to use the scale and coordinate system origin in place for the context element (this is not implemented consistently)
  • objectBoundingBox, which applies a non-uniform scale to the context element coordinate system such that one unit equals the width/height of the shape's bounding box.

The default bounding box does not include any stroke or marker area, so for straight horizontal/vertical lines the bounding box has zero area and the scaling transform is degenerate; the paint server then generates an error, and the fallback color (if any) is used.

In SVG 2, we add definitions for bounding boxes that include the stroke or even stroke plus markers (decorations). The Masking Module uses the terms fill-box and stroke-box to define the bounding box with and without the stroke. It would be preferable to be able to use these for object bounding box units. However, this should be a setting associated with a given use of a paint server, not with the paint server element itself.

That said, a userSpaceOnUse paint server/mask/filter is not interchangeable with an objectBoundingBox version, because the scale of units is so completely different. For patterns, there is also the viewBox attribute, which causes patternContentUnits to be ignored, and creates a uniform or non-uniform scaling transformation, but still doesn't reset the ratio between percentages and user units.

The CSS Masking module tries to extend the idea of unit types with control over the reference box, but the current spec text is very problematic:

  • Clip paths defined entirely in CSS (using the shape functions) have a behavior that is in-between objectBoundingBox and userSpaceOnUse: user space units are used, but the origin is re-set to the reference box.
  • It re-defines objectBoundingBox units in a way that is not backwards compatible, to be the same as clip-paths defined entirely in CSS. ("The coordinate system has its origin at the top left corner of the bounding box of the element to which the clipping path applies to and the same width and height of this bounding box. User coordinates are sized equivalently to the CSS px unit.") Browsers so far have not implemented this change.
  • It does not clearly define how userSpaceOnUse masks and clip-paths should work on non-SVG content, with many complaints from authors as a result.

The Masking module also introduces the option of controlling which reference box is used. For clipping paths, this is currently specified within the single clip-path property, for masks it has been decomposed into a separate properties inspired by the CSS background syntax (although they could also be specified in the mask shorthand). They adopt the terms `fill-box`, `stroke-box`, and `view-box` as equal options for the CSS `content-box`, `padding-box`, `border-box`, and `margin-box`, with rules about how the SVG boxes map to CSS boxes (treat as border-box) and vice versa (treat as fill-box).

Things get more complicated with masking and as currently specced would be very problematic for SVG content (e.g., by default any mask would clip to the fill-box).


Proposal

I recommend that the choice of which box to use for scaling be introduced as an independent and orthogonal issue to what type of scaling to use.

Setting the Scale Units

What type of scaling would be defined by the xxxUnits attributes on the graphic elements (pattern, gradients, masks, clipping paths, or filters). A new option would be added that would reset the origin and percentages, but would not change the scale of units, similar to how CSS shapes as clipping paths work.

userSpaceOnUse
Use the units and coordinate system origin in effect for the reference scaling box.
objectSpace
Use the same units and scale as userSpaceOnUse, but re-position the origin to the minimum corner of the reference scaling box (e.g., the top-left corner if transformations haven't been used), and reset 100% to the width/height of the reference box. This would be the implicit behavior when using CSS <image> values as paint, just as it is the implicit default for masking images and clip-paths defined with CSS shape functions.
objectBoundingBox
Translate the coordinate system origin to the minimum corner of the reference scaling box, and then apply a non-uniform scale such that 1 user unit in each of the horizontal and vertical direction equals the width and height of the reference scaling box.


Defining the Reference Box

There would be three types of reference scaling boxes:

  • SVG view boxes (view-box, farthest-view-box)
  • SVG bounding boxes (fill-box, stroke-box, and decorated-box)
  • CSS layout boxes (margin-box | border-box | padding-box | content-box)

For SVG view boxes, the coordinate system origin is that created by the viewBox property (or automatic viewBox) of the element's viewportElement or farthestViewportElement (view-box and farthest-view-box respectively). The height/width and minimum corner of the box are those directly defined by the viewBox, or by the actual dimensions of that element if it does not have an explicit viewBox. These options will allow you to use image fills (including CSS gradients), or paint servers defined with objectBoundingBox units, in a way similar to userSpaceOnUse SVG gradients (although results would not be identical in all cases, since the origin would be re-set).

For SVG bounding boxes, the coordinate system is that used when drawing that shape (and will therefore be the same as for view-box when userSpaceOnUse scaling is used). The height/width and minimum corner are those returned by the corresponding getBBox() method.

For CSS layout boxes, the coordinate system is the page coordinate system. The height/width and minimum corner are those defined by that specific layout box. For SVG elements that do not have their own CSS layout box, the CSS layout box of the farthest viewport element would be used.

In all cases, any transformations on the element or its ancestors affect the direction, origin, and scale of the coordinate system, just as they affect the direction, origin, and scale of the values returned by the getBBox() methods.