1. Introduction
Text and SVG graphical elements that define a shape—path
elements, basic shapes such as rect
—
This specification describes how text and SVG graphical elements are filled and stroked, by defining a number of properties that control the appearance and shape of an element’s fill and stroke.
Insert Specifying Paint from SVG2.
Deal with text decoration. See issue and other issue.
2. Paint
Paint is what makes abstract geometries visible. It consists of colors, patterns, images, gradients, and other 2D graphics. The | accentcolor | accentcolortext | activeborder | activecaption | activetext | aliceblue | antiquewhite | appworkspace | aqua | aquamarine | azure | background | beige | bisque | black | blanchedalmond | blue | blueviolet | brown | burlywood | buttonborder | buttonface | buttonhighlight | buttonshadow | buttontext | cadetblue | canvas | canvastext | captiontext | chartreuse | chocolate | coral | cornflowerblue | cornsilk | crimson | currentcolor | cyan | darkblue | darkcyan | darkgoldenrod | darkgray | darkgreen | darkgrey | darkkhaki | darkmagenta | darkolivegreen | darkorange | darkorchid | darkred | darksalmon | darkseagreen | darkslateblue | darkslategray | darkslategrey | darkturquoise | darkviolet | deeppink | deepskyblue | dimgray | dimgrey | dodgerblue | field | fieldtext | firebrick | floralwhite | forestgreen | fuchsia | gainsboro | ghostwhite | gold | goldenrod | gray | graytext | green | greenyellow | grey | highlight | highlighttext | honeydew | hotpink | inactiveborder | inactivecaption | inactivecaptiontext | indianred | indigo | infobackground | infotext | ivory | khaki | lavender | lavenderblush | lawngreen | lemonchiffon | lightblue | lightcoral | lightcyan | lightgoldenrodyellow | lightgray | lightgreen | lightgrey | lightpink | lightsalmon | lightseagreen | lightskyblue | lightslategray | lightslategrey | lightsteelblue | lightyellow | lime | limegreen | linen | linktext | magenta | mark | marktext | maroon | mediumaquamarine | mediumblue | mediumorchid | mediumpurple | mediumseagreen | mediumslateblue | mediumspringgreen | mediumturquoise | mediumvioletred | menu | menutext | midnightblue | mintcream | mistyrose | moccasin | navajowhite | navy | none | oldlace | olive | olivedrab | orange | orangered | orchid | palegoldenrod | palegreen | paleturquoise | palevioletred | papayawhip | peachpuff | peru | pink | plum | powderblue | purple | rebeccapurple | red | rosybrown | royalblue | saddlebrown | salmon | sandybrown | scrollbar | seagreen | seashell | selecteditem | selecteditemtext | sienna | silver | skyblue | slateblue | slategray | slategrey | snow | springgreen | steelblue | tan | teal | thistle | threeddarkshadow | threedface | threedhighlight | threedlightshadow | threedshadow | tomato | transparent | turquoise | violet | visitedtext | wheat | white | whitesmoke | window | windowframe | windowtext | yellow | yellowgreen"><color> type represents 0-dimensional paint; it is defined in [CSS3-COLOR]. The <paint> type represents 2-dimensional paint, and its syntax is:
<paint> = none | <image> | )"><svg-paint>
- none
-
Indicates no paint is applied.
- <image>
-
Defined in [CSS3-IMAGES] and includes image references and gradients.
- )"><svg-paint>
-
References SVG paint servers, and is defined in § 2.1 SVG-Specific Paints. A <url> value here is treated as an ambiguous image URL.
2.1. SVG-Specific Paints
<svg-paint> = child | child( <integer> )
- child
-
Refers to the last child paint server element of the element being painted.
If there is no such child, behaves as none.
- child(<integer>)
-
This functional notation, which accepts a positive integer as its argument, refers to the nth child paint server element (1-indexed, as for ::nth-child()) of the element being painted.
If there is no such child, behaves as none.
Arguments less than 1 are invalid.
context-fill and context-stroke need to be worked into the spec, but they belong as keywords on each individual stroke and fill sub-property, not as a <paint> value. Needs more WG discussion, and confirmation that this is an SVG2 feature that will be kept, before we do the relatively substantial edits for it.
CSS Images 4 § 2.7.1 Paint Sources defines how to handle SVG paint server coordinate spaces. Copy, ref, or something else here?
3. Fills
A fill paints within the outlines of an SVG shape or the glyphs of an inline (or SVG text) box. Similar to backgrounds [CSS3BG], fills can be a solid color (fill-color) and/or an image pattern (fill-image etc.).
It is strongly recommended to use the fill shorthand when setting fills. The individual painting longhands should only be used when it’s needed to alter one individual aspect.
Add a box-break property that is a shorthand for box-decoration-break, fill-break, and stroke-break?
3.1. Layering Multiple Fills
The fill of a box can have multiple layers. The number of layers is determined by the number of comma-separated values for the fill-image property. A value of none still creates a layer.
List-valued properties interact exactly as for background images, defined in CSS Backgrounds 3 § 2.1 Layering Multiple Background Images.
3.2. Fill Geometry
3.2.1. Winding Rule for SVG shapes: the fill-rule property
Name: | fill-rule |
---|---|
Value: | nonzero | evenodd |
Initial: | nonzero |
Applies to: | SVG shapes |
Inherited: | yes |
Percentages: | N/A |
Computed value: | as specified |
Canonical order: | per grammar |
Animation type: | discrete |
Media: | visual |
The fill-rule property indicates the rule used to determine what parts of the canvas are included inside the shape. For a simple, non-intersecting path, it is intuitively clear what region lies “inside”; however, for a more complex path, such as a path that intersects itself or where one subpath encloses another, the interpretation of “inside” is not so obvious.
The fill-rule property provides two options for how the inside of a shape is determined:
- nonzero
-
This rule determines the “insideness” of a point on the canvas
by drawing a ray from that point to infinity in any direction
and then examining the places where a segment of the shape crosses the ray.
Starting with a count of zero,
add one each time a path segment crosses the ray from left to right
and subtract one each time a path segment crosses the ray from right to left.
After counting the crossings,
if the result is zero then the point is outside the path.
Otherwise, it is inside.
The effect of a nonzero fill rule on paths with self-intersections and enclosed subpaths. - evenodd
-
This rule determines the “insideness” of a point on the canvas
by drawing a ray from that point to infinity in any direction
and counting the number of path segments from the given shape that the ray crosses.
If this number is odd,
the point is inside;
if even,
the point is outside.
The effect of an evenodd fill rule on paths with self-intersections and enclosed subpaths.
Note: The above descriptions do not specify what to do if a path segment coincides with or is tangent to the ray. Since any ray will do, one may simply choose a different ray that does not have such problem intersections.
This property does not apply to text (whether SVG or CSS), as the “inside” of a text glyph is intrinsically defined.
3.2.2. Fragmented Fills: the fill-break property
Name: | fill-break |
---|---|
Value: | bounding-box | slice | clone |
Initial: | bounding-box |
Applies to: | all elements |
Inherited: | yes? |
Percentages: | N/A |
Computed value: | as specified |
Canonical order: | per grammar |
Animation type: | discrete |
Media: | visual |
This property specifies how the geometry of a fragmented box is treated for fills.
Values have the following meanings: COPY FROM FRAGMENTATION
3.3. Fill Paint
3.3.1. Fill Color: the fill-color property
This property sets the fill color of an element. This color is drawn behind any fill images.
It’s recommended that authors use the color property to set the color of text, rather than fill-color.
Unlike background-color, the fill color must only be drawn if the final layer of fill-image is none or an invalid image. If it’s a valid image, the fill color must not be drawn.
Note: This is required to match the legacy behavior of SVG’s fill property, which built in a "fallback color" to the single-layer image syntax. In CSS this is better achieved with the image() function, which makes fallback explicit.
fill-color should be layerized, like stroke-color, so the two sets of properties are maximally consistent.
SVG user agents must include the following rules (or their equivalent) in their user agent style sheet:
@namespace svg"http://www.w3.org/2000/svg" ; svg : svg:root, *|*:not ( svg|*) > svg:svg{ fill-color : black; }
3.3.2. Fill Image Sources: the fill-image property
Name: | fill-image |
---|---|
Value: | <paint># |
Initial: | none |
Applies to: | text and SVG shapes |
Inherited: | yes |
Percentages: | N/A |
Computed value: | as specified, with any <image> computed |
Canonical order: | per grammar |
Animation type: | repeatable list |
Media: | visual |
This property sets the fill images of an element. Images are drawn with the first specified one on top (closest to the user) and each subsequent image behind the previous one. Values are interpreted identically to background-image, mutatis mutandi.
3.3.3. Fill Positioning Area: the fill-origin property
Name: | fill-origin |
---|---|
Value: | match-parent | fill-box | stroke-box | content-box | padding-box | border-box |
Initial: | match-parent |
Applies to: | all elements |
Inherited: | no |
Percentages: | N/A |
Computed value: | as specified |
Canonical order: | per grammar |
Animation type: | discrete |
Media: | visual |
This property specifies the coordinate system of the fill, setting the fill positioning area. Values have the following meanings:
- match-parent
- Use the same fill positioning area as the parent. If it has no parent, use the initial containing block.
- content-box
- padding-box
- border-box
- padding-box
- Use the box’s own content-box/padding-box/border-box as the fill positioning area. For SVG shapes, content-box and padding-box are treated as fill-box, while border-box is treated as stroke-box.
- fill-box
- For SVG shapes or SVG text, use the object bounding box of the element. For CSS boxes, use the bounding box of the text glyph outlines of the element and all in-flow or floated descendants.
- stroke-box
- For SVG shapes or SVG text, use the stroke bounding box of the element. For CSS boxes, use the bounding box of the text glyph stroke outlines of the element and all in-flow or floated descendants.
The SVG UA style sheet is amended to include the following rules:
svg : svg{ fill-origin : content-box; }
The fill painting area is infinite in size. When painting, the fill color/images are intersected with the glyph areas of the affected text, or the fill geometry of the affected SVG shape.
SVG paint servers carry around their own originator information in *Units attributes, but maybe SVGWG can add a new value that makes them pay attention to the CSS originator. Alternately, we can add an auto initial value here, which defers to the paint server’s information if you’re referring to one, and otherwise is match-parent.
3.3.4. Positioning Fill Images: the fill-position property
Name: | fill-position |
---|---|
Value: | <position># |
Initial: | 0% 0% |
Applies to: | text and SVG shapes |
Inherited: | yes |
Percentages: | n/a |
Computed value: | A list, each item consisting of: a pair of offsets (horizontal and vertical) from the top left origin each given as a combination of an absolute length and a percentage |
Canonical order: | per grammar |
Animation type: | repeatable list |
Media: | visual |
If fill images have been specified, this property specifies their initial position (after any resizing) within their corresponding fill positioning area. Values are interpreted identically to background-position, mutatis mutandi.
3.3.5. Sizing Fill Images: the fill-size property
Name: | fill-size |
---|---|
Value: | <bg-size># |
Initial: | auto |
Applies to: | text and SVG shapes |
Inherited: | yes |
Percentages: | n/a |
Computed value: | as specified, but with lengths made absolute and omitted auto keywords filled in |
Canonical order: | per grammar |
Animation type: | repeatable list |
Media: | visual |
Specifies the size of the fill images. Values are interpreted identically to background-size, mutatis mutandi.
3.3.6. Tiling Fill Images: the fill-repeat property
Name: | fill-repeat |
---|---|
Value: | <repeat-style># |
Initial: | repeat |
Applies to: | text and SVG shapes |
Inherited: | yes |
Percentages: | n/a |
Computed value: | A list, each item consisting of: two keywords, one per dimension |
Canonical order: | per grammar |
Animation type: | discrete |
Media: | visual |
Specifies how fill images are tiled after they have been sized and positioned. Values are interpreted identically to background-repeat, mutatis mutandi.
3.3.7. Fill Shorthand: the fill property
Name: | fill |
---|---|
Value: | <'background'> with modifications |
Initial: | See individual properties |
Applies to: | See individual properties |
Inherited: | See individual properties |
Percentages: | N/A |
Computed value: | See individual properties |
Canonical order: | per grammar |
Animation type: | See individual properties |
Media: | visual |
This property is a shorthand that sets all of the fill painting properties—
What should be the default value for fill-origin when set via this shorthand? content-box or fill-box? fill-box works better for fancy/overflowing fonts like Zapfino, but it’s much more expensive to calculate than content-box.
SVG has special color fallback syntax—
3.4. Fill Transparency
3.4.1. Fill Opacity: the fill-opacity property
Name: | fill-opacity |
---|---|
Value: | <'opacity'> |
Initial: | 1 |
Applies to: | text and SVG shapes |
Inherited: | yes |
Percentages: | N/A |
Computed value: | the specified value converted to a <number>, clamped to the range [0,1] |
Canonical order: | per grammar |
Animation type: | by computed value |
Media: | visual |
The fill-opacity property specifies the opacity of the painting operation used to fill the element. As with opacity, a value of 0 or 0% means fully transparent, and a value of 1 or 100% means fully opaque.
4. Strokes (Outlines)
A stroke draws a border along the outlines of an SVG shape or the glyphs of an inline (or SVG text) box. The resulting area can be filled similar to the fill area. Strokes can be a solid color (stroke-color) and/or an image pattern (stroke-image etc.).
It is strongly recommended to use the stroke shorthand when setting strokes. The individual painting longhands should only be used when it’s needed to alter one individual aspect.
By default, strokes are drawn on top of fills; the drawing order is controlled by the paint-order property.
4.1. Layering Multiple Strokes
The stroke of a box can have multiple layers. The number of layers is determined by the larger of the number of comma-separated values for the stroke-image property and the number of comma-separated values for the stroke-color property. A value of none still creates a layer.
If the two properties have different lengths, the values are aligned from the end, with missing front values for stroke-color defaulting to transparent, and missing front values for stroke-image defaulting to none.
List-valued properties interact exactly as for background images, defined in CSS Backgrounds 3 § 2.1 Layering Multiple Background Images.
stroke-color must front-fill with transparent, otherwise multiple image layers will randomly fall back to solid colors if the image fails to load. Do we want to have strokes and fills front-fill with initial values in general, or should the rest of these properties repeat their lists as they do for backgrounds?
4.2. Stroke Geometry
We currently layerize the stroke paint props, and stroke-width, per SVGWG resolution. Should we layerize the other geometry properties? They have similar use-cases, albeit somewhat more niche, and consistency in API is important. Worth the implementation/testing effort? Hard case: stroke-dasharray, because it has "commas anywhere lol" syntax.
4.2.1. Stroke Thickness: the stroke-width property
Name: | stroke-width |
---|---|
Value: | <length-percentage># |
Initial: | 1px |
Applies to: | text and SVG shapes |
Inherited: | yes |
Percentages: | relative to the scaled viewport size |
Computed value: | the absolute length, or percentage |
Canonical order: | per grammar |
Animation type: | by computed value |
Media: | visual |
This property specifies the width of the strokes on the outline. A zero value causes no stroke to be painted for that layer. Negative values are invalid.
The scaled viewport size is the geometric mean of the viewport width and height.
4.2.2. Stroke Positioning: the stroke-align property
Name: | stroke-align |
---|---|
Value: | center | inset | outset |
Initial: | center |
Applies to: | text and SVG shapes |
Inherited: | yes |
Percentages: | N/A |
Computed value: | as specified |
Canonical order: | per grammar |
Animation type: | discrete |
Media: | visual |
This property allows the author to align a stroke along the outline.
- center
- The stroke for each subpath is centered on the outline.
- inset
-
The stroke for each subpath
lies on the “inside” of the outline
(into the fill area).
The stroke-linejoin property must be ignored.
Why is stroke-linejoin ignored? It’s still needed for interior corners.
- outset
- The stroke for each subpath lies on the “outside” of the outline (outside the fill area).
How does this apply to open path segments? One suggestion is to alias outset to left and inset to right for open paths. How are end caps handled?
-
Stroke with stroke-align center value.
-
Fill region.
-
Stroke with stroke-align inset value per spec (? stroke only paints inside fill region).
-
Stroke with stroke-align outset value (implemented by blocking out fill region).
-
Stroke with stroke-align inset value, alternative interpretation.
-
Stroke with stroke-align outset value per spec (?).
-
Stroke with stroke-align alternative left value; pink shows round line-cap.
How does this apply to paths with loops? Is the region inside the red circle in the below figure part of the stroked (as shown)? Are internal edges stroked if the fill rule is nonzero; if so how? (Shown without stroking below.)
How are dashes handled? Are they based on original path?
-
Normal dash pattern.
-
Dash pattern based on dash pattern or original path. Note light-gray region which is from inside dash part (simply using clipping path does not yield proper result).
-
Dash pattern based on center of offset path. Light gray represents dash pattern based on center of inset path.
4.2.3. Stroke End Shapes: the stroke-linecap property
Name: | stroke-linecap |
---|---|
Value: | butt | round | square |
Initial: | butt |
Applies to: | text and SVG shapes |
Inherited: | yes |
Percentages: | N/A |
Computed value: | as specified |
Canonical order: | per grammar |
Animation type: | discrete |
Media: | visual |
stroke-linecap specifies the shape to be used at the end of open subpaths
(such as the segments of a dashed stroke,
or an unclosed path
element)
when they are stroked.
The possible values are:
- butt
- The stroke for each subpath does not extend beyond its two endpoints. (A zero length subpath will therefore not have any stroke.)
- round
- At each end of each subpath, the stroke is extended by a half circle with a radius equal to half the stroke width. (The stroke for a zero-length subpath is a full circle centered at the subpath’s point.)
- square
-
At the end of each subpath,
the stroke is extended by a rectangle
with the same width as the stroke width and whose length is half of the stroke width.
(The stroke for a zero-length subpath
is a square with side length equal to the stroke width,
centered at the subpath’s point,
and oriented such that two of its sides are parallel to the effective tangent at that subpath’s point.
See § 4.6 Computing the Shape of the Stroke for details on how to determine the tangent at a zero-length subpath.)
Adding a rectangle to the end of dashes on a curved outline looks bad. It should just extend the dash by stroke-width/2, following the outline.
See the definition of the cap shape below for a more precise description of the shape a line cap will have.
4.2.4. Stroke Corner Shapes: the stroke-linejoin property
Name: | stroke-linejoin |
---|---|
Value: | [ crop | arcs | miter ] || [ bevel | round | fallback ] |
Initial: | miter |
Applies to: | text and SVG shapes |
Inherited: | yes |
Percentages: | N/A |
Computed value: | as specified |
Canonical order: | per grammar |
Animation type: | discrete |
Media: | visual |
stroke-linejoin specifies the shape to be used at the corners of paths or basic shapes when they are stroked. It has two parts: [ crop | arcs | miter ] specifies whether or not, and how, to extend the corner of the stroke; [ bevel | round | fallback ] specifies how to render the "cap" of the corner when it’s limited in length by stroke-miterlimit.
- crop
-
The stroke extends the minimal amount past the corner
necessary to form a convex corner.
This acts identically to miter, but forces stroke-miterlimit to behave as if it had its minimum value of 1.
- miter
- A sharp corner is used to join path segments. The corner is formed by extending the outer edges of the stroke at the tangents of the path segments until they intersect.
- arcs
- An arcs corner is used to join path segments. The arcs shape is formed by extending the outer edges of the stroke at the join point with arcs that have the same curvature as the outer edges at the join point.
- bevel
- The corner is cropped at the stroke-miterlimit by the perpendicular to its diagonal.
- round
- The corner is cropped as for bevel, and a filled arc tangent to the truncated stroke edges is appended to round the corner.
- fallback
-
When the stroke-miterlimit is exceeded,
this behaves as if crop bevel were specified.
Can we just remove fallback? It’s a stupid value, useful only because we didn’t have the clip-at-miterlimit behavior in SVG1. Question is if people are mostly just *accidentally* getting the bevel behavior right now, and would be okay with their joins extending up to the miterlimit and only getting the excess corners clipped, or if they actually wanted the discontinuous behavior currently specified. The breakpoint is between 29 and 30 degrees.
If [ crop | arcs | miter ] is omitted, it defaults to crop. If [ bevel | round | fallback ] is omitted, it defaults to fallback.
4.2.5. Stroke Corner Limits: the stroke-miterlimit property
Name: | stroke-miterlimit |
---|---|
Value: | <number> |
Initial: | 4 |
Applies to: | text and SVG shapes |
Inherited: | yes |
Percentages: | N/A |
Computed value: | a number |
Canonical order: | per grammar |
Animation type: | discrete |
Media: | visual |
This property specifies the maximum size of a miter or arcs join
when two stroke segments meet at a corner.
(The apparent “size” of the join—
This property has no effect when stroke-align is inset.
Values have the following meanings:
- <number>
-
Specifies the limit on the join’s size
as a ratio of its diagonal to the stroke-width.
Values less than 1 are invalid (and make the declaration invalid).
For a miter linejoin, the length of the diagonal is calculated from the angle between the two segments as
stroke-width / sin(θ/2)
. The linejoin is clipped perpendicular to the line bisecting the angle between the two path segments.For an arcs linejoin, the length of the diagonal is calculated along a circular arc that is tangent to the line bisecting the angle between the two segments at the point the two segments intersect and passes through the end point of the join. The linejoin is clipped perpendicular to this arc.
Should we add angles? The miterlimit number has physical justification, but it’s difficult to understand without experimentation.
4.2.6. Fragmented Strokes: the stroke-break property
Name: | stroke-break |
---|---|
Value: | bounding-box | slice | clone |
Initial: | bounding-box |
Applies to: | all elements |
Inherited: | ? |
Percentages: | N/A |
Computed value: | as specified |
Canonical order: | per grammar |
Animation type: | discrete |
Media: | visual |
This property specifies how the geometry of a fragmented box is treated for strokes.
Values have the following meanings: COPY FROM FRAGMENTATION
4.3. Stroke Dashing
4.3.1. Stroke Dash Patterns: the stroke-dasharray property
Name: | stroke-dasharray |
---|---|
Value: | none | <length-percentage>+# |
Initial: | none |
Applies to: | text and SVG shapes |
Inherited: | yes |
Percentages: | relative to the scaled viewport size |
Computed value: | as specified |
Canonical order: | per grammar |
Animation type: | repeatable list |
Media: | visual |
This property controls the pattern of dashes and gaps used to stroke paths.
- none
- No dashing: the stroke is drawn continuously.
- <length-percentage>+#
-
Specifies a dashing pattern to use.
Each <length-percentage> value represents the length of the next dash or gap
(beginning with the first dash and alternating from there)
of the stroke.
The pattern repeats over the length of the stroke.
(If the number of values is odd,
the pattern behaves as if it was duplicated to yield an even number of values.)
The dashing pattern is reset and begins anew at the start of each subpath.
Negative values are invalid. If all values are zero, it is treated as none.
SVG allows comma separation. Do we need to allow this in CSS for back-compat? (Please say no.)
Need a way to specify dash lengths relative to the width of the stroke. (For instance, to do square dashes.)
Need a way to specify dash lengths relative to the length of the path. (For instance, to do the "self-drawing SVG" thing without script.)
4.3.2. Stroke Dash Start Position: the stroke-dashoffset property
Name: | stroke-dashoffset |
---|---|
Value: | <length-percentage> |
Initial: | 0 |
Applies to: | text and SVG shapes |
Inherited: | yes |
Percentages: | relative to the scaled viewport size |
Computed value: | as specified |
Canonical order: | per grammar |
Animation type: | repeatable list |
Media: | visual |
This property specifies the distance into the repeated dash pattern to start dashing at the beginning of the path. Values can be negative.
See § 4.6.3 Dash Positions for a more precise description of positions along a path that dashes will be placed.
4.3.3. Corner Control: the stroke-dash-corner and stroke-dash-justify properties
Name: | stroke-dash-corner |
---|---|
Value: | none | <length> |
Initial: | none |
Applies to: | text and SVG shapes |
Inherited: | yes |
Percentages: | N/A |
Computed value: | specified value, with lengths made absolute |
Canonical order: | per grammar |
Animation type: | by computed value if <length>, otherwise discrete |
Media: | visual |
The stroke-dash-corner property controls whether a dash is always painted at the vertices of a stroked shape. The points at which a dash corner is painted include the start and end points of every segment within the shape’s equivalent path.
- none
-
This property has no special effect on dashing: the dashes are positioned/sized continuously along the path.
- <length>
-
A dash of the given <length> must be painted at each vertex of the shape. For an open shape, the first corner dash is positioned so that it begins at the start of the path, and the last corner dash so that it ends at the end of the path. The other corner dashes of an open shape, and all corner dashes of a closed shape, are positioned so that they are centered on their vertex.
Should the corner dash at the first and last vertex of an open shape be half the length of the others? Should this be author controllable?
Should there be a way to specify a padding, so that any dash pattern between the corner dashes does not not run up against them?
The corner needs to take over the role of the first dash in the dash pattern, so the "interior" of each segment starts and ends with a gap. Automatically skip the first dash in the pattern in each segment?
Keyword to use the length of the first dash as the corner.
When stroke-dash-corner is not none, dash patterns (as specified by stroke-dasharray) are repeated separately on each path segment, in the space between the segment’s corner dashes.
Need to define what happens when corner dashes would overlap.
Should we auto-trigger justification when this property is active? Without it, you’ll get some dumb-looking results by default.
Name: | stroke-dash-justify |
---|---|
Value: | none | [ stretch | compress ] || [ dashes || gaps ] |
Initial: | none |
Applies to: | text and SVG shapes |
Inherited: | yes |
Percentages: | N/A |
Computed value: | specified value, with lengths made absolute |
Canonical order: | per grammar |
Animation type: | discrete |
Media: | visual |
The stroke-dash-justify property specifies whether and how a stroke’s dash pattern will be adjusted so that it is repeated a whole number of times along each of an element’s subpaths.
- none
-
No adjustment of the dash pattern is made: it’s clipped at the end of the subpath with however much progress it made to that point.
- stretch
-
Indicates that when the dash pattern does not fit into a subpath a whole number times, the dashes or gaps (or both) will be lengthened so that it does.
- compress
-
Indicates that when the dash pattern does not fit into a subpath a whole number times, the dashes or gaps (or both) will be shortened so that it does.
- dashes
-
Indicates that when a dash pattern is to be stretched or compressed, the length of the dashes will be adjusted.
- gaps
-
Indicates that when a dash pattern is to be stretched or compressed, the length of the gaps will be adjusted.
If neither stretch nor compress is specified, it defaults to whichever would result in less adjustment.
If neither dashes nor gaps is specified, it defaults to ???.
Default should probably be to adjust the gaps only.
The target length that a dash pattern will be adjusted to depends on the value of the stroke-dash-corner property:
-
If stroke-dash-corner is none, then the dash pattern will be adjusted to fit within the length of the subpath a whole number of times.
-
If stroke-dash-corner has any other value, including a zero length, then the dash pattern will be adjusted differently for each path segment of the subpath such that it fits between the two corner dashes at the ends of the segment a whole number of times.
The adjustment of dash and gap length in a dash pattern is done by scaling the lengths by a factor, which is the number closest to 1 that will result in the dash pattern fitting in the target length a whole number of times. If stretch is used, the factor is a number between 1 and 2, while if compress is used, the factor is a number between 0 and 1.
If stretch is specified, but the dash pattern is longer than the target length, no adjustment is performed.
Should it center the segment when it’s too long?
If compress is specified, but the adjustable parts of the dash pattern can’t be compressed enough to fit the dash pattern into the target length, the compressible parts must be set to zero.
Do we want to allow control of whether the stroke ends with a dash or with a gap? For a closed path, you probably want a gap at the end, while for an open path, a dash at the end is probably better. Maybe omit control for this, and just do it automatically based on whether the path is closed?
4.4. Stroke Paint
4.4.1. Stroke Color: the stroke-color property
This property sets the stroke colors of an element. Like stroke-image, stroke colors are drawn with the first specified one on top (closest to the user) and each subsequent color behind the previous one.
Stroke colors are only drawn if the stroke-image on the corresponding layer is none or an invalid image. (In other words, each layer draws either an image or a color, with images winning if both are specified.)
Note: This is required to match the legacy behavior of SVG’s stroke property, which built in a "fallback color" to the single-layer image syntax. In CSS this is better achieved with the image() function, which makes fallback explicit.
4.4.2. Stroke Image Sources: the stroke-image property
Name: | stroke-image |
---|---|
Value: | <paint># |
Initial: | none |
Applies to: | text and SVG shapes |
Inherited: | yes |
Percentages: | N/A |
Computed value: | as specified, with any <image> computed |
Canonical order: | per grammar |
Animation type: | repeatable list |
Media: | visual |
This property sets the stroke images of an element. Images are drawn with the first specified one on top (closest to the user) and each subsequent image behind the previous one. Values are interpreted identically to background-image, mutatis mutandi.
4.4.3. Stroke Positioning Area: the stroke-origin property
Name: | stroke-origin |
---|---|
Value: | match-parent | fill-box | stroke-box | content-box | padding-box | border-box |
Initial: | match-parent |
Applies to: | all elements |
Inherited: | no |
Percentages: | N/A |
Computed value: | as specified |
Canonical order: | per grammar |
Animation type: | discrete |
Media: | visual |
This property specifies the coordinate system of the stroke, setting the stroke positioning area. Values have the following meanings:
- match-parent
- Use the same stroke positioning area as the parent. If it has no parent, use the initial containing block.
- content-box
- padding-box
- border-box
- padding-box
- Use the box’s own content-box/padding-box/border-box as the stroke positioning area. For SVG shapes, content-box and padding-box are treated as fill-box, while border-box is treated as stroke-box.
- fill-box
- For SVG shapes or SVG text, use the object bounding box of the element. For CSS boxes, use the bounding box of the text glyph outlines of the element and all in-flow or floated descendants.
- stroke-box
- For SVG shapes or SVG text, use the stroke bounding box of the element. For CSS boxes, use the bounding box of the text glyph stroke outlines of the element and all in-flow or floated descendants.
The SVG UA style sheet is amended to include the following rules:
svg : svg{ stroke-origin : content-box; }
The stroke painting area is infinite in size. When painting, the stroke color/images are intersected with the glyph strokes of the affected text, or the stroke geometry of the affected SVG shape.
SVG paint servers carry around their own originator information in *Units attributes, but maybe SVGWG can add a new value that makes them pay attention to the CSS originator. Alternately, we can add an auto initial value here, which defers to the paint server’s information if you’re referring to one, and otherwise is match-parent.
4.4.4. Positioning Stroke Images: the stroke-position property
Name: | stroke-position |
---|---|
Value: | <position># |
Initial: | 0% 0% |
Applies to: | text and SVG shapes |
Inherited: | yes |
Percentages: | n/a |
Computed value: | A list, each item consisting of: a pair of offsets (horizontal and vertical) from the top left origin each given as a combination of an absolute length and a percentage |
Canonical order: | per grammar |
Animation type: | repeatable list |
Media: | visual |
If stroke images have been specified, this property specifies their initial position (after any resizing) within their corresponding stroke positioning area. Values are interpreted identically to background-position, mutatis mutandi.
4.4.5. Sizing Stroke Images: the stroke-size property
Name: | stroke-size |
---|---|
Value: | <bg-size># |
Initial: | auto |
Applies to: | text and SVG shapes |
Inherited: | yes |
Percentages: | n/a |
Computed value: | as specified, but with lengths made absolute and omitted auto keywords filled in |
Canonical order: | per grammar |
Animation type: | repeatable list |
Media: | visual |
Specifies the size of the stroke images. Values are interpreted identically to background-size, mutatis mutandi.
4.4.6. Tiling Stroke Images: the stroke-repeat property
Name: | stroke-repeat |
---|---|
Value: | <repeat-style># |
Initial: | repeat |
Applies to: | text and SVG shapes |
Inherited: | yes |
Percentages: | n/a |
Computed value: | A list, each item consisting of: two keywords, one per dimension |
Canonical order: | per grammar |
Animation type: | discrete |
Media: | visual |
Specifies how stroke fill images are tiled after they have been sized and positioned. Values are interpreted identically to background-repeat, mutatis mutandi.
4.4.7. Stroke Shorthand: the stroke property
Name: | stroke |
---|---|
Value: | <'background'> with modifications |
Initial: | See individual properties |
Applies to: | See individual properties |
Inherited: | See individual properties |
Percentages: | N/A |
Computed value: | See individual properties |
Canonical order: | per grammar |
Animation type: | See individual properties |
Media: | visual |
This property is a shorthand that sets all of the stroke painting properties—
4.5. Stroke Transparency
4.5.1. Stroke Opacity: the stroke-opacity property
Name: | stroke-opacity |
---|---|
Value: | <'opacity'> |
Initial: | 1 |
Applies to: | text and SVG shapes |
Inherited: | yes |
Percentages: | N/A |
Computed value: | the specified value converted to a <number>, clamped to the range [0,1] |
Canonical order: | per grammar |
Animation type: | by computed value |
Media: | visual |
The stroke-opacity property specifies the opacity of the painting operation used to stroke the current object. As with opacity, a value of 0 or 0% means fully transparent, and a value of 1 or 100% means fully opaque.
4.6. Computing the Shape of the Stroke
4.6.1. Stroke Paths
In all cases,
all stroking properties which are affected by directionality,
such as those having to do with dash patterns,
must be rendered such that the stroke operation
starts at the same point at which the graphics element starts.
In particular, for path
elements,
the start of the path is the first point of the initial “moveto” command.
For stroking properties such as dash patterns whose computations are dependent on progress along the outline of the graphics element, distance calculations are required to utilize the SVG user agent’s standard Distance along a path algorithms.
When stroking is performed using a complex paint server,
such as a gradient or a pattern
,
the stroke operation must be identical to the result that would have occurred
if the geometric shape defined by the geometry of the current graphics element
and its associated stroking properties
were converted to an equivalent path
element
and then filled using the given paint server.
A subpath consisting of a single “moveto” shall not be stroked.
Any zero length subpath shall not be stroked
if the stroke-linecap property has a value of butt but shall be stroked if the stroke-linecap property has a value of round or square,
producing respectively a circle or a square centered at the given point.
Examples of zero-length subpaths include 'M 10,10 L 10,10'
, 'M 20,20 h 0'
, 'M 30,30 z'
,
and 'M 40,40 c 0,0 0,0 0,0'
.
This should be redundant with the stroke shape computation requirements below. In this section, we should phrase the requirements descriptively rather than normatively.
4.6.2. Stroke Shape
path
, or basic shape is,
taking into account the stroking properties above:
-
Let shape be an empty shape.
-
Let path be the equivalent path of the element.
-
For each subpath of path:
-
Let positions be the dash positions for the subpath.
-
For each pair (start, end) in positions:
-
Let dash be the shape that includes, for all distances between start and end along the subpath, all points that lie on the line perpendicular to the subpath at that distance and which are within distance stroke-width of the point on the subpath at that position.
-
Set dash to be the union of dash and the starting cap shape for the subpath at position start.
-
Set dash to be the union of dash and the ending cap shape for the subpath at position end.
-
Let index and last be the indexes of the path segments in the subpath at distance start and end along the subpath.
Note: It does not matter whether any zero length segments are included when choosing index and last.
-
While index < last:
-
Set dash to be the union of dash and the line join shape for the subpath at segment index index.
-
Set index to index + 1.
-
-
Set shape to be the union of shape and dash.
-
-
-
Return shape.
4.6.3. Dash Positions
This section doesn’t handle stroke-dash-corner and stroke-dash-justify yet.
path
or basic shape
is a sequence of pairs of values,
which represent the starting and ending distance along the subpath
for each of the dashes that form the subpath’s stroke.
It is determined as follows:
-
Let pathlength be the length of the subpath.
-
Let dashes be the list of values of stroke-dasharray on the element, repeated if necessary so that it has an even number of elements; if the property has the value none, then the list has a single value 0.
-
Let count be the number of values in dashes.
-
Let sum be the sum of the values in dashes.
-
If sum = 0, then return a sequence with the single pair (0, pathlength).
-
Let positions be an empty sequence.
-
Let offset be the value of the stroke-dashoffset property on the element.
-
If offset is negative, then set offset to
sum − abs(offset)
. -
Set offset to
offset mod sum
. -
Let index be the smallest integer such that
sum(dashesi, 0 ≤ i ≤ index) ≥ offset
. -
Let dashlength be
min(sum(dashesi, 0 ≤ i ≤ index) − offset, pathlength)
. -
If
index mod 2 = 0
, then append to positions the pair (0, dashlength). -
Let position be dashlength.
-
While position < pathlength:
-
Set index to
(index + 1) mod count
. -
Let dashlength be
min(dashesindex, pathlength − position)
. -
If index mod 2 = 0, then append to positions the pair (position, position + dashlength).
-
Set position to
position + dashlength
.
-
-
Return positions.
4.6.4. Cap Shapes
-
If stroke-linecap is butt, then return an empty shape.
-
Otherwise, if stroke-linecap is round, then:
-
If this is a starting cap, then return a semicircle of radius stroke-width positioned such that:
-
Its straight edge is parallel to the line perpendicular to the subpath at distance position along it.
-
The midpoint of its straight edge is at the point that is along the subpath at distance position.
-
The direction from the midpoint of its arc to the midpoint of its straight edge is the same as the direction of the subpath at distance position along it.
-
-
Otherwise, this is an ending cap. Return a semicircle of radius stroke-width positioned such that:
-
Its straight edge is parallel to the line perpendicular to the subpath at distance position along it.
-
The midpoint of its straight edge is at the point that is along the subpath at distance position.
-
The direction from the midpoint of its straight edge to the midpoint of its arc is the same as the direction of the subpath at distance position along it.
-
-
-
Otherwise, stroke-linecap is square:
-
If this is a starting cap, then return a rectangle with side lengths stroke-width and stroke-width / 2 positioned such that:
-
Its longer edges, A and B, are parallel to the line perpendicular to the subpath at distance position along it.
-
The midpoint of A is at start.
-
The direction from the midpoint of B to the midpoint of A is the same as the direction of the subpath at distance position along it.
-
-
Otherwise, this is an ending cap. Return a rectangle with side lengths stroke-width and stroke-width / 2 positioned such that:
-
Its longer edges, A and B, are parallel to the line perpendicular to the subpath at distance position along it.
-
The midpoint of A is at end.
-
The direction from the midpoint of A to the midpoint of B is the same as the direction of the subpath at distance position along it.
-
-
On the top row, the green lines indicate the perpendicular to the tangent at the path endpoints and the pink areas are the caps. The bottom row shows the stroke without the perpendicular and cap highlighting.
4.6.5. Line Join Shape
-
Let P be the point at the end of the segment.
-
Let A be the line parallel to the tangent at the end of the segment.
-
Let B be the line parallel to the tangent at the start of the following segment.
-
If A and B are the same line, then return an empty shape.
-
Let |Aleft| and |Aright| be lines parallel to A at a distance of stroke-width / 2 to the left and to the right of A relative to the subpath direction, respectively.
-
Let |Bleft| and |Bright| be lines parallel to B at a distance of stroke-width / 2 to the left and to the right of B relative to the subpath direction, respectively.
-
Let |P1|, |P2| and |P3| be points determined as follows:
-
If the smaller angle between A and B is on the right of these lines, considering the direction of the subpath, then |P1| and |P2| are the points on |Aleft| and |Bleft| closest to P, and |P3| is the intersection of |Aleft| and |Bleft|.
-
Otherwise, |P1| and |P2| are the points on |Aright| and |Bright| closest to P, and |P3| is the intersection of |Aright| and |Bright|.
-
-
Let bevel be the triangle formed from the three points P, |P1| and |P2|.
-
If stroke-linejoin is round, then return the union of bevel and a circular sector of radius stroke-width, centered on P, and which has |P1| and |P2| as the two endpoints of the arc.
-
If stroke-linejoin is arcs, then find the circles that are tangent to the stroke edges at |P1| and |P2| with the same curvature as the edges at those points (see below). If both curvatures are zero, fall through to miter-clip.
Extend the stroke edges using these circles (or a line, in the case of zero curvature):
-
If the two circles (or circle and line) do not intersect, fall through to miter-clip.
-
If the two circles (or circle and line) intersect, the line join region is defined by the lines that connect P with |P1| and |P2| and the arcs defined by the circles (or arc and line) between the closest intersection point to P, and |P1| and |P2|.
Next, calculate the miter limit as defined in § 4.2.5 Stroke Corner Limits: the stroke-miterlimit property. Clip any part of the line join region that extends past the miter limit. Return the resulting region.
Note: Note that the curvatures are calculated in user-space before any transforms are applied.
-
-
If stroke-linejoin is miter or miter-clip then the line join region is the union of bevel and the triangle formed from the three points |P1|, |P2| and |P3|.
-
Let θ be the angle between A and B. If
1 / sin(θ / 2) ≤ stroke-miterlimit
, then return the line join region. -
If stroke-linejoin is miter-clip, then clip any part of the line join region that extends past the miter limit and return this region.
-
Return bevel.
4.6.6. Arcs Linejoin
- For a line:
- The curvature is infinite. Extend the outer stroke edge by a line.
- For an elliptical arc:
-
where:
The parameter θ at the beginning or end of an arc segment can be found by using the formulas in the Elliptical arc implementation notes. (Note, some renderers convert elliptical arcs to cubic Béziers prior to rendering so the equations here may not be needed.)
- For a quadratic Bézier:
-
Where κ(0) and κ(1) are the signed curvatures at the start and end of the path segment respectively, and the P’s are the three points that define the quadratic Bézier.
- For a cubic Bézier:
-
Where κ(0) and κ(1) are the signed curvatures at the start and end of the path segment respectively, and the P’s are the four points that define the cubic Bézier. Note, if P0 and P1, or P2 and P3, are degenerate, the curvature will be infinite and a line should be used in constructing the join.
4.7. Perfect-World Syntax
The stroke-* properties and values from SVG inherit SVG’s somewhat incoherent naming schemes. We’d name them differently if they were being designed today. We should see how much we can move toward this, perhaps with alias/shorthands.
Old Syntax | New Syntax |
---|---|
stroke | stroke-paint |
stroke shorthand | |
stroke-alignment | stroke-align: inset | outset | center |
stroke-opacity | no change |
stroke-width | no change |
stroke-linecap | stroke-cap: none | round | square |
stroke-linejoin | stroke-corner-shape: bevel | round | [ arc | miter ] [ bevel | round ]? |
stroke-miterlimit | stroke-corner-limit |
stroke-dasharray | stroke-dash-array |
stroke-dashoffset | stroke-dash-offset |
stroke-dashcorner | stroke-dash-corner |
stroke-dashadjust | stroke-dash-justify: none | [ stretch | compress ] || [ dashes | gaps ] |
stroke-dash shorthand |
5. Text Decoration Fills and Strokes
For handling fills and strokes on text decorations, two sets of fill and stroke properties are defined: one prefixed with text-decoration-* for line decorations and one prefixed with text-emphasis-* for emphasis marks. These properties are exactly analogous to the fill and stroke properties above, except that:
-
(like text-decoration itself) the text-decoration-* variants do not inherit.
-
the initial value of each such property is a match-text keyword which takes the value from the element’s corresponding fill-*/stroke-* property.
These should definitely be at-risk, possibly deferred to the next level.
6. Privacy and Security Considerations
This specification introduces no new privacy or security considerations.