Tags

, , , ,

For a personal project I have code written in Lua that draws images for print and display using SVG as an intermediate drawing language. The SVG artwork is then processed to produce PNG images for display, imported directly into a Scribus document as figures in a larger page layout, or as PDF files for pre-press and printing.

One of the features I needed was to combine hand-drawn artwork with software generated artwork. This meant loading a portion of a drawing from a standalone SVG file, and using it as part of a new SVG file.

Along the way I learned a few things that I’d like to share in case writing them down helps others with similar needs.

SVG

SVG (Scalable Vector Graphics) is a file format that allows elaborate drawing to be specified in vectors, which makes it easy to render them at any scale or resolution with all edges as sharp and smooth as possible.

In contrast, raster images (the display you are looking at right now, all printers other than pen plotters, as well as GIF, PNG and JPEG files) represent images as an array of colored pixels. You can’t enlarge such an image past the point where the resolution of the display medium is comparable to the resolution of the source file without the file’s pixels becoming fuzzy dots.

SVG is an ideal format to draw logos, graphs, and similar “graphic design” artwork. But for most users to see the results, it still needs to be rendered for their display. Many web browsers have support for SVG rendering (even when found in-line with the text in HTML), but support is not universal across the browsers most users are using today.

So my program creates artwork in SVG, then calls on an external utility to render it to PNG for display in blog posts or PDF for printing.

SVG in XML

SVG is itself and application of a more general format known as XML. The XML specification controls the detailed syntax, or what characters you use to format the file. The SVG specification controls the semantics, or how that syntax is interpreted to render artwork.

An XML file is text in a specific character set (Unicode in utf-8 by default), where the whole file is divided broadly into “content” and “markup”.

As used by SVG, almost all of the artwork is made with markup, for example you draw a circle using a circle tag, and specify where it goes and its radius with its cx, cy, and r attributes. Arbitrary paths are an extreme example of this, using the path tag with a terse language describing all the points that control the path in its d attribute.

The principle exception is for text, which uses the text tag with many attributes to specify font and style, with the content of that tag being the text to display.

Tree of Nodes

Like all XML documents, an SVG file has a single top-level tag that contains the whole drawing. That tag is named, unsurprisingly, svg.

Complete subsidiary drawings can be contained inside their own (nested) svg tags. And assuming they have their coordinate system adequately specified in its attributes, that nesting can be done by literally copying one SVG file inside another. So if we have playing cards in files named A-S.svg, 2-S.svg, through K-S.svg, a royal flush can be drawn by composing the files A-S.svg, K-S.svg, Q-S.svg, J-S.svg, and 10-S.svg, with suitable positioning and rotation. The resulting file for the hand would have an outline that looks like this:

<svg id="hand">
  <svg id="ace">...</svg>
  <svg id="king">...</svg>
  <svg id="queen">...</svg>
  <svg id="jack">...</svg>
  <svg id="ten">...</svg>
</svg>

There are other nodes that are naturally containers, the most common being the g tag that creates a group of objects that can be treated as a single object.

The coordinate system used for drawing is specified in the svg tag, and can be modified by transformations applied to each nested container and object. I take advantage of this to build a hand with each card file’s svg node contained in a g node that scales, rotates, and moves it to the right location.

Speedbump

Except it is never quite that simple.

XML includes several additional features that make things interesting, two of which are included in every file written by Inkscape (my favorite SVG editor).

First, there is the general idea of comments. These will look familiar if you have used HTML. Any (a white lie, so don’t push it) text enclosed between <!-- and --> is a comment. Comments can generally appear anywhere a tag or character entity can. In principle, the entire comment is simply invisible to the XML parser and any application. While in practice comments have been misused in HTML, in SVG they really are invisible. A file written by Inkscape will contain a comment near the top that says so.

Second, there is the XML Declaration which should appear at the top of the prolog of a compliant XML version 1.0 file and must appear at the top of the prologue of a compliant XML version 1.1 or later file. If it is missing, the file is assumed to be XML 1.0 and in a character encoding determined by context.

In addition, the prolog can contain XML Processing Instructions, a Document Type Definition, and comments and whitespace.

The key speedbump is that there can only be one prolog in a valid XML document. So when composing several SVG documents together, it is important to correctly strip out any prologs from the individual drawings.

Since the only interesting element that Inkscape includes in its prolog is the XML Declaration, it is sufficient to strip the XML Declarations out of any included files.

What happens if I hit the bump?

Inkscape’s rendering of composed documents that include more than one XML Declaration only fails occasionally.

Worse, the failure manifests as some relatively unrelated drawing nodes effectively being transformed from markup to text, and hence disappearing from the rendered output if Inkscape is used to render SVG to PNG or PDF.

Conclusions

SVG is generally easy to work with, as long as you use an XML aware system for all the required transformations. Note that in general you cannot parse XML with a regular expression, if actual parsing is required you must use a full XML parser.

When composing SVG from several files by simple concatenation, you will likely get multiple XML declarations in the finished output. Don’t do that. Always strip any declarations before concatenation.

Inkscape is great, but it has failure modes that are quirky rather than clear and simple warning or error message.


If you have a project involving embedded systems, micro-controllers, electronics design, audio, video, or more we can help. Check out our main site and call or email us with your needs. No project is too small!

+1 626 303-1602
Cheshire Engineering Corp.
710 S Myrtle Ave #315
Monrovia, CA 91016


(Written with StackEdit.)

Advertisements