Notes on Stoking Lines
Or the Arcane Art of Stoking
Last updated 1 October 2015
How is a line stroked? That seems like such a basic question that it surely must be answered in the SVG specification. But it's not. One can look to PostScript for a hint as that was one of the inspirations for SVG. The PostScript reference has little to say about exactly how to stroke a line. What it does say is regurgitated in the PDF 1.7 reference (page 231), adding only a comment on stroking zero length line segments. Here is the relevant text extract from the PDF spec:
The S operator paints a line along the current path. The stroked line follows each straight or curved segment in the path, centered on the segment with sides parallel to it. Each of the path’s sub-paths is treated separately. The results of the S operator depend on the current settings of various parameters in the graphics state (see Section 4.3, “Graphics State,” for further information on these parameters):
- The width of the stroked line is determined by the current line width parameter (“Line Width” on page 215).
- The color or pattern of the line is determined by the current color and color space for stroking operations.
- The line can be painted either solid or with a dash pattern, as specified by the current line dash pattern (“Line Dash Pattern” on page 217).
- If a sub-path is open, the unconnected ends are treated according to the current line cap style, which may be butt, rounded, or square (“Line Cap Style” on page 216).
- Wherever two consecutive segments are connected, the joint between them is treated according to the current line join style, which may be mitered, rounded, or beveled (“Line Join Style” on page 216). Mitered joins are also subject to the current miter limit (“Miter Limit” on page 217). Note: Points at which unconnected segments happen to meet or intersect receive no special treatment. In particular, using an explicit l operator to give the appearance of closing a sub-path, rather than using h, may result in a messy corner, because line caps are applied instead of a line join.
- The stroke adjustment parameter (PDF 1.2) specifies that coordinates and line widths be adjusted automatically to produce strokes of uniform thickness despite rasterization effects (Section 6.5.4, “Automatic Stroke Adjustment”). If a sub-path is degenerate (consists of a single-point closed path or of two or more points at the same coordinates), the S operator paints it only if round line caps have been specified, producing a filled circle centered at the single point. If butt or projecting square line caps have been specified, S produces no output, because the orientation of the caps would be indeterminate. (This rule applies only to zero-length sub-paths of the path being stroked, and not to zero-length dashes in a dash pattern. In the latter case, the line caps are always painted, since their orientation is determined by the direction of the underlying path.) A single point open sub-path (specified by a trailing m operator) produces no output.
Hmm, that doesn't help too much.
Here are some possible ways of stroking:
-
Take a line segment of length 'stroke-linewidth', place it at
the start of a path so it is centered on and orthogonal to the
path and then
stroke
it along the path till the end of the path. Any point that the line crosses is filled as part of the stroke. This appears to be what the PDF reference says to do. -
Take a circle of radius 'stroke-linewidth'/2.0, center it on
the start of the path, and then
stroke
it along the path until you reach the end. At the start and end of the path, remove the hemisphere that sticks out past the path (if the 'line-endcap' type is 'butt'). This should be equivalent to the previous method. - Calculate two paths, one one each side of the path with an offset of 'stroke-linewidth'/2.0 from the path. Connect the two start points and the two end points of these paths and the fill the enclosed region. This appears to be what Cairo and Adobe Reader do. It is not equivalent to the previous methods as it leaves holes in the path if the path curvature is small near the ends.
Normal stroking
The gray shows the result of stroking the red path with a 'stroke-width' of 40px. As this is an SVG file, results will differ between browsers.

Firefox: Stroking seems to be done according to option "1".

Chrome: Stroking seems to be done according to option "3".

Opera (Presto): Stroking seems to be done according to option "3".

Inkscape: Stroking seems to be done according to option "3". Inkscape relies on Cairo for path stroking.

Batik: Stroking seems to be done according to option "3".

Acroread: Stroking seems to be done according to option "1".

Evince: Stroking seems to be done according to option "3".
Here the offset paths are explicitly shown.
The blue paths show the offset paths as calculated.
Stroking with a dash pattern
The gray shows the result of stroking the red path with a 'stroke-width' of 40px and a 'stroke-dasharray' of '3 1'. As this is an SVG file, results will differ between browsers.
Chrome, Opera (Presto), and Squiggle all render the dashed stroke the same. Firefox and Inkscape render it the same.

Chrome.

Firefox.
Other considerations
It is useful to look at other paths. For example, there seems to be consistency between renderers when the stroke self-overlaps in the middle of the path.
The gray shows the result of stroking the red path with a 'stroke-width' of 40px. Top to bottom: Two straight lines; two path segments smoothly connected; one Bezier curve. As this is an SVG file, results may differ between browsers; however, most (all?) browsers agree on the rendering.
Stroke alignment presents another set of problems.
Stroking with 'stroke-alignment' value 'right'. The original paths are show in red. Top: The blue paths show the "raw" offset path. Bottom: The desired result.