To a large extent, a detailed knowledge of the internal form is unnecessary unless you plan to modify the library.

The internal form for the path definition is based on the SVG 1.2 concept of normalization:

  • A normalized path has only four elements, moveto, line, curve and closepath
  • A moveto command establishes a new current point and starts a new subpath
  • A Z or z command closes a path by adding a line command back to the initial current point always
  • A moveto command after a Z or z establishes a new subpath
  • A command other than moveto following a Z or z command starts a new subpath with the same current point as the previous one.

This leads to an internal form consisting of:

  • A small number of internal line drawing commands: L, C and A, note all absolute
  • Restrict the positioning of M and Z to make path reversal easy
    • Insert a moveto command after a Z command that takes it to the current point of the previous curve
    • Insert a lineto command always when a Z or z command appears
  • Have each command as a single element with attributes to reduce the number of characters output
  • Try using XSLT 2.0 transformation functions where appropriate

Most operations on SVG paths are straightforward. The one exception is reversing a path. The original SVG path description was poorly defined for this operation. It was not always clear when a new subpath started, nor was it clear that reversing a path twice would have the same subpath structure as the original path. The aim in the path_ology implementation is therefore to have a subpath structure that makes reversing a path just the operation of reversing the order of the subpaths and reversing the drawing commands of each subpath.

Animating a path is still poorly described in SVG 1.2. To make life easier the internal form should require:

  • To animate between two path values, the subpath structure must be the same
  • To animate between paths, the normalised path values must be identical

The proposed internal form is therefore:

  • Each path consists of a sequence of subpaths
  • Each subpath starts with a moveto command, is followed by any combination of line, arc and curve commands
  • Is completed by either a close command or an open command
  • If the last command is a closed command, the final current position must be the same as the first current position

Reversing a subpath consists of:

  1. The last command defines the type of curve (open or close)
  2. Output a moveto to the final position
  3. Reverse each command starting from the last drawing command
  4. Change the initial moveto command to the last command (open or close)

2. A Simple Example

Suppose we have the following path:

<path d="
M xa,ya C xb,yb xc,yc xd,yd Z M xh,yh L xi,yi M xj,yj L xk,yk
"/>

We need an internal representation that allows us to change relative to absolute and vice versa and to reverse the path quickly and easily. This requires the individual path elements to be self-sufficient. To change to relative produces:

<path d="
M xa,ya 
c 
xb-xa,yb-ya 
xc-xa,yc-ya 
xd-xa,yd-ya 
     Lxa-xd,ya-yd  (inserted possibly)
Z 
m xh-xa,yh-ya or Mxh,yh (the second alternative is necessary as Chrome 
                         currently has a bug withrespect to Zm)
l xi-xh,yi-yh 
m xj-xi,yj-yi or M xj,yj 
l xk-xj,yk-yj
"/>

Reversing the path produces:

<path d="
Mxk,yk
Lxj,yj
Mxi,yi
Lxh,yh
Mxa,ya
Lxd,yd
Cxc,yc xb,yb xa,ya
Z
"/>

Internal forms for the line drawing commands are reasonably obvious:

<c t="C" o="C" cx="xa" cy="ya" x1="xb" y1="yb" x2="xc" y2="yc" x3="xd" y3="yd"/>
<c t="L" o="L" cx="xi" cy="xj" x3="yi" y3="yj"/>

Just adding the initial position makes both reversing and changing to relative straightforward. The attribute t defines the type and o defines the original type. All abbreviations are changed to one or other of these on input (l,v,h,V,H all become L) (S,Q,T,c,s,q,t all become C).

The SVG document states that each path consists of a set of subpaths which are either open or closed. The Z command effectively completes one subpath and starts a new one. The meaning of the two being different in terms of linecap etc. This suggests that an internal form should consist of a set of subpaths (M to Z) or (M to C/L). As the Z is effectively adding an additional L command, the two would be closer in form if M to Z added the closing line and M to C/L added an end curve command. Also the Z should add an M command after it if the next command is a line drawing command. Curves would either be M...LZ or M...E. On reversing, Z and E effectively become M and M becomes either Z or E. That would imply that either the internal form should differentiate between the two type of M or the subpaths should be named differently. The first approach has been chosen as it keeps the individual elements with all the information required. If an M is turned to relative it must retain the previous origin so each M element must contain two coordinates, the previous current position and the new one. Similarly, as the E becomes a C, it must retain the next current position. The same is true for Z. The forms for the four commands should be:

<c t="M" o="M" cx="xbf" cy="ybf" x3="xa" y3="ya"/>
<c t="N" o="M" cx="xbf" cy="ybf" x3="xa" y3="ya"/>
<c t="Z" o="Z" cx="xk" cy="yk" x1="xi" y1="yi"/>
<c t="E" o=""  cx="xk" cy="yk" x1="xi" y1="yi"/>

Suppose we have the following SVG document:

