The Journal of Online Mathematics and Its Applications, Volume 7 (2007)
Scalable Vector Graphics, David Lane

II. SVG: The Nuts and Bolts

This is the second part of my article on scalable vector graphics. If you have not already done so, you should read the first part which contains an introduction to SVG and a small gallery of mathematical examples. In this part, I'll take the simplest of these, the angle in a semicircle, and give a detailed step by step construction.

The images below are all screen captures (reduced in size). Clicking on the image opens the associated SVG in a separate window. I've provided the complete source code as a separate link in each case.

Triangle in a semicircle
View source code

We'll proceed in three steps:

  1. We will review the XML format as it applies to SVG.
  2. Next we will add SVG "elements" to construct a static image
  3. Finally, we will add drag and drop functionality using JavaScript.

1. XML Basics

XML, short for the eXtensible Markup Language, is a text based format for storing and transmitting data, designed to be readable by both humans and computers. There are various "grammars" of XML, including XHTML (the XML version of HTML), SVG, MathML (the Mathematics Markup Language), ChemML (the Chemistry Markup Language), and GML (the Geography Markup Language), among many others.

In the case of XHTML and SVG, the XML format is used to store data which is interpreted and rendered to the screen by a web browser. But this is not the only use for the format. XML can also be used to transfer data in a distributed computer application (XML-RPC) , to transfer data between clients and servers (XForms or Adobe XDF), as a format for application configuration data (similar to an .ini file), and as an application file format (For example, Microsoft Word files can be saved as WordML).

All XML files have certain features in common. To see what these are, let's compare the basic structure of an XHTML and an SVG document.

Here is the XHTML:

<?xml version="1.0" encoding="iso-8859-1"?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>
<title>Typical XHTML Document</title>
</head>

<body>
...
html content goes here
...
</body>

</html>

And here is a typical SVG:

<?xml version="1.0" encoding="iso-8859-1"?>

<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
   "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg xmlns="http://www.w3.org/2000/svg"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   width="500" height="300" >
...
svg content goes here
...
</svg>

The XML Declaration

The first line of each document is called the "xml declaration", and is common to every type of XML file. The encoding attribute can be omitted for plain vanilla ASCII, but may need to be changed, say to UTF-16, for non-English language support.

DTDs

The next line gives a link to the Document Type Definition (DTD), which contains a description of the "legal" elements and attributes for the given format. If your XML document contains a DOCTYPE line then you can validate your code using the W3C Markup Validator. (You may have also heard the phrase "XML schema". A schema is similar to a DTD--it is a more advanced way of defining an XML grammar--one of the differences being that the schema itself is written in an XML format.)

