This post will be a really basic intro to SVG graphics—how they are structured, and how you can generate and manipulate them, primarily with ColdFusion. But you don’t have to wade through my prose, you are welcome to skip straight to the SVG Spec by W3.
Let’s start with a really basic SVG image. The image below is a PNG rendered from the SVG, but if you click through you can see the source file. Depending on your browser’s support for SVG (almost every browser other then IE supports SVG natively), you might see the raw XML or a rendering of the graphic.
SVG source files are XML before they get rendered into GIF, JPEG, or PNG formats. The source for this graphic is straightforward:
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg version="1.2" width="500" height="64" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <linearGradient id="Fade"> <stop style="stop-color: #000000; stop-opacity: 1;" offset="0"/> <stop style="stop-color: #000000; stop-opacity: 0;" offset="1"/> </linearGradient> <linearGradient id="Tropical"> <stop style="stop-color: #ff0000; stop-opacity: 1;" offset="0"/> <stop style="stop-color: #ff9900; stop-opacity: 1;" offset="0.33"/> <stop style="stop-color: #ffcc00; stop-opacity: 1;" offset="0.5"/> <stop style="stop-color: #ff9900; stop-opacity: 1;" offset="0.66"/> <stop style="stop-color: #ff0000; stop-opacity: 1;" offset="1"/> </linearGradient> <linearGradient xlink:href="#Tropical" id="TropicalGrad" gradientUnits="userSpaceOnUse" x1="230" y1="4" x2="230" y2="20"/> <linearGradient xlink:href="#Tropical" id="StarGrad" gradientUnits="userSpaceOnUse" x1="466" y1="0" x2="492" y2="28"/> <linearGradient xlink:href="#Fade" id="FadeGrad" x1="0" y1="54" x2="0" y2="14" gradientUnits="userSpaceOnUse"/> </defs> <rect x="0" y="0" width="500" height="64" style="fill: #000000;"/> <g id="TopHalf" style="font-family: Arial Black; font-size: 26px; text-align: center; text-anchor: middle; fill-opacity: 1;"> <text x="230" y="24" width="460" height="64" style="fill: url(#TropicalGrad);">Vector graphics are so Web2.0</text> <path d="M492,21 L483,20 L477,28 L475,19 L466,15 L474,10 L474,1 L482,7 L490,5 L487,13 L492,21 z" style="fill: url(#StarGrad); stroke: none;"/> </g> <use xlink:href="#TopHalf" transform="scale(1, -1) translate(0, -64)"/> <rect style="fill: url(#FadeGrad); stroke: none;" width="500" height="32" x="0" y="32"/> </svg>
At the top of the file you can see typical XML stuff: an XML declaration, then a root node. The root node references two namespaces: one for the SVG format (duh), and the other for links within the file. The latter allows for things like gradients and stock text, which are accomplished by referencing definitions in other parts of the document. Beyond that, there’s a version corresponding to the version of the SVG spec, and basic height and width attributes. No big deal so far, right?
The defs section defines stock things like gradients. Gradients come in two parts: the first gradient defines the colors, while a second gradient references the first and maps those colors onto the canvas with specific coordinates. This way, you can use the same gradient in different places without having to redefine the colors. I’ve done this here, where #Tropical defines the orange and red colors, then #TropicalGrad and #StarGrad place those colors on different parts of the canvas.
The rest of the file is just drawing instructions. As you can guess, the rect element defines a rectangle. You can also see that SVG uses CSS for styling elements. Styles can be broken out into XML attributes, such as fill="#000000", if you like.
The g element defines a group, or a layer. Just like container elements in HTML, group elements are good for holding styles that the inner elements can use.
Text is … tricky. The v1.0 and v1.2 SVG specs handle text slightly differently, depending on if the text is a single line or multiple lines. Wrapping text blocks were not around until the v1.2 specs, and use a different set of elements: flowText, flowDiv, flowPara, and flowRoot. The v1.0 element is much simpler: text. On the one hand it’s much less flexible, but on the other hand support for it is much more uniform in various viewers and web browsers and renderers. I’ll have an entire post about handling text in SVG.
Drawable things are handled with the path element. Remember the old LOGO language with the turtle? Same thing. The d attribute handles the drawing instructions, most of which are very easy to remember.
The use element is similar to a #include or cfinclude, except that you will generally use it to clone an element from the same document. In this example, we clone the layer/group containing the text and star, then use a series of transform commands on it to pull the thing down, flip it, but not reverse it. Sorry.
The last rect is cheating a bit—instead of masking out the cloned content, I put an alpha gradient on top of it that starts transparent and fades to opaque. Masking is a little trickier, but not all that much, and it really comes in handy when you’re doing 32-bit alpha PNG files.
So there you have it, the basics of SVG: structure, basic shapes, text, groups, and transforms. There’s plenty more fine detail, but those are the broad strokes.
As a thought exercise, how much would we have to change if we wanted to put the graphic on a white background? Or a blue background?
Not much, actually:
The #Fade gradient has black stops.
The first rect has a black fill.
How could we modify it with ColdFusion? Presumably, we’d load the file as an XML document. Finding the specific places to modify should be as simple as using a couple of XPath queries. Something like “id('Fade')/stop” would work, and you could add id elements to the stops if you wanted to. Finding the first rect would be similar, and again easier if an id was added.
I’m sure you can see where I’m going with this—generating and modifying SVG with ColdFusion is wicked easy. In my next post, we’ll build an SVG renderer using Batik and ColdFusion so we can get some actual image files.