Index: trunk/src/com/kitfox/svg/Tspan.java
===================================================================
--- trunk/src/com/kitfox/svg/Tspan.java	(revision 4256)
+++ trunk/src/com/kitfox/svg/Tspan.java	(revision 6002)
@@ -1,41 +1,48 @@
 /*
- * Stop.java
+ * SVG Salamander
+ * Copyright (c) 2004, Mark McKay
+ * All rights reserved.
  *
+ * Redistribution and use in source and binary forms, with or 
+ * without modification, are permitted provided that the following
+ * conditions are met:
  *
- *  The Salamander Project - 2D and 3D graphics libraries in Java
- *  Copyright (C) 2004 Mark McKay
+ *   - Redistributions of source code must retain the above 
+ *     copyright notice, this list of conditions and the following
+ *     disclaimer.
+ *   - Redistributions in binary form must reproduce the above
+ *     copyright notice, this list of conditions and the following
+ *     disclaimer in the documentation and/or other materials 
+ *     provided with the distribution.
  *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Lesser General Public
- *  License as published by the Free Software Foundation; either
- *  version 2.1 of the License, or (at your option) any later version.
- *
- *  This library is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- *  Lesser General Public License for more details.
- *
- *  You should have received a copy of the GNU Lesser General Public
- *  License along with this library; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- *  Mark McKay can be contacted at mark@kitfox.com.  Salamander and other
- *  projects can be found at http://www.kitfox.com
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE. 
+ * 
+ * Mark McKay can be contacted at mark@kitfox.com.  Salamander and other
+ * projects can be found at http://www.kitfox.com
  *
  * Created on January 26, 2004, 1:56 AM
  */
-
 package com.kitfox.svg;
 
 import com.kitfox.svg.xml.StyleAttribute;
-import java.awt.*;
-import java.awt.font.*;
-import java.awt.geom.*;
-import java.util.*;
-
-import com.kitfox.svg.xml.*;
-import org.xml.sax.*;
-
-//import org.apache.batik.ext.awt.geom.ExtendedGeneralPath;
+import java.awt.Graphics2D;
+import java.awt.Shape;
+import java.awt.font.FontRenderContext;
+import java.awt.font.GlyphMetrics;
+import java.awt.font.GlyphVector;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.Rectangle2D;
 
 /**
@@ -43,6 +50,8 @@
  * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
  */