It's possible to leave the DOCTYPE declaration out of an SVG file without any adverse effect, and in fact some authors recommend this. See, for example, Jonathan Watt's SVG authoring guidelines. (I'm rather obviously ignoring some of his advice--e.g. on DOCTYPE declarations and on the use of the "style" attribute. However, his comments about using DOM level 2 namespace methods are very important).

A final note: In some examples on the web you may encounter the following out-of-date DOCTYPE declaration:

<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
   "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">

This refers to the previous SVG recommendation version 1.0. Note that this is the only DTD that the Adobe SVG viewer recognizes, so when you load a file with the newer DTD, ASV will briefly complain about an "unknown doctype declaration." This is harmless.

Root elements

The outermost tag of an XML document is called the "root element". In the first example the root element is the <html> tag, and in the second it's the <svg> tag. All of the document contents must be contained between these tags.

The XML Namespace or "xmlns" attribute is designed to provide a unique identifier that precludes inteference with other XML data with the same tags. The SVG reader or browser doesn't actually visit the site to check anything, but if you leave it off some browsers (e.g. Firefox) will fail to render the SVG.

Note the two usages (from the SVG example):

The first of these is the default namespace for the root <svg> element. Any un-prefixed XML content (elements or attributes) contained in the document are understood to be part of the SVG specification. All other content has to be prefixed.

I included the XLink namespace in my example because the "href" attribute is frequently used in SVG documents to refer to gradients , filters and re-usable elements. But "href" is a part of the XLink specification, not SVG. The browser doesn't know this automatically, so the "xmlns:xlink" line tells it that elements and attributes which are prefixed by "xlink:" are a part of that specification. In other words, to use href in the document I need to write "xlink:href" rather than just "href" by itself

Note that I don't have to use "xlink" for this prefix. I could have written something like "xmlns:foo="http://www.w3.org/1999/xlink" and then written "foo:href" instead.

2. Adding SVG Elements

SVG Content Tags

Finally we're ready to add the content. You don't really have to understant any of the previous discussion on XML in order to write your own SVG's. Just copy the code template above and start adding content between the <svg> tags.

Commonly used elements include the obviously named <circle>, <ellipse>, <rect>, <line>, <polyline>, <polygon> and <path> tags, as well as the important tags <g> for grouping elements and <use> for re-using elements defined in the <defs> section. The W3C SVG 1.1 Recommendation has a complete list and explains the syntax in each case (Click on the link and scroll down to find the page on "Basic Shapes", for example). Another very good listing can be found at the W3Schools SVG site.

Here is a very basic (and rather ugly) SVG showing the use of some common tags. With the exception of the somewhat mysterious <path> element, you can probably guess the meaning of the attributes in each case.

<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
   "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg xmlns="http://www.w3.org/2000/svg"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   width="270" height="270" >
<polygon style="stroke:#24a;stroke-width:1.5;fill:#eefefe"
   points="10,10 180,10 10,250 10,10" />
<circle style="stroke:#d33;stroke-width:2;fill:#7ce"
   cx="100" cy="80" r="50" />
<rect style="stroke:#2aa;stroke-width:7;fill:#ded;opacity:.8"
   x="30" y="80" height="120" width="220" />
<line style="stroke:#eea;stroke-width:8" x1="10" y1="30" x2="260" y2="100"/>
<path style="fill:#daa;fill-rule:evenodd;stroke:none"
  d="M 230,250 C 360,30 10,255 110,140 z "/>

</svg>

Changing Coordinates

Now we're ready to tackle the semicircle. The first step is to pick a width and a height, say 500 x 300.

<svg xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    width="500" height="300" >

This establishes a "user coordinate system" with the origin (0,0) in the upper left corner and with the x and y coordinates increasing to the right and downwards. If you're like me and you hate to thinking upside down, it's possible change the coordinate system for all of the SVG tags by placing them inside of a group element <g> with a transform attribute, like this.

<g transform="matrix(1, 0, 0, -1, 250, 250)">
...
svg elements with center at (250,250), with the positive
directions up and to the right.
...
</g>

The transform attribute can take several different values, for example "scale", "rotate", "skew" and "translate." The "matrix" value combines all of these effects into a single 3 × 3 matrix, using the following convention:

matrix[[a,c,e],[b,d,f],[0,0,1]]

SVG uses here the fact that the group of affine transformations of the plane can be represented as a set of linear transformations of 3-space, provided we use the z coordinate for the translations. Note that since the last row of the matrix is always (0, 0, 1), only six pieces of data are required to specify a transformation.

In this case the transform moves the origin to the point (250, 250) and scales the y coordinate by −1, flipping it upside down (or rightside up, depending on your point of view). Multiplying my matrix by a column vector of the form (x , y, 1) transforms a vector in the new coordinate system back to the original coordinates. Try it to see the effect. For more information, see the SVG specification: "Coordinate System Transformations" (Sec 7.4).

This trick has it's drawbacks. For one thing, included text will be upside down unless I leave it outside of this group, or else flip it again vertically inside of the group. Also we'll see below that we have to compensate for this transform in order to use the mouse --- translating the window "mouse coordinates" to SVG "user coordinates."

CSS Styles

Before creating the semicircle, let's anticipate the need for some CSS styling.

If you plan to have several elements with identical style attributes, then you should declare these separately and reference them using the "class=" attribute, just as you would when writing HTML. In my semicircle, for example, there will be several labels which will share the same font, the fill and stroke of the points themselves, and the stroke of the lines connecting all of the points.

The <style> information is included in a <defs> section which you should place after the opening <svg> tag and before the rest of the content, like this:

<defs>
<style type="text/css"><![CDATA[
   .axes { fill:none;stroke:#333333;stroke-width:1.6 }
   .pointlabels { font-size:16px; font-family: Arial; font-style: italic;
         stroke:none; fill:#000000; text-anchor: middle }
   .point{ fill:#000000; stroke:white; stroke-width:1 }
   .outline{ fill:#ffffdd; stroke:#0077bb; stroke-width:3 }
   .thinline{ stroke:#770000; stroke-dasharray: 12,4; stroke-width:1.6 }
   .thickline{ fill:none; stroke:#ff2222; stroke-width:3.5 }
   ]]>
</style>
</defs>

The [CDATA[ ... ]] enclosing the style information is a standard XML construct for hiding information which might confuse an XML parser. I'll use this again in the next section to "hide" the JavaScript.

Building the Semicircle

Now we're ready...

I'll put point A at (−200,0), point C at (200, 0), point B at (0, 200), and a point D at the origin, connecting them all with lines, and adding labels. This is done with the three tags <circle>, <line> and <text>. Notice how I had to flip the text tags vertically.

<g transform="matrix(1, 0, 0, -1, 250, 250)">
   <line class="axes" x1="-240" y1="0" x2="240" y2="0" />
   <line class="thickline" x1="200" y1="0" x2="0" y2="200" />
   <line class="thickline" x1="-200" y1="0" x2="0" y2="200" />
   <line class="thinline" x1="0" y1="0" x2="0" y2="200" />
   <circle class="point" cx="0" cy="200" r="5" />
   <circle class="point" cx="0" cy="0" r="5" />
   <circle class="point" cx="-200" cy="0" r="5" />
   <circle class="point" cx="200" cy="0" r="5" />
   <text class="pointlabels" x="0" y="-214" transform="scale(1,-1)">B</text>
   <text class="pointlabels" x="-210" y="20" transform="scale(1,-1)">A</text>
   <text class="pointlabels" x="210" y="20" transform="scale(1,-1)">C</text>
   <text class="pointlabels" x="0" y="20" transform="scale(1,-1)">D</text>
</g>

Here is the result:

angle in a semicircle
View source code

The semicircle and its enclosed area are written using the <path> element, the small square using the <rect> element.

<path class="outline" d="M -200,0 A200,200 0 0,0 200,0 L -200,0"/>
<rect transform="rotate(225,0,200)"
x="0" y="200" width="20" height="20" style="fill:#ffffff; stroke:#000000; stroke-width:1.6"/>

The <path> element can draw rectilinear paths, arcs, quadratic or cubic beziers using a complicated syntax described in Chapter 8 of the specification. In this case the "d" attribute string can be read as:

Move (M) to the point (−200, 0), then draw an arc (A) along an ellipse with x radius = 200 and y radius = 200, with x-axis unrotated, in the shorter. negative angle direction to the point (200, 0). Finally, close off the loop by drawing a straight line (L) back to (−200, 0)

The <rect> is placed with its lower left hand corner at (0, 200), then rotated counterclockwise by 225 degrees around the point (0, 200). Note that I just said lower left and counterclockwise; this is because this element is inside of my big group <g> with the matrix transform discussed above. In an un-transformed coordinate system it would have been upper left and clockwise instead.

Angle in a circle
View source code

3. Drag and Drop with SVG

The SVG Document Object Model

SVG uses the W3C SVG Document Object Model, which is an extension of the familiar Dom Level 2 Core specification. Web page designers who are accustomed to manipulating HTML documents using JavaScript will recognize most of the techniques below, but some of the objects, properties and methods will be new.

The descriptions of these properties and methods are spread over the various sections of the SVG specification itself. Below, for example, I create an instance of an object called an SVGPoint. The specifications for this object can be found in the SVG chapter on "Document Structure" (Chap 5) in the section called "DOM Interfaces" (Sec 5.17).

The <script> Tag

We will place the JavaScripts after the opening <svg> tag and before the <def>'s section. The contents of the script are hidden from the XML parser in a CDATA section:

<script type="text/ecmascript">
//<![CDATA[
...
javascript code
...
// ]]>
</script>

An Easy Example

Before adding drag and drop to the semicircle, let's look at a bare-bones example with the minimum code possible. Here I've placed a single blue circle on a yellow background. If you click anywhere on the canvas the circle will snap to the mouse, then you can drag and drop it where you like.

A simple example
View source code

Here's how it works...

In the JavaScript I create an object called myCirc, which is associated to the svg <circle> element using the getElementById() method.

myCirc = document.getElementById("mycirc");

This line is contained inside the function Init(), called in response to the document load "event." Note the change in the root <svg> tag.

<svg xmlns="http://www.w3.org/2000/svg"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   width="300" height="200"
   onload="Init( evt )" >

Now the cx and cy attributes of myCirc can be updated using setAttributeNS().

myCirc.setAttributeNS(null, "cx", new x coordinate );
myCirc.setAttributeNS(null, "cy", new y coordinate ); 

To use the mouse we have to add mouse event listeners to the SVG itself, so I've placed a transparent "listener canvas" on top of the other elements. A mouse event anywhere on this canvas calls the corresponding JavaScript function.

<rect id="canvas" x="0" y="0" width="300" height="200" opacity="0"
   pointer-events="visible"
   onmousedown="onMouseDown(evt)"
   onmousemove="onMouseMove(evt)"
   onmouseup="onMouseUp(evt)"/>

Finally, there's the function getMouse(evt), which is called in various spots by the other functions. The mouse coordinates are conveniently stored as an ordered pair (x,y) in an SVGPoint object.

function getMouse(evt){
   var position = svgRoot.createSVGPoint();
   position.x = evt.clientX;
   position.y = evt.clientY;
   return position;
}

The rest of the code should be obvious.

Adding Drag and Drop to the Semicircle

The full semicircle example (source code) has several different elements whose attributes are updated using the method setAttributeNS(), but the basic structure of the code is the same.

One difference is the following function, which translates the mouse coordinates into "local user coordinates," taking into account our change of coordinates in the outermost <g> element.

function getUserCoordinates(p1){
   var p1;
   var p2 = svgRoot.createSVGPoint();
   p2.x = p1.x - 250;
   p2.y = 250 -p1.y;
   return p2;
}

Note that if you zoom or pan, or otherwise change the transform matrix or viewbox, this will break the mouse functionality. If you need to account for these dynamically, check out Kevin Lindsay's Mouser JavaScript class at kevlindev.com.