[svg-strokes] … message topic …
” (archive)Copyright © 2023 W3C® (MIT, ERCIM, Keio, Beihang). W3C liability, trademark and document use rules apply.
This specification defines properties for controlling the appearance of strokes painted for SVG shapes.
This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at https://www.w3.org/TR/.
This document is the 08 March 2023 Editor’s Draft of SVG Strokes. The purpose of this specification is to define a number of improved SVG stroking features.
This specification is an early draft that largely contains features that were dropped during the development of SVG 2. In the future, this specification will supercede the SVG 2 Stroke definition, however at this time the SVG 2 Stroke definition must be considered the normative definition.
Comments on this Editor’s Draft are welcome.
Comments can be sent to www-svg@w3.org,
the public email list for issues related to vector graphics on the Web. This list is
archived and
senders must agree to have their message publicly archived from their
first posting. To subscribe send an email to
www-svg-request@w3.org with
the word subscribe
in the subject line.
This document has been produced by the W3C SVG Working Group as part of the Graphics Activity within the W3C Interaction Domain. The goals of the W3C SVG Working Group are discussed in the W3C SVG Charter. The W3C SVG Working Group maintains a public Web page, https://www.w3.org/Graphics/SVG/, that contains further background information. The authors of this document are the SVG Working Group participants.
This document was produced by a group operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.
Publication as a Editor’s Draft does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.
A list of current W3C Recommendations and other technical documents can be found at https://www.w3.org/TR/. W3C publications may be updated, replaced, or obsoleted by other documents at any time.
This document is governed by the 1 August 2014 W3C Process Document.
This section is non-normative.
SVG graphical elements that define a shape – ‘path’ elements, basic shapes, and text content elements – are rendered by being filled, which is painting the interior of the object, and stroked, which is painting along the outline of the object.
This specification describes how SVG graphical elements are stroked, by defining a number of properties that control the appearance of a stroke and by specifying the shape of an element's stroke.
SVG 2 supports multiple strokes, which we will need updated wording to handle in this specification.
This module replaces and extends the definition of stroking properties in SVG 2 (the "Stroke properties" section in the "Painting: Filling, Stroking and Marker Symbols" chapter). [SVG2]
In this section, we define a number of properties that allow the author to control different aspects of a stroke, including its paint, thickness, position, use of dashing, and joining and capping of path segments.
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.
Name: | stroke |
---|---|
Value: | <paint> |
Initial: | none |
Applies to: | shapes and text content elements |
Inherited: | yes |
Percentages: | N/A |
Media: | visual |
Computed value: | as specified, but with <color> values computed and <url> values made absolute |
Animatable: | yes |
The stroke property paints along the outline of the given graphical element.
A subpath (see Paths) 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'.
The above paragraph should be redundant with the stroke shape computation requirements below. In this section, we should phrase the requirements descriptively rather than normatively.
SVG 2 Requirement: | Include a way to specify stroke position. |
---|---|
Resolution: | SVG 2 shall include a way to specify stroke position. |
Purpose: | To allow a stroke to be inside or outside the path. |
Owner: | Cameron (ACTION-3162) |
Note: | See proposal page. |
Name: | stroke-alignment |
---|---|
Value: | center | inner | outer |
Initial: | center |
Applies to: | shapes and text content elements |
Inherited: | yes |
Percentages: | N/A |
Media: | visual |
Computed value: | as specified |
Animatable: | yes |
This property allows the author to align a stroke along the outline of the current object.
This value indicates that the stroke for each subpath is positioned along the outline of the current stroke. The extends of the stroke increase to both sides of the outline accordingly dependent on the stroke-width.
This value indicates that the stroke area is defined by the outline of each subpath of the current object and the computed value of the stroke-width property as offset orthogonal from the outline into the fill area of each subpath.
The stroke-linejoin property must be ignored.
This value indicates that the stroke area is defined by the outline of each subpath of the current object and the computed value of the stroke-width property as offset orthogonal from the outline away from the fill area of each subpath.
The fill area of the current object is defined by the fill-rule property.
Does that require updates on the stroke computation?
SVG 2 Requirement: | Allow more author control over positions of dashes. |
---|---|
Resolution: | SVG 2 shall allow more author control over positions of dashes. |
Purpose: | To allow things like aligning dashes at rectangle corners or along paths, needed for mapping. |
Owner: | Cameron (ACTION-3163) |
Note: | See proposal page. |
Name: | stroke-opacity |
---|---|
Value: | <number> |
Initial: | 1 |
Applies to: | shapes and text content elements |
Inherited: | yes |
Percentages: | N/A |
Media: | visual |
Computed value: | as specified, but clamped to the range [0, 1] |
Animatable: | yes |
The stroke-opacity property specifies the opacity of the painting operation used to stroke the current object. (See Painting shapes and text.) As with fill-opacity, a value of 0 means fully transparent, and a value of 1 means fully opaque.
See also the opacity property, which specifies group opacity.
Name: | stroke-width |
---|---|
Value: | <percentage> | <length> |
Initial: | 1px |
Applies to: | shapes and text content elements |
Inherited: | yes |
Percentages: | refer to the size of the current viewport (see Units) |
Media: | visual |
Computed value: | absolute length or percentage |
Animatable: | yes |
This property specifies the width of the stroke on the current object. A zero value causes no stroke to be painted. A negative value is invalid.
Name: | stroke-linecap |
---|---|
Value: | butt | round | square |
Initial: | butt |
Applies to: | shapes and text content elements |
Inherited: | yes |
Percentages: | N/A |
Media: | visual |
Computed value: | as specified |
Animatable: | yes |
stroke-linecap specifies the shape to be used at the end of open subpaths when they are stroked. The possible values are:
This value indicates that at each end of each subpath, the shape representing the stroke will be extended by a half circle with a radius equal to the stroke width. If a subpath has zero length, then the resulting effect is that the stroke for that subpath consists solely of a full circle centered at the subpath's point.
This value indicates that at the end of each subpath, the shape representing the stroke will be extended by a rectangle with the same width as the stroke width and whose length is half of the stroke width. If a subpath has zero length, then the resulting effect is that the stroke for that subpath consists solely of 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 ‘path’ element implementation notes for details on how to determine the tangent at a zero-length subpath.
See the definition of the cap shape below for a more precise description of the shape a line cap will have.
Name: | stroke-linejoin |
---|---|
Value: | miter | miter-clip | round | bevel | arcs |
Initial: | miter |
Applies to: | shapes and text content elements |
Inherited: | yes |
Percentages: | N/A |
Media: | visual |
Computed value: | as specified |
Animatable: | yes |
stroke-linejoin specifies the shape to be used at the corners of paths or basic shapes when they are stroked. For further details see the path implementation notes.
The miter-clip and arcs values are new in SVG 2. The miter-clip value offers a more consistent presentation for a path with multiple joins as well as better behavior when a path is animated. The arcs value provides a better looking join when the path segments at the join are curved.
Adding 'arcs' line join was resolved at the Rigi Kaltbad group meeting.
Adding 'miter-clip' line join was resolved at the Sydney (2015) group meeting.
Name: | stroke-miterlimit |
---|---|
Value: | <number> |
Initial: | 4 |
Applies to: | shapes and text content elements |
Inherited: | yes |
Percentages: | N/A |
Media: | visual |
Computed value: | as specified |
Animatable: | yes |
When two line segments meet at a sharp angle and a value of miter, miter-clip, or arcs has been specified for stroke-linejoin, it is possible for the join to extend far beyond the thickness of the line stroking the path. The stroke-miterlimit imposes a limit on the extent of the line join.
For the miter or the miter-clip values, given the angle θ between the segments in local coordinate system, the miter length is calculated by:
miter length = stroke-width / sin(theta / 2)
If the miter length divided by the stroke width exceeds the stroke-miterlimit then for the value:
For the arcs value, the miter length 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 line join is clipped, if necessary, by a line perpendicular to this arc at a miter length equal to the value of the stroke-miterlimit value multiplied by the stroke width.
The effect of 'stroke-miterlimit' on an 'arcs' line join was resolved at Sydney (2015) group meeting.
See the definition of the line join shape below for a more precise description of the shape a line join will have.
Name: | stroke-dasharray |
---|---|
Value: | none | <dasharray> |
Initial: | none |
Applies to: | shapes and text content elements |
Inherited: | yes |
Percentages: | refer to the size of the current viewport (see Units) |
Media: | visual |
Computed value: | absolute lengths or percentages for <dasharray>, or keyword specified |
Animatable: | yes (non-additive) |
where:
<dasharray> = [ <length> | <percentage> | <number> ]#*
The stroke-dasharray property controls the pattern of dashes and gaps used to form the shape of a path's stroke.
Specifies a dashing pattern to use. A <dasharray> is a list of comma and/or white space separated lengths and percentages. Each value specifies a length along the path for which the stroke is to be painted (a dash) and not painted (a gap). Every second value in the list beginning with the first one specifies the length of a dash, and every other value specifies the length of a gap between the dashes. If the list has an odd number of values, then it is repeated to yield an even number of values. (Thus, the rendering behavior of stroke-dasharray: 5,3,2 is equivalent to stroke-dasharray: 5,3,2,5,3,2.)
The resulting even-length dashing pattern is repeated along each subpath. The dashing pattern is reset and begins again at the start of each subpath.
If any value in the list is negative, the <dasharray> value is invalid. If all of the values in the list are zero, then the stroke is rendered as if a value of none were specified.
Name: | stroke-dashoffset |
---|---|
Value: | <length> | <percentage> |
Initial: | 0 |
Applies to: | shapes and text content elements |
Inherited: | yes |
Percentages: | refer to the size of the current viewport (see Units) |
Media: | visual |
Computed value: | absolute length or percentage |
Animatable: | yes |
The stroke-dashoffset property specifies the distance into the repeated dash pattern to start the stroke dashing at the beginning of the path. If the value is negative, then the effect is the same as dash offset d:
d = s - (abs(stroke-dashoffset) mod s)
where s is the sum of the dash array values.
See the definition of dash positions below for a more precise description of positions along a path that dashes will be placed.
Name: | stroke-dashcorner |
---|---|
Value: | none | <length> |
Initial: | none |
Applies to: | shapes and text content elements |
Inherited: | yes |
Percentages: | refer to the size of the current viewport (see Units) |
Media: | visual |
Computed value: | absolute length or keyword specified |
Animatable: | yes |
This is a new feature, added to allow better control of dashing at the vertices of a shape, such as at the four corners of a rectangle.
The stroke-dashcorner 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.
This will not help with placing a corner dash on a ‘rect’ with rounded corners, as they will also be placed at the points between the arcs forming the rounded corners and the straight line segments.
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 stroke-dashcorner property also controls how the dash pattern given by stroke-dasharray is repeated. When none is used, the dash pattern is repeated over the entire length of the subpath. When any other value is used, the dash pattern is repeated separately on each path segment in the space between the segment's two adjacent corner dashes.
Need to define what happens when corner dashes would overlap.
Name: | stroke-dashadjust |
---|---|
Value: | none | [stretch | compress] [dashes | gaps]? |
Initial: | none |
Applies to: | shapes and text content elements |
Inherited: | yes |
Percentages: | N/A |
Media: | visual |
Computed value: | as specified |
Animatable: | yes |
This is a new feature, added to allow for more visually pleasing stroke dash patterns.
The stroke-dashadjust property specifies whether and how a stroke's dash pattern will be adjusted so that it is repeated a whole number of times along an element's subpaths.
Do we need to be able to choose to stretch or compress?
If neither the dashes or gaps keyword is given, both the dashes and the gaps will be adjusted.
The target length that a dash pattern will be adjusted to depends on the value of the stroke-dashcorner property:
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 there is no appropriate factor that can be chosen – for example if stretch is used but the dash pattern is longer than the target length, or if gaps was specified but all of the gaps in the dash pattern are zero – then no adjustment is performed.
Should there be an auto value which means round to the closest number of whole repetitions?
Does the compression or expansion of the dashes and gaps happen after an odd-lengthed dash array is doubled so that it has an even number of entries or before? Probably after.
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 a non-closed path, a dash at the end is probably better. Omit control for this, but just do it automatically based on whether the path is closed?
SVG 2 Requirement: | Specify stroke dashing more precisely. |
---|---|
Resolution: | SVG 2 shall specify stroke dashing more precisely. |
Purpose: | To define dash starting point on basic shapes and path segments. |
Owner: | Cameron (no action) |
Something in this section needs to reference ‘pathLength’ so that dash lengths are in the author's path length space.
This section doesn't handle stroke-dashcorner and stroke-dashadjust yet.
The stroke shape of an element is the shape that is filled by the stroke property. The following algorithm describes what the stroke shape of a ‘path’ or basic shape is, taking into account the stroking properties above:
This should include text elements too, but should we keep stroke dashing on text?
It does not matter whether any zero length segments are included when choosing index and last.
The dash positions for a given subpath of the equivalent path of a ‘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:
The starting and ending cap shapes at a given position along a subpath are determined as follows:
The line join shape for a given segment of a subpath is determined as follows:
The arcs stroke-linejoin requires finding circles that are both tangent to and have the same curvatures as the outer stroke edges at the ends of path segments. To find one of these circles, first calculate the curvature κ of the path segment at its end (see below). Next, find the radius of a circle corresponding to this curvature: r = 1/κ. Increase or decrease the radius by one half of the stroke width to account for the stroke: rc = r ± ½ stroke-width. The center of the circle will be on a line normal to the path end a distance of rc away from the outer stroke edge at the end.
For a line: the curvature is infinite. Extend the outer stroke edge by a line.
For an elliptical arc:
$$\kappa(t) = {{r_x r_y}\over{(r_x^2 \sin^2 t + r_y^2 \cos^2 t)^{3/2}}}$$
where:
$$t = \arctan \left( {r_y \over r_x} \tan \theta \right)$$
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:
$$\kappa(0) = {2\over3}{(P_1-P_0)\times((P_0-P_1)+(P_2-P_1))\over|P_1-P_0|^3}$$
$$\kappa(0) = {2\over3}{(P_1-P_0)\times((P_0-P_1)+(P_2-P_1))\over|P_1-P_0|^3}$$
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:
$$\kappa(0) = {2\over3}{(P_1-P_0)\times((P_0-P_1)+(P_2-P_1))\over|P_1-P_0|^3}$$
$$\kappa(1) = {2\over3}{(P_3-P_2)\times((P_1-P_2)+(P_3-P_2))\over|P_3-P_2|^3}$$
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.