SVG can be extremely complex to support very sophisticated requirements, however, these notes will try to cover the minimum necessary understanding needed to just represent simple geometry.

Resources

Editors and Viewers

  • inkscape - The comprehensive editor.

  • inkview - Lightweight viewer. Scaling is weak. Can’t pipe input file.

  • rsvg-view-3 - Viewer with scaling. Lame ass lack of documentation. Seems to support piping input file - but it does not in reality!

  • display - From ImageMagick suite. Works pretty well. Scales ok. Must convert to bitmaps, but sometimes, that’s what is needed.

  • your browser - Don’t forget that the simplest way to deal with SVG is to just open it in a browser. You can use Vim and Firefox to great effect.

Conversion

It may be possible to convert SVG directly to PDF. Some possible techniques are discussed here.

Does a superb job but doesn’t bring in imported bitmaps.

rsvg-convert -f pdf -o t.pdf t.svg

Does a good job and nicely usable from within Python programs but doesn’t preserve fonts well. The documentation says, "…features related to fonts are poorly supported."

cairosvg t.svg -o t.pdf

The output is fine but I couldn’t figure out how to read and write standard input/output.

inkscape t.svg --export-pdf=t.pdf

These conversion utilities often have other output targets like PNG and PS.

To go the other way and pull SVG vectors out of a diagram in EPS or a PDF, it seems that Inkscape is the best bet for non-scaling problems. The challenge there (in 2021) is that they’re suffering the Python2 to 3 change over and some things are broken. I found I needed to add this "python-interpreter" definition to the "extensions" group in my ~/.config/inkscape/preferences.xml.

<group
   id="extensions"
    python-interpreter="/usr/bin/python2.7" />
<group

Hopefully this is fixed soon and we can stop worrying about it.

Inkscape can also be used without the GUI to make conversions from EPS to SVG. Here is an example of that.