<svg width="1000" height="1000">
<style type="text/css">
path {stroke:blue;stroke-width:2;fill:none}
</style>
<path style="stroke:blue" id="example" d="M100,250
L125,275 
H150V250
M150,250      
l25,25h25v-25C215,275 235,225 250,250S285,225 300,250Q325,275 350,250
T400,250A20,20,1,0,0,450,250a30,30,1,0,0,50,0
c15,25 35-25 50,0s35-25 50,0s35,25 50,0q25,25 50,0t50,0t50,0
v100h-650z
l100,-100h300z
m500,-100v40h50
m25,0 
m25,0 v50h40z
h50
">
<set attributeName="id" to="'example'" begin="1s" />
</path>

</svg>

The internal form for the path command would be:

<path style="stroke:blue" id="example">
<s>
<c t="N" o="M" cx="0" cy="0" x3="100" y3="250"/>
<c t="L" o="L" cx="100" cy="250" x3="125" y3="275"/>
<c t="L" o="H" cx="125" cy="275" x3="150" y3="275"/>
<c t="L" o="V" cx="150" cy="275" x3="150" y3="250"/>
<c t="E" o="" cx="150" cy="250" x1="150" y1="250"/>
</s>
<s>
<c t="M" o="M" cx="150" cy="250" x3="150" y3="250"/>
<c t="L" o="l" cx="150" cy="250" x3="175" y3="275"/>
<c t="L" o="h" cx="175" cy="275" x3="200" y3="275"/>
<c t="L" o="v" cx="200" cy="275" x3="200" y3="250"/>
<c t="C" o="C" cx="200" cy="250" x1="215" y1="275" x2="235" y2="225" x3="250" y3="250"/>
<c t="C" o="S" cx="250" cy="250" x1="265" y1="275" x2="285" y2="225" x3="300" y3="250"/>
<c t="C" o="Q" cx="300" cy="250" x1="316.667" y1="266.667" x2="333.333" y2="266.667" x3="350" y3="250"/>
<c t="C" o="T" cx="350" cy="250" x1="366.667" y1="233.333" x2="383.333" y2="233.333" x3="400" y3="250"/>
<c t="A" o="A" cx="400" cy="250" a="20" b="20" d="1" e="0" f="0" x3="450" y3="250"/>
<c t="A" o="a" cx="450" cy="250" a="30" b="30" d="1" e="0" f="0" x3="500" y3="250"/>
<c t="C" o="c" cx="500" cy="250" x1="515" y1="275" x2="535" y2="225" x3="550" y3="250"/>
<c t="C" o="s" cx="550" cy="250" x1="565" y1="275" x2="585" y2="225" x3="600" y3="250"/>
<c t="C" o="s" cx="600" cy="250" x1="615" y1="275" x2="635" y2="275" x3="650" y3="250"/>
<c t="C" o="q" cx="650" cy="250" x1="666.667" y1="266.667" x2="683.333" y2="266.667" x3="700" y3="250"/>
<c t="C" o="t" cx="700" cy="250" x1="716.667" y1="233.333" x2="733.333" y2="233.333" x3="750" y3="250"/>
<c t="C" o="t" cx="750" cy="250" x1="766.667" y1="266.667" x2="783.333" y2="266.667" x3="800" y3="250"/>
<c t="L" o="v" cx="800" cy="250" x3="800" y3="350"/>
<c t="L" o="h" cx="800" cy="350" x3="150" y3="350"/>
<c t="L" o="" cx="150" cy="350" x3="150" y3="250"/>
<c t="Z" o="Z" cx="150" cy="250" x1="150" y1="250"/>
</s>
<s>
<c t="M" o="" cx="150" cy="250" x3="150" y3="250"/>
<c t="L" o="l" cx="150" cy="250" x3="250" y3="150"/>
<c t="L" o="h" cx="250" cy="150" x3="550" y3="150"/>
<c t="L" o="" cx="550" cy="150" x3="150" y3="250"/>
<c t="Z" o="Z" cx="150" cy="250" x1="650" y1="150" />
</s>
<s>
<c t="N" o="m" cx="150" cy="250" x3="650" y3="150"/>
<c t="L" o="v" cx="650" cy="150" x3="650" y3="190"/>
<c t="L" o="h" cx="650" cy="190" x3="700" y3="190"/>
<c t="E" o="" cx="700" cy="190" x1="725" y1="190" />
</s>
<s>
<c t="N" o="m" cx="700" cy="190" x3="725" y3="190"/>
<c t="E" o="" cx="725" cy="190" x1="750" y1="190"/>
</s>
<s>
<c t="M" o="m" cx="725" cy="190" x3="750" y3="190"/>
<c t="L" o="v" cx="750" cy="190" x3="750" y3="240"/>
<c t="L" o="h" cx="750" cy="240" x3="790" y3="240"/>
<c t="L" o="" cx="790" cy="240" x3="750" y3="190"/>
<c t="Z" o="Z" cx="750" cy="190" x1="750" y1="190"/>
</s>
<s>
<c t="N" o="" cx="750" cy="190" x3="750" y3="190"/>
<c t="L" o="h" cx="750" cy="190" x3="800" y3="190"/>
<c t="E" o="" cx="800" cy="190" x1="0" y1="0"/>
</s>
</path>