Stroking: It’s not as easy as it looks.
What could be easier that stroking a path? It’s a fundamental concept in all graphics libraries. You construct a path: in PostScript:newpath 100 100 moveto 150 100 lineto 10 setlinewidth strokein SVG:
<path d="M 100,100 150,100" stroke-width="10"/>and voila, you have a horizontal path, 50 pixels long, that is 10 pixels wide. Hmm, if only it were that easy. It turns out that stroking an arbitrary path can be quite complicated. Different graphics libraries can give quite different results.
A Bezier path segment with high curvature at the end. Web browsers differ on the rendering. (SVG)


Rendering of above path: Firefox (left/top), Chrome (right/bottom). (PNG)
A Bezier path segment with high curvature at the end. Stroke constructed by offsetting path. Red: original path, blue: offset paths. (SVG)
- Sweep a line:
- Firefox, Adobe Reader
- Offset paths:
- Chrome, Inkscape (Cairo), Opera (Presto), Evince, Batik, rsvg
Two same size circular paths with different stroke widths. When one-half the stroke width exceeds the circle radius (right circle), web browsers differ in their rendering. (SVG)


Rendering of a circular path when one-half the stroke width is greater than the radius in: Firefox (left/top), Chrome (right/bottom). (PNG)
Offset pathsmethod, an inner path is always created. As the direction of this path is the same regardless of the stroke width, one cannot differentiate between the case where the stroke width is less than one-half the radius and the case where it is not. This can be seen in the animation below:
Stroking the path. The arrows indicate the direction of the offset paths. If the drawing is not animated, view the image by itself. (SVG)
This seems to indicate that theThe 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 subpaths is treated separately…
sweeping the linetechnique is what is expected and indeed, Adobe’s own product, Adobe Reader, appears to do just that.
Stroke Alignment
Designers often want more control over how a stroke is positioned: only on the inside, only on the outside, or some arbitrary ratio of the two. The new SVG ‘stroke-alignment‘ property offers this control. For a closed path, it is relatively easy to figure out how this property should behave:Top: the original path. Middle: left: stroke inside; right: stroke outside. Bottom: left: stroke to left; right: stroke to right.
inside, what is
outside? One can define the terms by looking at what is filled: inside is in the fill, outside is not in the fill. With this definition, a single straight line segment would render nothing for an ‘inside’ stroke and a stroke on both sides for an ‘outside’ stroke. The SVG specification has a slightly different definition for ‘outside’ (see figure). For an open path it may make more sense to talk about left/right rather than inside/outside.
Top to bottom: Default stroke. Fill area (in gray). Inside (according to SVG specification?). Outside (implemented here by masking). Inside (another interpretation). Outside (according to SVG specification?). Stroke on left (round end cap in pink).
Round end caps. Top to bottom: Default stroke. Stroke alignment ‘outside’, end-cap radius doubled. Stroke alignment ‘outside’, end-cap radius same as normal.
Offset Paths
We’ve seen that offsetting a path can be used for constructing strokes. What about offsetting a path for the purpose of creating a new path? This is quite useful in mapping. For example you might want to show multiple bus routes going along a road with different offsets for each route. More stylistically, you could produce theshadowingseen around land masses in older, hand-drawn maps.

An excerpt from a submarine cable map showing the use of offset paths to shade around land masses. Note also the use of inside strokes to define country boundaries.
- Offsets of Bezier segments are not Beziers; in fact they are 10th-order polynomials. In practice, one can do a pretty good job of estimating the offset by breaking up a Bezier path into smaller segments.
- Offset paths can have loops at
cusps
. - Offset paths may require breaking apart
left
andright
offset paths and recombining to form outset and inset paths. It can be difficult to get this right.
simpleexample path with offsets both inside and outside:
Left: insets, right: outsets. Red path is original.
cusploops:
The same original path as in the above figure. Left: the light blue region is created by stroking the original path. As can be seen it matches the corresponding outset (blue) and inset (green) paths. Right: The raw
offset paths used to construct the visible outset and inset paths. In this case, the outset path is constructed from the raw outset path (blue) and the inset path is constructed from the raw inset path (green). Cusp
loops and overlaps have been removed.
Left: The left-offset path (blue) and the right-offset path (green), relative to the path’s direction (clock-wise). Right: The resulting outset path (blue) and inset path (green).
Linked Offsetfunction gets it wrong:
The resulting outset path (blue) and inset path (green) as found by Inkscape’s Linked Offset
function.
outsidejoins are rounded. It would be desirable to be able to specify the type of join to use. This can maintain the
feelof the original path.
Left: Outset path with three different types of joins: ‘bevel’, ’round’, and ‘arcs’. Right: Outset paths with various offsets and with the ‘arcs’ line join. Note: the ‘arcs’ line join fails for the outer most path as the generated arcs do not intersect; this results in falling back to a ‘miter’ line join.
References
- An offset algorithm for polyline curves Xu-Zheng Liu, Jun-Hai Yong, Guo-Qin Zheng, Jia-Guang Sun.

It seems logical that Firefox and Adobe Reader render it right even though specifications say nothing about stroking a path. Imagine a self-intersecting path that has stroke width much less than its radii of curvature. It does not produce something like “evenodd” rule for “fill-rule”, it acts like “nonzero”. Your examples for circle with half stroke-width higher than its radius and high curvature path fall in the same category. It’s stroke effectively produces a shape that is self-intersecting, and there’s no reason for rendering it differently from more trivial self-intersections.
I guess SVG specs should be more specific about it (no pun intended). If they ever allow for something like evenodd/nonero options, if should default to “nonzero” kind when not specified, because it’s what trivial self-intersections produce.
An interesting possibility is to have different fill-rules for stroke and fill, combined with transparencies, paint-order (stroke over fill, fill over stroke) and stroke-alignment.
Now, how do strokes behave with respect to fills? Can stroke-fill intersection have fill-rules like nonzero/evenodd of their own? I think it’s overcomplicating things, but still?
Also, what markers should behave like? I assume they should just be overlaid on top of each other (do they assume transparency?).