SVG Utility: Example

The SVG utility is designed to help you to embed the SVG images that you generate using XSLT into an HTML page.

This page gives a simple example of using the SVG Utility to give a couple of charts based on some data within an HTML page. Say you had some data like:

<stats>
   <item><label>Jan</label><value>642</value></item>
   <item><label>Feb</label><value>527</value></item>
   <item><label>Mar</label><value>364</value></item>
   <item><label>Apr</label><value>843</value></item>
   <item><label>May</label><value>295</value></item>
   <item><label>Jun</label><value>250</value></item>
   <item><label>Jul</label><value>654</value></item>
   <item><label>Aug</label><value>828</value></item>
   <item><label>Sep</label><value>454</value></item>
   <item><label>Oct</label><value>732</value></item>
   <item><label>Nov</label><value>236</value></item>
   <item><label>Dec</label><value>546</value></item>
</stats>

You might want to generate a couple of SVG charts from this data - a bar chart and a line chart, for example. In this example, I use my:diagram elements in my stylesheet to determine the types of charts that I want to create:

<my:diagrams>
   <my:diagram type="column" />
   <my:diagram type="line" />
</my:diagrams>

First, I need to import the SVG utilities stylesheet into my stylesheet:

<xsl:import href="svg-utils.xsl" />

Second, I need to create a node-set variable within which there's one node per SVG image that I want to create. The nodes in this case are the my:diagram elements within the stylesheet, so I access them:

<xsl:variable name="images" select="document('')/*/my:diagrams/my:diagram" />

Now I need to insert the script and the embed elements into my document. I use the utility to insert the script into the head of my HTML, passing the $images variable as the value of the $images parameter:

<html>
   <head>
      <title>SVG Chart Demonstration</title>
      <xsl:call-template name="insertSVGScript">
         <xsl:with-param name="images" select="$images" />
      </xsl:call-template>
   </head>
   <body>
      ...
   </body>
</html>

In the body of the HTML page, I insert the embed elements by applying templates to the nodes held in the $images variable:

<html>
   <head>
      ...
   </head>
   <body>
      <h1>SVG Chart Demonstration</h1>
      <p>The following charts are generated automatically using XSLT.</p>
      <xsl:apply-templates select="$images" mode="createEmbed" />
   </body>
</html>

Finally, I need to create the SVG for the two diagrams. To use the SVG Utility, I need to do this within a template in getSVG mode:

<xsl:template match="my:diagram" mode="getSVG">
   <xsl:variable name="points" select="$stats/item" />
   <xsl:variable name="type" select="@type" />
   <xsl:variable name="space" select="30" />
   <xsl:variable name="width-height-ratio" select="2 div 3" />
   <xsl:variable name="col-width" select="20" />
   <xsl:variable name="col-gap" select="10" />
   <xsl:variable name="point-width" select="5" />
   <xsl:variable name="xaxis-width" 
                 select="count($points) * ($col-width + $col-gap)" />
   <xsl:variable name="width" select="round($xaxis-width + (2 * $space))" />
   <xsl:variable name="height" 
                 select="round($xaxis-width * $width-height-ratio + 
                               (2 * $space))" />
   <xsl:variable name="max-value">
      <xsl:for-each select="$points">
         <xsl:sort select="value" order="descending" />
         <xsl:if test="position() = 1"><xsl:value-of select="value" /></xsl:if>
      </xsl:for-each>
   </xsl:variable>
   <xsl:variable name="unit-multiplier" 
                 select="($xaxis-width * $width-height-ratio) div 
                         ($max-value * 1.05)" />
   <svg width="100%" viewBox="0 0 {$width} {$height}" 
        preserveAspectRatio="xMinYMin meet">
      <!-- border around chart -->
      <rect width="{$width}" height="{$height}" 
            style="fill: #CCC; stroke: black;" />
      <!-- X Axis line -->
      <line x1="{$space}" y1="{$height - $space}" 
            x2="{$width - $space}" y2="{$height - $space}" 
            style="stroke: black;" />
      <!-- Y Axis line -->
      <line x1="{$space}" y1="{$space}" 
            x2="{$space}" y2="{$height - $space}" 
            style="stroke: black;" />
      <xsl:for-each select="$points">
         <xsl:variable name="item-height" 
                       select="round(value * $unit-multiplier)" />
         <xsl:variable name="xMin" 
                       select="$space + 
                               (count(preceding-sibling::item) * 
                                ($col-width + $col-gap))" />
         <!-- Columns -->
         <xsl:choose>
            <xsl:when test="$type = 'column'">
               <rect x="{$xMin + ($col-gap div 2)}" 
                     y="{$height - $space - $item-height}" 
                     width="{$col-width}" height="{$item-height}" 
                     style="fill: blue; stroke: black;" />
            </xsl:when>
            <xsl:otherwise>
               <xsl:variable name="x" 
                             select="$xMin + ($col-gap div 2) + 
                                     (($col-width - $point-width) div 2)" />
               <xsl:variable name="y" 
                             select="$height - $space - 
                                     $item-height - ($point-width div 2)" />
               <rect x="{$x}"
                     y="{$y}"
                     width="{$point-width}"
                     height="{$point-width}"
                     style="fill: blue; stroke: black;" />
               <xsl:variable name="next" select="following-sibling::item" />
               <xsl:if test="$next">
                  <xsl:variable name="nextX" 
                                select="$xMin + $col-width + $col-gap * 1.5 + 
                                        (($col-width - $point-width) div 2)" />
                  <xsl:variable name="nextY" 
                                select="$height - $space - 
                                        ($point-width div 2) - 
                                        round($next/value * 
                                              $unit-multiplier)" />
                  <line x1="{$x + $point-width div 2}"
                        y1="{$y + $point-width div 2}"
                        x2="{$nextX + $point-width div 2}"
                        y2="{$nextY + $point-width div 2}"
                        style="stroke: blue;" />
               </xsl:if>
            </xsl:otherwise>
         </xsl:choose>
         <!-- Column captions -->
         <text x="{$xMin}" y="{$height - $space div 3}" 
               textLength="{$col-width}" style="font: Arial;">
            <xsl:value-of select="label" />
         </text>
      </xsl:for-each>
   </svg>
</xsl:template>

If you have an SVG Viewer plug-in installed, you can view this example. The full stylesheet is available at .

More Information


/xslt/utilities/svg-utils.xml by Jeni Tennison; generated using SAXON 6.5 from Michael Kay