XSLT to generate SVG tutorial

The educational technology and digital learning wiki
Jump to navigation Jump to search

<pageby nominor="false" comments="false"/>

This article or section is currently under construction

In principle, someone is working on it and there should be a better version in a not so distant future.
If you want to modify this page, please discuss it with the person working on it (see the "history")

Introduction

This tutorial will teach you how to generate SVG graphics from XML data. We will produce both pure SVG and HTML5 embedded SVG.

Learning goals

  • Understand the purpose of XSLT, i.e. be able to think of XSLT as a translation language.
  • Do simple transformations from XML to HTML
  • Be able to use simple XPath expressions (tag and attribute names) in template selectors and for element and attribute extraction.
Prerequisites
Next steps

My first SVG from XML

Generating SVG with XSLT is really easy if you master some basic XSLT programming as explained in the XSLT Tutorial - Basics. Of course, you also should have some SVG basics.

At the time of writing, you can serve SVG in three different ways on the web:

  1. Embedded SVG in XHTML 1. This requires that you serve XHTML as XML. If you don't understand what that means, skip this option.
  2. Generate a pure SVG file. All modern browsers can display that.
  3. Generate an HTML5 file with inline SVG. All modern browsers can display that.

SVG Example level 0

We firstly will show how to produce some SVG graphic from the contents of a most simple XML document. We then show how to do this for HTML5.

Take the following XML file:

<?xml version="1.0" ?>
<?xml-stylesheet href="zero.xsl" type="text/xsl" ?>
<thing>
  <height>50</height>
  <width>100</width>
</thing>

We can imagine rendering this as a nice red rectangle. There we have to write a template that will extract contents of height and width and then use this to define the dimensions of the SVG rectangle. Absolute beginners should notice that the names of these XML tags do not matter, we also could have called these elements a and b...

Below is the XSLT code. As you can see, it is fairly straight forward. With respect to XSLT to HTML transforms, there are two differences:

  • We should declare the SVG namespace in the xsl:stylesheet root element on top
  • We should configure the output produced, else your browser may not want to display the SVG code.
<?xml version="1.0"?>

<xsl:stylesheet version="1.0" 
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
		xmlns="http://www.w3.org/2000/svg"
		>
  <xsl:output
      method="xml"
      indent="yes"
      standalone="no"
      doctype-public="-//W3C//DTD SVG 1.1//EN"
      doctype-system="http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"
      media-type="image/svg" />
  
  <xsl:template match="thing">
    <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" >
      <rect x="10" y="10" width="{width}" 
	    height="{height}" fill="red" stroke="black"/>  
    </svg>
  </xsl:template>
</xsl:stylesheet>

Now let us examine the template that deals with thing. Since we do not use a template for the root //), we firstly must generate the svg tag.

<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" >
   .....
</svg>

Next, we simply have to define an SVG rectangle and substitute the values for width and height by the values that we extract from the XML. {width} and {height} will be substituted by text found within <width> and <height> tags in the XML document.

Live Code (may include slight variations):

HTML5 example level 0

Now let's produce HTML5. The principle is exactly the same, but the output declaration is a bit different and the template is longer since we will have to produced more code.

<?xml version="1.0"?>

<xsl:stylesheet version="1.0" 
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
		xmlns="http://www.w3.org/2000/svg"
		>
<xsl:output
     method="xml"
     doctype-system="about:legacy-compat"
     omit-xml-declaration = "yes"
     encoding="UTF-8"
     indent="yes" />
  
  <xsl:template match="thing">
   <html xmlns="http://www.w3.org/1999/xhtml">
     <head>
       <meta charset="utf-8"></meta>
       <title>XHTML5 + SVG example</title>
     </head>
     <body>
       <p>This line is HTML, embedded SVG is below. Read the <a
       href="http://edutechwiki.unige.ch/en/XSLT_to_generate_SVG_tutorial">
       XSLT to generate SVG tutorial</a></p>

       <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" >
	 <rect x="10" y="10" width="{width}" 
	       height="{height}" fill="red" stroke="black"/>  
       </svg>
     </body>
   </html>
  </xsl:template>
  
</xsl:stylesheet>

Live Code (may include slight variations):

Introductory SVG example

The example we introduced above was really uninteresting. In XML data we usually find more than just two numbers. So let's see what we can do with a simple list of numbers:

Take the following xml file:

<?xml version="1.0" ?>
<?xml-stylesheet href="intro.xsl" type="text/xsl" ?>
<list>
 <item>10</item>
 <item>15</item>
 <item>12</item>
 <item>20</item>
 <item>5</item>
</list>

Such a list would render nicely as a bar chart:

The following code will do that. Of course it remains simple. E.g. good code would also compute a height and width parameter that takes into account both the data and the drawing space we want to use.

The difference between the "zero" example and this one is that we do some very simple math and also that we use XSLT functions count() and position().

  • The y coordinate of the rectangle is 100 minus the number found in the XML
  y="{100- .}
  • We use the count() function to figure out the number of item elements we got in the list.
    <rect x="10" y="105" width="{10 * count(item)}" 
	  height="5" fill="black" stroke="red"/>
  • Please also note that empty spaces between elements do count as nodes according to the XML standard. Therefore you must remove these empty nodes with the following instruction.
  <xsl:strip-space elements="list"/>
  • Each rectangle is positioned in the SVG according to its position in the XML list:
    <rect x="{10*position()}" y="{100- .}" width="10" 
	  height="{.}" fill="red" stroke="black"/>

The full source code is below. We will not discuss the HTML5 version here, but below is link that will provide you code.

<?xml version="1.0"?>
<!-- keep all three namespaces, xlink may not be needed, but the others are -->
<xsl:stylesheet version="1.0" 
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
		xmlns:xlink="http://www.w3.org/1999/xlink"
		xmlns="http://www.w3.org/2000/svg"
		>
  
  <!-- **** output/input configuration -->
  
  <xsl:output
      method="xml"
      indent="yes"
      standalone="no"
      doctype-public="-//W3C//DTD SVG 1.1//EN"
      doctype-system="http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"
      media-type="image/svg" />
  
  <!-- must remove white spaces within the list element, 
       otherwise count will not work -->
  <xsl:strip-space elements="list"/>
  
  <xsl:template match="/">
    <svg xmlns="http://www.w3.org/2000/svg" width="800" height="800" >
      <xsl:apply-templates/>
    </svg>
  </xsl:template>
  
  <xsl:template match="list">
    <rect x="10" y="105" width="{10 * count(item)}" 
	  height="5" fill="black" stroke="red"/>
    <xsl:apply-templates/>
  </xsl:template>
  
  <xsl:template match="item">
    <rect x="{10*position()}" y="{100- .}" width="10" 
	  height="{.}" fill="red" stroke="black"/>  
  </xsl:template>
  
</xsl:stylesheet>

Live example:

We also could procude a version for programmers who are scared of rule-based programming. You would use code like this:

<xsl:template match="list">
 <xsl:for-each select="item">
    <rect x="{10*position()}" y="{100- .}" width="10" height="{.}" 
          fill="red" stroke="black"/>            
  </xsl:for-each>
  </xsl:template>

Live example:


Work in progress

Links