for N in simple_eps/*eps; do
    B=$(basename $N) ; echo $B
    inkscape --without-gui --file=$N --export-plain-svg=svg/${B%%eps}svg
done

When I did this conversion from EPS street sign data to plain SVG, the file size was reduced by 97%!

Tiny

There are many specifications of SVG with various fine points and features. The most limited set seems to be called SVG Tiny which was designed for use with mobile devices or anything that can’t commit to vast computational resources. Seems ideal for very simple output.

Scale

The initial coordinate system has the origin at the top/left with the x-axis pointing to the right and the y-axis pointing down. The initial user coordinate system has one user unit equal to the parent (implicit or explicit) user agent’s "pixel".

Elements can be enclosed in a transformation (like PostScript really) using syntax like this:

<rect x="1" y="1" width="150" height="50"
    fill="none" stroke="blue" stroke-width="2"/>
<g transform="translate(50,50)">
    <g fill="none" stroke="red" stroke-width="3">
        <line x1="0" y1="0" x2="50" y2="0" stroke="red"/>
        <line x1="0" y1="0" x2="0" y2="50"/>
    </g>
</g>
Example InitialCoords - SVG's initial coordinate system

Also scale, rotate (looks like degrees, +=CW), skewX, skewY, and matrix are ok.

The scale transform can be handy because the origin (0,0) of SVG is in the upper left with positive Y naturally pointing down while many other mathematical and modelling applications have the origin in the lower left with Y pointing up. The scale factors are in ratios of 1 and the Y defaults to X if missing. The solution to the upside down image to do something like:

<g transform="scale(1,-1)">
   ...Some geometry...
</g>

Elements that establish a new viewport can use the viewBox attribute to specify a region which child content will be constrained to fit. viewBox="minx, miny, width, height)"

Also there is a preserveAspectRatio attribute. This can equal none (do not force uniform scaling), or a bunch of more complicated things.

Inkscape has a nice resource for studying the gory details of what the units in SVG and Inkscape are all about.

Paths

Paths can be constructed much like PostScript. The syntax looks something like:

<path d="M 100 100 L 300 100 L 200 300 z"
        fill="red" stroke="blue" stroke-width="3" />

It is not clear what the d attribute actually stands for (definition?). It also seems that commas are completely optional in these definitions and effectively are converted to spaces.

You can also do some implicit poly line creation with something like this.

<path d="m 100,100 1,0 0,1 -1,0 0,-1" />

Note that the m is lower case so that the coordinates that follow are implicit lineto operations in relative mode. (So this makes a 1x1 square at position 100,100.) If the M is upper case, then the subsequent values are absolute. The starter coordinate in a step like this is always absolute. This is how Inkscape sometimes renders poly lines.

The offical documentation about paths is not unhelpful.

M

(x y)+

moveto absolute

m

(x y)+

moveto relative

L

(x y)+

lineto absolute

l

(x y)+

lineto relative

H

x

horizontal lineto absolute

h

x

horizontal lineto relative

V

y

vertical lineto absolute

v

y

vertical lineto relative

A

(rx ry XAR LAF SF x y)+

elliptical arc, r= ellipse radii, XAR=x-axis rotation (for tilted ellipses),LAF=large arc flag (long=1,short=0), SF=sweep flag (1=CW,0=CCW)

a

(rx ry XAR LAF SF x y)+

elliptical arc, relative

C

(x1 y1 x2 y2 x y)+

curveto absolute, x & y=target, x[12] & y[12] are control points

c

(x1 y1 x2 y2 x y)+

curveto relative

S

(x2 y2 x y)+

smooth continued curveto absolute

s

(x2 y2 x y)+

smooth continued curveto relative

Q

(x1 y1 x y)+

quadratic Bezier curveto absolute

q

(x1 y1 x y)+

quadratic Bezier curveto relative

T

(x y)+

smooth continued quadratic Bezier curveto absolute

t

(x y)+

smooth continued quadratic Bezier curveto relative

z or Z

close path

Paths may be able to be used instead of lines so that a skewed aspect ratio can be plotted (like a data plot) in a viewbox without the strokes looking fat in one direction. To do this, one presumably uses the vector-effect="non-scaling-stroke" attribute and value (documented here).

For more details, see also http://www.w3.org/TR/SVG11/paths.html

Path Example - Demonstrating a quick path test
<svg width="600" height="350" xmlns="http://www.w3.org/2000/svg" >
<path d="M 590 310 L 50 310 L 590 50 L 50 50 z" fill="green" />
<path d="M 556 325 L 133 331 L 266 235 L 385 228 z" fill="blue" />
</svg>

Shape Elements

There are specialized tags for useful shape forms. These basic shapes are basically shorthand for paths of the correct form. The shape elements are:

rect

<rect x="1" y="1" width="1198" height="398"
    fill="none" stroke="blue" stroke-width="2"/>
<rect x="400" y="100" width="400" height="200"
    fill="yellow" stroke="navy" stroke-width="10"  />

circle

<circle cx="600" cy="200" r="100"
    fill="red" stroke="blue" stroke-width="10"  />

ellipse

<ellipse transform="translate(900 200) rotate(-30)"
    rx="250" ry="100" fill="none" stroke="blue" stroke-width="20"  />

line

<line x1="100" y1="300" x2="300" y2="100"
    stroke-width="5"  />

Lines never use fill attributes. The color can be set with the stroke attribute.

polyline

<polyline fill="none" stroke="blue" stroke-width="10" points="50,375
    150,375 150,325 250,325 250,375 350,375 350,250 450,250 450,375
    550,375 550,175 650,175 650,375 750,375 750,100 850,100 850,375
    950,375 950,25 1050,25 1050,375 1150,375" />

polygon

I wondered if it was possible to easily close a polyline like using z with path, but then I noticed that the polygon specification indicates that it is actually nothing more than a polyline with a close. It’s the same format. There is no implied radial symmetry (like a star) or anything like that. It’s just a polyline closed by default.

Examples

Viewbox Example

Each user unit is 10 pixels.

<svg xmlns="http://www.w3.org/2000/svg"
    width="500" height="200" viewBox="0 0 50 20" >
     <rect x="20" y="10" width="10" height="5"
               style="stroke: #000000; fill:none;"/>
     <rect x="0" y="0" width="50" height="20"
               style="stroke: #cecece; fill:none;"/>
</svg>

Exploring Coordinates

<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny"
     width="300px" height="100px">

  <desc>Example InitialCoords - SVG's initial coordinate system</desc>

  <g fill="none" stroke="black" stroke-width="3">
    <line x1="0" y1="1.5" x2="300" y2="1.5"/>
    <line x1="1.5" y1="0" x2="1.5" y2="100"/>
  </g>
  <g fill="red" stroke="none">
    <rect x="0" y="0" width="3" height="3"/>
    <rect x="297" y="0" width="3" height="3"/>
    <rect x="0" y="97" width="3" height="3"/>
  </g>
  <g font-size="14" font-family="Verdana">
    <text x="10" y="20">(0,0)</text>
    <text x="240" y="20">(300,0)</text>
    <text x="10" y="90">(0,100)</text>
  </g>
</svg>
Example InitialCoords - SVG's initial coordinate system (0,0) (300,0) (0,100)

Plotting Over A Graphic

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="320px" height="160px" version="1.1"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink">
    <image xlink:href="https://xed.ch/dev/help/Images/earthmap.jpg" height="125" width="250"/>
    <g transform="translate(125,63)">
    <g transform="scale(.694444,-.694444)"> <!-- full range is Ypx / 180deg -->
    <g transform="translate(-2.5,-2.5)"> <!-- Center square marks -->
    <rect fill="red" width="5px" height="5px" x="1.071" y="50.81" />
    <rect fill="#00ffff" width="5px" height="5px" x="-117.1" y="32.82" />
    </g>
    <circle fill="#00ff00" r="2.5px"  cx="117.1" cy="-32.82" />
    </g> </g>
</svg>

Here is how to make elements of the SVG contain links. I think this pretty well makes image maps obsolete for any image that isn’t a photograph.

<svg width="500" height="500" xmlns="http://www.w3.org/2000/svg">
  <circle cx="150" cy="150" r="120" stroke="black" stroke-width="2" fill="red"/>
  <circle cx="150" cy="150" r="75" stroke="black" stroke-width="2" fill="white"/>
  <circle cx="150" cy="150" r="30" stroke="black" stroke-width="2" fill="blue"/>
  <!-- Link around the text -->
  <a href="https://xed.ch" target="_blank">
    <text x="150" y="150" dominant-baseline="middle"
          text-anchor="middle" fill="black" font-size="30">xed.ch</text>
  </a>
</svg>
xed.ch