[svg-strokes] … message topic …
” (archive)Copyright © 2015 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 http://www.w3.org/TR/.
This document is the 09 April 2015 First Public Working Draft of SVG Strokes. The purpose of this specification is to define a number of improved SVG stroking features.
Comments on this First Public Working Draft are welcome.
Comments can be sent to [email protected],
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
[email protected] 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, http://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 First Public Working 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 http://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: | 1 |
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.
stroke-miterlimit
’
is exceeded, the line join falls back to bevel
(see below).stroke-miterlimit
’ is exceeded, the miter is clipped at a
miter length equal to the ‘stroke-miterlimit
’ value multiplied
by the stroke width (see below).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.
stroke-width
’ value. The value of
‘stroke-miterlimit
’ must be a <number> greater
than or equal to 1. Any other value is an error (see
Error processing).
For the miter or the miter-clip values, given the angle θ between the segments in user space, 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:
stroke-dashcorner
’ is none,
then the dash pattern will be adjusted to fit within the length of
the subpath a whole number of times.stroke-dashcorner
’ has any other value, including a zero
length, then the dash pattern will be adjusted differently for each
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 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?
stroke-width
’ of
the point on the subpath at that position.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:
stroke-dasharray
’
on the element, converted to user units, 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.stroke-dashoffset
’
property on the element.The starting and ending cap shapes at a given position along a subpath are determined as follows:
stroke-linecap
’ is butt, then return an empty shape.stroke-linecap
’ is round, then:
stroke-width
’ positioned such that:
stroke-width
’ positioned such that:
stroke-linecap
’ is square:
stroke-width
’ and ‘stroke-width
’ / 2 positioned such that:
stroke-width
’ and ‘stroke-width
’ / 2 positioned such that:
The line join shape for a given segment of a subpath is determined as follows:
stroke-width
’ / 2 to the
left and to the right of A relative to the subpath direction, respectively.stroke-width
’ / 2 to the
left and to the right of B, relative to the subpath direction, respectively.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.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
the ‘stroke-miterlimit
’ section. Clip any part of the line
join region that extends past the miter limit. Return the
resulting region.
Note that the curvatures are calculated in user-space before any
transforms are applied.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.
stroke-miterlimit
’, then return
the line join region.
stroke-linejoin
’ is miter-clip,
then clip any part of the line join region that extends past the
miter limit and return this region.
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 ( {r_y \over r_x} \tan \theta )$$
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.
stroke-alignment
’, ‘stroke-dashadjust
’ and
‘stroke-dashcorner
’ properties.