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
-
My SVG PSA.
-
This guy has a great set of demo SVG animations.
-
Most coherent and patient explanation of SVG coordinate systems with viewbox complexities.
-
Here’s another good guide to scaling and viewBox issues.
-
Apparently SVG is now supported for favicons. This tip shows how and points out that you can use SVG text to use emojis as quick and dirty stand ins for proper favicons.
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".
Useful stuff on scale and coordinates here.
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>
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.
|
(x y)+ |
moveto absolute |
|
(x y)+ |
moveto relative |
|
(x y)+ |
lineto absolute |
|
(x y)+ |
lineto relative |
|
x |
horizontal lineto absolute |
|
x |
horizontal lineto relative |
|
y |
vertical lineto absolute |
|
y |
vertical lineto relative |
|
(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) |
|
(rx ry XAR LAF SF x y)+ |
elliptical arc, relative |
|
(x1 y1 x2 y2 x y)+ |
curveto absolute, x & y=target, x[12] & y[12] are control points |
|
(x1 y1 x2 y2 x y)+ |
curveto relative |
|
(x2 y2 x y)+ |
smooth continued curveto absolute |
|
(x2 y2 x y)+ |
smooth continued curveto relative |
|
(x1 y1 x y)+ |
quadratic Bezier curveto absolute |
|
(x1 y1 x y)+ |
quadratic Bezier curveto relative |
|
(x y)+ |
smooth continued quadratic Bezier curveto absolute |
|
(x y)+ |
smooth continued quadratic Bezier curveto relative |
|
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
<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>
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>
Links
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>