XML.com: XML From the Inside Out
oreilly.comSafari Bookshelf.Conferences.

advertisement

Automated Tree Drawing: XSLT and SVG

September 08, 2004

Trees are a very basic abstraction used in many computer science areas, including XML. If you're writing a document about tree structures you'll often want to show trees as graphics. This is not a problem for a few trees as they can be hand drawn in some graphics editor. But if you need dozens of trees, you would do well to use a compact text syntax for describing trees that can later be turned into nice pictures. In this article I'll show you how to parse simple text notation by means of XSLT and turn it into SVG graphics.

Compact Tree Syntax

Before we delve into details of XSLT implementation we should describe our task. We want to create a transformation that can take a compact tree notation and turn it into an SVG graphic. Our tree syntax will be very simple. Each character will correspond to one node, and subnodes will be closed in parenthesis. For example, the following expression a(bcd(ef)) represents the tree depicted in Figure 1.

Rendered tree a(bcd(ef)) Figure 1. Rendered tree a(bcd(ef)).

 

Our notation is also able to express hedges. A hedge is a sequence of trees or alternatively, “tree without the root node.” Figure 2 shows rendering of a hedge a(bcd)e(fgh).

Rendered hedge a(bcd)e(fgh) Figure 2. Rendered hedge a(bcd)e(fgh).

If the name of a node should be longer than one character, we can put the name inside curly braces. You can see the expression {foo}({bar}{baz}) rendered as a tree in Figure 3.

Rendered tree {foo}({bar}{baz})
Figure 3. Rendered tree {foo}({bar}{baz}).

Solution Overview

Conversion from the compact syntax into the full-fledged SVG image is not trivial, so we will do it in several steps. The first step will parse the textual representation of a tree and will create an XML structure representing the same tree. The following XML fragment is an XML representation of the tree from Figure 1.

<node label="a">
   <node label="b"/>
   <node label="c"/>
   <node label="d">
      <node label="e"/>
      <node label="f"/>
   </node>
</node>

Each node in the tree is represented by a node element. Element nesting corresponds to the hierarchy of the tree. The label of each node is captured in the label attribute.

Creating XML representation from the text string will be the hardest part of the whole task since XSLT does not offer powerful string manipulation functions. But once we get the XML representation of a tree we can process it quite easily with XSLT.

In the next step we'll decorate the XML tree with some layout information. We'll add depth of a node in the tree and width of the attached sub-tree to each node.

<node depth="1" width="4" label="a">
   <node depth="2" width="1" label="b"/>
   <node depth="2" width="1" label="c"/>
   <node depth="2" width="2" label="d">
      <node depth="3" width="1" label="e"/>
      <node depth="3" width="1" label="f"/>
   </node>
</node>

With this layout information it is quite easy to get an SVG image. We must just emit correct SVG constructs and turn the layout information to the real coordinates using several calculations. And with that the requested SVG image of the tree is created.

<svg:svg xmlns:svg = "http://www.w3.org/2000/svg" viewBox = "0 0 80 60">
 <svg:g transform = "translate(0,-5) scale(10)">
  <svg:text x = "4" y = "1.8" style = "text-anchor: middle; font-size: 0.9;">a</svg:text>
  <svg:rect x = "3.1" y = "1" width = "1.8" height = "1" rx = "0.4" ry = "0.4" style = "fill: none; stroke: black; stroke-width: 0.1;"/>
  <svg:line x1 = "4" y1 = "2" x2 = "1" y2 = "3" style = "stroke-width: 0.1; stroke: black;"/>
  <svg:line x1 = "4" y1 = "2" x2 = "3" y2 = "3" style = "stroke-width: 0.1; stroke: black;"/>
  <svg:line x1 = "4" y1 = "2" x2 = "6" y2 = "3" style = "stroke-width: 0.1; stroke: black;"/>
  <svg:text x = "1" y = "3.8" style = "text-anchor: middle; font-size: 0.9;">b</svg:text>   <svg:rect x = "0.1" y = "3" width = "1.8" height = "1" rx = "0.4" ry = "0.4" style = "fill: none; stroke: black; stroke-width: 0.1;"/>
  <svg:text x = "3" y = "3.8" style = "text-anchor: middle; font-size: 0.9;">c</svg:text>
  <svg:rect x = "2.1" y = "3" width = "1.8" height = "1" rx = "0.4" ry = "0.4" style = "fill: none; stroke: black; stroke-width: 0.1;"/>
  <svg:text x = "6" y = "3.8" style = "text-anchor: middle; font-size: 0.9;">d</svg:text>
  <svg:rect x = "5.1" y = "3" width = "1.8" height = "1" rx = "0.4" ry = "0.4" style = "fill: none; stroke: black; stroke-width: 0.1;"/>
  <svg:line x1 = "6" y1 = "4" x2 = "5" y2 = "5" style = "stroke-width: 0.1; stroke: black;"/>
  <svg:line x1 = "6" y1 = "4" x2 = "7" y2 = "5" style = "stroke-width: 0.1; stroke: black;"/>
  <svg:text x = "5" y = "5.8" style = "text-anchor: middle; font-size: 0.9;">e</svg:text>
  <svg:rect x = "4.1" y = "5" width = "1.8" height = "1" rx = "0.4" ry = "0.4" style = "fill: none; stroke: black; stroke-width: 0.1;"/>
  <svg:text x = "7" y = "5.8" style = "text-anchor: middle; font-size: 0.9;">f</svg:text>
  <svg:rect x = "6.1" y = "5" width = "1.8" height = "1" rx = "0.4" ry = "0.4" style = "fill: none; stroke: black; stroke-width: 0.1;"/>
 </svg:g>
</svg:svg>

Pages: 1, 2, 3

Next Pagearrow







close