-public class Tspan extends ShapeElement {
-
+public class Tspan extends ShapeElement
+{
+
+    public static final String TAG_NAME = "tspan";
     float[] x = null;
     float[] y = null;
@@ -50,18 +59,30 @@
     float[] dy = null;
     float[] rotate = null;
-
     private String text = "";
-
     float cursorX;
     float cursorY;
 
 //    Shape tspanShape;
-
-    /** Creates a new instance of Stop */
-    public Tspan() {
-    }
-
-    public float getCursorX() { return cursorX; }
-    public float getCursorY() { return cursorY; }
+    /**
+     * Creates a new instance of Stop
+     */
+    public Tspan()
+    {
+    }
+
+    public String getTagName()
+    {
+        return TAG_NAME;
+    }
+
+    public float getCursorX()
+    {
+        return cursorX;
+    }
+
+    public float getCursorY()
+    {
+        return cursorY;
+    }
 
     public void setCursorX(float cursorX)
@@ -74,28 +95,28 @@
         this.cursorY = cursorY;
     }
-/*
-    public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent)
-    {
-		//Load style string
-        super.loaderStartElement(helper, attrs, parent);
-
-        String x = attrs.getValue("x");
-        String y = attrs.getValue("y");
-        String dx = attrs.getValue("dx");
-        String dy = attrs.getValue("dy");
-        String rotate = attrs.getValue("rotate");
-
-        if (x != null) this.x = XMLParseUtil.parseFloatList(x);
-        if (y != null) this.y = XMLParseUtil.parseFloatList(y);
-        if (dx != null) this.dx = XMLParseUtil.parseFloatList(dx);
-        if (dy != null) this.dy = XMLParseUtil.parseFloatList(dy);
-        if (rotate != null)
-        {
-            this.rotate = XMLParseUtil.parseFloatList(rotate);
-            for (int i = 0; i < this.rotate.length; i++)
-                this.rotate[i] = (float)Math.toRadians(this.rotate[i]);
-        }
-    }
-    */
+    /*
+     public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent)
+     {
+     //Load style string
+     super.loaderStartElement(helper, attrs, parent);
+
+     String x = attrs.getValue("x");
+     String y = attrs.getValue("y");
+     String dx = attrs.getValue("dx");
+     String dy = attrs.getValue("dy");
+     String rotate = attrs.getValue("rotate");
+
+     if (x != null) this.x = XMLParseUtil.parseFloatList(x);
+     if (y != null) this.y = XMLParseUtil.parseFloatList(y);
+     if (dx != null) this.dx = XMLParseUtil.parseFloatList(dx);
+     if (dy != null) this.dy = XMLParseUtil.parseFloatList(dy);
+     if (rotate != null)
+     {
+     this.rotate = XMLParseUtil.parseFloatList(rotate);
+     for (int i = 0; i < this.rotate.length; i++)
+     this.rotate[i] = (float)Math.toRadians(this.rotate[i]);
+     }
+     }
+     */
 
     /**
@@ -107,18 +128,29 @@
     }
 
-    
     protected void build() throws SVGException
     {
         super.build();
-        
+
         StyleAttribute sty = new StyleAttribute();
-        
-        if (getPres(sty.setName("x"))) x = sty.getFloatList();
-
-        if (getPres(sty.setName("y"))) y = sty.getFloatList();
-
-        if (getPres(sty.setName("dx"))) dx = sty.getFloatList();
-
-        if (getPres(sty.setName("dy"))) dy = sty.getFloatList();
+
+        if (getPres(sty.setName("x")))
+        {
+            x = sty.getFloatList();
+        }
+
+        if (getPres(sty.setName("y")))
+        {
+            y = sty.getFloatList();
+        }
+
+        if (getPres(sty.setName("dx")))
+        {
+            dx = sty.getFloatList();
+        }
+
+        if (getPres(sty.setName("dy")))
+        {
+            dy = sty.getFloatList();
+        }
 
         if (getPres(sty.setName("rotate")))
@@ -127,10 +159,10 @@
             for (int i = 0; i < this.rotate.length; i++)
             {
-                rotate[i] = (float)Math.toRadians(this.rotate[i]);
-            }
-                
-        }
-    }
-    
+                rotate[i] = (float) Math.toRadians(this.rotate[i]);
+            }
+
+        }
+    }
+
     public void addShape(GeneralPath addShape) throws SVGException
     {
@@ -138,14 +170,19 @@
         {
             cursorX = x[0];
+        } else if (dx != null)
+        {
+            cursorX += dx[0];
+        }
+
+        if (y != null)
+        {
             cursorY = y[0];
-        }
-        else if (dx != null)
-        {
-            cursorX += dx[0];
+        } else if (dy != null)
+        {
             cursorY += dy[0];
         }
 
         StyleAttribute sty = new StyleAttribute();
-        
+
         String fontFamily = null;
         if (getStyle(sty.setName("font-family")))
@@ -160,4 +197,11 @@
             fontSize = sty.getFloatValueWithUnits();
         }
+
+        float letterSpacing = 0;
+        if (getStyle(sty.setName("letter-spacing")))
+        {
+            letterSpacing = sty.getFloatValueWithUnits();
+        }
+
 
         //Get font
@@ -165,5 +209,5 @@
         if (font == null)
         {
-            addShapeSysFont(addShape, font, fontFamily, fontSize);
+            addShapeSysFont(addShape, font, fontFamily, fontSize, letterSpacing);
             return;
         }
@@ -171,5 +215,5 @@
         FontFace fontFace = font.getFontFace();
         int ascent = fontFace.getAscent();
-        float fontScale = fontSize / (float)ascent;
+        float fontScale = fontSize / (float) ascent;
 
         AffineTransform xform = new AffineTransform();
@@ -184,5 +228,8 @@
             xform.setToTranslation(cursorX, cursorY);
             xform.scale(fontScale, fontScale);
-            if (rotate != null) xform.rotate(rotate[posPtr]);
+            if (rotate != null)
+            {
+                xform.rotate(rotate[posPtr]);
+            }
 
             String unicode = text.substring(i, i + 1);
@@ -200,6 +247,5 @@
                 cursorX = x[posPtr];
                 cursorY = y[posPtr++];
-            }
-            else if (dx != null && posPtr < dx.length)
+            } else if (dx != null && posPtr < dx.length)
             {
                 cursorX += dx[posPtr];
@@ -207,5 +253,5 @@
             }
 
-            cursorX += fontScale * glyph.getHorizAdvX();
+            cursorX += fontScale * glyph.getHorizAdvX() + letterSpacing;
         }
 
@@ -213,7 +259,8 @@
     }
 
-    private void addShapeSysFont(GeneralPath addShape, Font font, String fontFamily, float fontSize)
-    {
-        java.awt.Font sysFont = new java.awt.Font(fontFamily, java.awt.Font.PLAIN, (int)fontSize);
+    private void addShapeSysFont(GeneralPath addShape, Font font,
+        String fontFamily, float fontSize, float letterSpacing)
+    {
+        java.awt.Font sysFont = new java.awt.Font(fontFamily, java.awt.Font.PLAIN, (int) fontSize);
 
         FontRenderContext frc = new FontRenderContext(null, true, true);
@@ -226,6 +273,9 @@
         {
             xform.setToIdentity();
-            xform.setToTranslation(cursorX, cursorY);
-            if (rotate != null) xform.rotate(rotate[Math.min(i, rotate.length - 1)]);
+            xform.setToTranslation(cursorX + i * letterSpacing, cursorY);
+            if (rotate != null)
+            {
+                xform.rotate(rotate[Math.min(i, rotate.length - 1)]);
+            }
 
             String unicode = text.substring(i, i + 1);
@@ -240,6 +290,5 @@
                 cursorX = x[posPtr];
                 cursorY = y[posPtr++];
-            }
-            else if (dx != null && posPtr < dx.length)
+            } else if (dx != null && posPtr < dx.length)
             {
                 cursorX += dx[posPtr];
@@ -255,6 +304,5 @@
             cursorX = x[0];
             cursorY = y[0];
-        }
-        else if (dx != null)
+        } else if (dx != null)
         {
             cursorX += dx[0];
@@ -263,5 +311,5 @@
 
         StyleAttribute sty = new StyleAttribute();
-        
+
         String fontFamily = null;
         if (getPres(sty.setName("font-family")))
@@ -282,5 +330,5 @@
         {
             System.err.println("Could not load font");
-            java.awt.Font sysFont = new java.awt.Font(fontFamily, java.awt.Font.PLAIN, (int)fontSize);
+            java.awt.Font sysFont = new java.awt.Font(fontFamily, java.awt.Font.PLAIN, (int) fontSize);
             renderSysFont(g, sysFont);
             return;
@@ -290,5 +338,5 @@
         FontFace fontFace = font.getFontFace();
         int ascent = fontFace.getAscent();
-        float fontScale = fontSize / (float)ascent;
+        float fontScale = fontSize / (float) ascent;
 
         AffineTransform oldXform = g.getTransform();
@@ -312,6 +360,8 @@
             {
                 renderShape(g, path);
-            }
-            else glyph.render(g);
+            } else
+            {
+                glyph.render(g);
+            }
 
             if (x != null && posPtr < x.length)
@@ -319,6 +369,5 @@
                 cursorX = x[posPtr];
                 cursorY = y[posPtr++];
-            }
-            else if (dx != null && posPtr < dx.length)
+            } else if (dx != null && posPtr < dx.length)
             {
                 cursorX += dx[posPtr];
@@ -342,5 +391,5 @@
         renderShape(g, textShape);
         Rectangle2D rect = font.getStringBounds(text, frc);
-        cursorX += (float)rect.getWidth();
+        cursorX += (float) rect.getWidth();
     }
 
@@ -358,6 +407,7 @@
 
     /**
-     * Updates all attributes in this diagram associated with a time event.
-     * Ie, all attributes with track information.
+     * Updates all attributes in this diagram associated with a time event. Ie,
+     * all attributes with track information.
+     *
      * @return - true if this node has changed state as a result of the time
      * update
