Patch against rev 98 of https://svn.java.net/svn/svgsalamander~svn/trunk. It strips some classes, that aren't needed (basically animation and gui) and removes dependencies. The only purpose of this patch is to reduce binary download size for JOSM users.
Index: core/src/com/kitfox/svg/animation/Animate.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/Animate.java	2012-06-17 23:56:57.664545050 +0200
@@ -0,0 +1,419 @@
+/*
+ * Animate.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on August 15, 2004, 2:51 AM
+ */
+
+package com.kitfox.svg.animation;
+
+import com.kitfox.svg.SVGElement;
+import com.kitfox.svg.SVGException;
+import com.kitfox.svg.SVGLoaderHelper;
+import com.kitfox.svg.animation.parser.AnimTimeParser;
+import com.kitfox.svg.xml.ColorTable;
+import com.kitfox.svg.xml.StyleAttribute;
+import com.kitfox.svg.xml.XMLParseUtil;
+import java.awt.Color;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.PathIterator;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+
+/**
+ * Animate is a really annoying morphic tag that could represent a real value,
+ * a color or a path
+ *
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+public class Animate extends AnimateBase implements AnimateColorIface
+{
+//    StyleAttribute retAttrib = new StyleAttribute
+    public static final int DT_REAL = 0;
+    public static final int DT_COLOR = 1;
+    public static final int DT_PATH = 2;
+    int dataType = DT_REAL;
+    
+    protected double fromValue = Double.NaN;
+    protected double toValue = Double.NaN;
+    protected double byValue = Double.NaN;
+    protected double[] valuesValue;
+    
+    protected Color fromColor = null;
+    protected Color toColor = null;
+
+    protected GeneralPath fromPath = null;
+    protected GeneralPath toPath = null;
+
+    /** Creates a new instance of Animate */
+    public Animate()
+    {
+    }
+    
+    public int getDataType()
+    {
+        return dataType;
+    }
+    
+    public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) throws SAXException
+    {
+		//Load style string
+        super.loaderStartElement(helper, attrs, parent);
+
+        String strn = attrs.getValue("from");
+        if (strn != null)
+        {
+            if (XMLParseUtil.isDouble(strn))
+            {
+                fromValue = XMLParseUtil.parseDouble(strn); 
+            } 
+//            else if (attrs.getValue("attributeName").equals("d"))
+//            {
+//                fromPath = this.buildPath(strn, GeneralPath.WIND_EVEN_ODD);
+//                dataType = DT_PATH;
+//            }
+            else
+            {
+                fromColor = ColorTable.parseColor(strn); 
+                if (fromColor == null)
+                {
+                    //Try path
+                    fromPath = this.buildPath(strn, GeneralPath.WIND_EVEN_ODD);
+                    dataType = DT_PATH;
+                }
+                else dataType = DT_COLOR;
+            }
+        }
+
+        strn = attrs.getValue("to");
+        if (strn != null)
+        {
+            if (XMLParseUtil.isDouble(strn))
+            {
+                toValue = XMLParseUtil.parseDouble(strn); 
+            } 
+            else
+            {
+                toColor = ColorTable.parseColor(strn); 
+                if (toColor == null)
+                {
+                    //Try path
+                    toPath = this.buildPath(strn, GeneralPath.WIND_EVEN_ODD);
+                    dataType = DT_PATH;
+                }
+                else dataType = DT_COLOR;
+            }
+        }
+
+        strn = attrs.getValue("by");
+        try 
+        {
+            if (strn != null) byValue = XMLParseUtil.parseDouble(strn); 
+        } catch (Exception e) {}
+
+        strn = attrs.getValue("values");
+        try 
+        {
+            if (strn != null) valuesValue = XMLParseUtil.parseDoubleList(strn); 
+        } catch (Exception e) {}
+    }
+    
+    /**
+     * Evaluates this animation element for the passed interpolation time.  Interp
+     * must be on [0..1].
+     */
+    public double eval(double interp)
+    {
+        boolean fromExists = !Double.isNaN(fromValue);
+        boolean toExists = !Double.isNaN(toValue);
+        boolean byExists = !Double.isNaN(byValue);
+        boolean valuesExists = valuesValue != null;
+        
+        if (valuesExists)
+        {
+            double sp = interp * valuesValue.length;
+            int ip = (int)sp;
+            double fp = sp - ip;
+            
+            int i0 = ip;
+            int i1 = ip + 1;
+            
+            if (i0 < 0) return valuesValue[0];
+            if (i1 >= valuesValue.length) return valuesValue[valuesValue.length - 1];
+            return valuesValue[i0] * (1 - fp) + valuesValue[i1] * fp;
+        }
+        else if (fromExists && toExists)
+        {
+            return toValue * interp + fromValue * (1.0 - interp);
+        }
+        else if (fromExists && byExists)
+        {
+            return fromValue + byValue * interp;
+        }
+        else if (toExists && byExists)
+        {
+            return toValue - byValue * (1.0 - interp);
+        }
+        else if (byExists)
+        {
+            return byValue * interp;
+        }
+  
+        //Should not reach this line
+        throw new RuntimeException("Animate tag could not be evalutated - insufficient arguements");
+    }
+
+    public Color evalColor(double interp)
+    {
+        if (fromColor == null && toColor != null)
+        {
+            float[] toCol = new float[3];
+            toColor.getColorComponents(toCol);
+            return new Color(toCol[0] * (float)interp, 
+                toCol[1] * (float)interp, 
+                toCol[2] * (float)interp);
+        }
+        else if (fromColor != null && toColor != null)
+        {
+            float nInterp = 1 - (float)interp;
+            
+            float[] fromCol = new float[3];
+            float[] toCol = new float[3];
+            fromColor.getColorComponents(fromCol);
+            toColor.getColorComponents(toCol);
+            return new Color(fromCol[0] * nInterp + toCol[0] * (float)interp, 
+                fromCol[1] * nInterp + toCol[1] * (float)interp, 
+                fromCol[2] * nInterp + toCol[2] * (float)interp);
+        }
+        
+        throw new RuntimeException("Animate tag could not be evalutated - insufficient arguements");
+    }
+
+    public GeneralPath evalPath(double interp)
+    {
+        if (fromPath == null && toPath != null)
+        {
+            PathIterator itTo = toPath.getPathIterator(new AffineTransform());
+            
+            GeneralPath midPath = new GeneralPath();
+            float[] coordsTo = new float[6];
+            
+            for (; !itTo.isDone(); itTo.next())
+            {
+                int segTo = itTo.currentSegment(coordsTo);
+                
+                switch (segTo)
+                {
+                    case PathIterator.SEG_CLOSE:
+                        midPath.closePath();
+                        break;
+                    case PathIterator.SEG_CUBICTO:
+                        midPath.curveTo(
+                                (float)(coordsTo[0] * interp), 
+                                (float)(coordsTo[1] * interp), 
+                                (float)(coordsTo[2] * interp), 
+                                (float)(coordsTo[3] * interp), 
+                                (float)(coordsTo[4] * interp), 
+                                (float)(coordsTo[5] * interp)
+                                );
+                        break;
+                    case PathIterator.SEG_LINETO:
+                        midPath.lineTo(
+                                (float)(coordsTo[0] * interp), 
+                                (float)(coordsTo[1] * interp)
+                                );
+                        break;
+                    case PathIterator.SEG_MOVETO:
+                        midPath.moveTo(
+                                (float)(coordsTo[0] * interp), 
+                                (float)(coordsTo[1] * interp)
+                                );
+                        break;
+                    case PathIterator.SEG_QUADTO:
+                        midPath.quadTo(
+                                (float)(coordsTo[0] * interp), 
+                                (float)(coordsTo[1] * interp), 
+                                (float)(coordsTo[2] * interp), 
+                                (float)(coordsTo[3] * interp)
+                                );
+                        break;
+                }
+            }
+            
+            return midPath;
+        }
+        else if (toPath != null)
+        {
+            PathIterator itFrom = fromPath.getPathIterator(new AffineTransform());
+            PathIterator itTo = toPath.getPathIterator(new AffineTransform());
+            
+            GeneralPath midPath = new GeneralPath();
+            float[] coordsFrom = new float[6];
+            float[] coordsTo = new float[6];
+            
+            for (; !itFrom.isDone(); itFrom.next())
+            {
+                int segFrom = itFrom.currentSegment(coordsFrom);
+                int segTo = itTo.currentSegment(coordsTo);
+                
+                if (segFrom != segTo)
+                {
+                    throw new RuntimeException("Path shape mismatch");
+                }
+                
+                switch (segFrom)
+                {
+                    case PathIterator.SEG_CLOSE:
+                        midPath.closePath();
+                        break;
+                    case PathIterator.SEG_CUBICTO:
+                        midPath.curveTo(
+                                (float)(coordsFrom[0] * (1 - interp) + coordsTo[0] * interp), 
+                                (float)(coordsFrom[1] * (1 - interp) + coordsTo[1] * interp), 
+                                (float)(coordsFrom[2] * (1 - interp) + coordsTo[2] * interp), 
+                                (float)(coordsFrom[3] * (1 - interp) + coordsTo[3] * interp), 
+                                (float)(coordsFrom[4] * (1 - interp) + coordsTo[4] * interp), 
+                                (float)(coordsFrom[5] * (1 - interp) + coordsTo[5] * interp)
+                                );
+                        break;
+                    case PathIterator.SEG_LINETO:
+                        midPath.lineTo(
+                                (float)(coordsFrom[0] * (1 - interp) + coordsTo[0] * interp), 
+                                (float)(coordsFrom[1] * (1 - interp) + coordsTo[1] * interp)
+                                );
+                        break;
+                    case PathIterator.SEG_MOVETO:
+                        midPath.moveTo(
+                                (float)(coordsFrom[0] * (1 - interp) + coordsTo[0] * interp), 
+                                (float)(coordsFrom[1] * (1 - interp) + coordsTo[1] * interp)
+                                );
+                        break;
+                    case PathIterator.SEG_QUADTO:
+                        midPath.quadTo(
+                                (float)(coordsFrom[0] * (1 - interp) + coordsTo[0] * interp), 
+                                (float)(coordsFrom[1] * (1 - interp) + coordsTo[1] * interp), 
+                                (float)(coordsFrom[2] * (1 - interp) + coordsTo[2] * interp), 
+                                (float)(coordsFrom[3] * (1 - interp) + coordsTo[3] * interp)
+                                );
+                        break;
+                }
+            }
+            
+            return midPath;
+        }
+        
+        throw new RuntimeException("Animate tag could not be evalutated - insufficient arguements");
+    }
+    
+    /**
+     * If this element is being accumulated, detemine the delta to accumulate by
+     */
+    public double repeatSkipSize(int reps)
+    {
+        boolean fromExists = !Double.isNaN(fromValue);
+        boolean toExists = !Double.isNaN(toValue);
+        boolean byExists = !Double.isNaN(byValue);
+        
+        if (fromExists && toExists)
+        {
+            return (toValue - fromValue) * reps;
+        }
+        else if (fromExists && byExists)
+        {
+            return (fromValue + byValue) * reps;
+        }
+        else if (toExists && byExists)
+        {
+            return toValue * reps;
+        }
+        else if (byExists)
+        {
+            return byValue * reps;
+        }
+
+        //Should not reach this line
+        return 0;
+    }
+
+    protected void rebuild(AnimTimeParser animTimeParser) throws SVGException
+    {
+        super.rebuild(animTimeParser);
+
+        StyleAttribute sty = new StyleAttribute();
+
+        if (getPres(sty.setName("from")))
+        {
+            String strn = sty.getStringValue();
+            if (XMLParseUtil.isDouble(strn))
+            {
+                fromValue = XMLParseUtil.parseDouble(strn);
+            }
+            else
+            {
+                fromColor = ColorTable.parseColor(strn);
+                if (fromColor == null)
+                {
+                    //Try path
+                    fromPath = this.buildPath(strn, GeneralPath.WIND_EVEN_ODD);
+                    dataType = DT_PATH;
+                }
+                else dataType = DT_COLOR;
+            }
+        }
+
+        if (getPres(sty.setName("to")))
+        {
+            String strn = sty.getStringValue();
+            if (XMLParseUtil.isDouble(strn))
+            {
+                toValue = XMLParseUtil.parseDouble(strn);
+            }
+            else
+            {
+                toColor = ColorTable.parseColor(strn);
+                if (toColor == null)
+                {
+                    //Try path
+                    toPath = this.buildPath(strn, GeneralPath.WIND_EVEN_ODD);
+                    dataType = DT_PATH;
+                }
+                else dataType = DT_COLOR;
+            }
+        }
+
+        if (getPres(sty.setName("by")))
+        {
+            String strn = sty.getStringValue();
+            if (strn != null) byValue = XMLParseUtil.parseDouble(strn);
+        }
+
+        if (getPres(sty.setName("values")))
+        {
+            String strn = sty.getStringValue();
+            if (strn != null) valuesValue = XMLParseUtil.parseDoubleList(strn);
+        }
+    }
+    
+}
Index: core/src/com/kitfox/svg/animation/AnimateBase.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/AnimateBase.java	2012-06-17 23:56:57.664545050 +0200
@@ -0,0 +1,133 @@
+/*
+ * Animate.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on August 15, 2004, 2:51 AM
+ */
+
+package com.kitfox.svg.animation;
+
+import com.kitfox.svg.SVGElement;
+import com.kitfox.svg.SVGException;
+import com.kitfox.svg.SVGLoaderHelper;
+import com.kitfox.svg.animation.parser.AnimTimeParser;
+import com.kitfox.svg.animation.parser.ParseException;
+import com.kitfox.svg.xml.StyleAttribute;
+import java.io.StringReader;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+abstract public class AnimateBase extends AnimationElement
+{
+    protected double repeatCount = Double.NaN;
+    protected TimeBase repeatDur;
+    
+    /** Creates a new instance of Animate */
+    public AnimateBase()
+    {
+    }
+    
+    public void evalParametric(AnimationTimeEval state, double curTime)
+    {
+        evalParametric(state, curTime, repeatCount, repeatDur == null ? Double.NaN : repeatDur.evalTime());
+    }
+    
+    public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) throws SAXException
+    {
+		//Load style string
+        super.loaderStartElement(helper, attrs, parent);
+
+        String repeatDurTime = attrs.getValue("repeatDur");
+
+        try
+        {
+            if (repeatDurTime != null)
+            {
+                helper.animTimeParser.ReInit(new StringReader(repeatDurTime));
+                this.repeatDur = helper.animTimeParser.Expr();
+                this.repeatDur.setParentElement(this);
+            }
+        }
+        catch (Exception e)
+        {
+            throw new SAXException(e);
+        }
+        
+        String strn = attrs.getValue("repeatCount");
+        if (strn == null)
+        {
+            repeatCount = 1;
+        }
+        else if ("indefinite".equals(strn))
+        {
+            repeatCount = Double.POSITIVE_INFINITY;
+        }
+        else
+        {
+            try { repeatCount = Double.parseDouble(strn); } 
+            catch (Exception e) { repeatCount = Double.NaN; }
+        }
+    }
+
+    protected void rebuild(AnimTimeParser animTimeParser) throws SVGException
+    {
+        super.rebuild(animTimeParser);
+
+        StyleAttribute sty = new StyleAttribute();
+
+        if (getPres(sty.setName("repeatDur")))
+        {
+            String strn = sty.getStringValue();
+            if (strn != null)
+            {
+                animTimeParser.ReInit(new StringReader(strn));
+                try {
+                    this.repeatDur = animTimeParser.Expr();
+                } catch (ParseException ex) {
+                    ex.printStackTrace();
+                }
+            }
+        }
+
+        if (getPres(sty.setName("repeatCount")))
+        {
+            String strn = sty.getStringValue();
+            if (strn == null)
+            {
+                repeatCount = 1;
+            }
+            else if ("indefinite".equals(strn))
+            {
+                repeatCount = Double.POSITIVE_INFINITY;
+            }
+            else
+            {
+                try { repeatCount = Double.parseDouble(strn); }
+                catch (Exception e) { repeatCount = Double.NaN; }
+            }
+        }
+    }
+}
Index: core/src/com/kitfox/svg/animation/AnimateColor.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/AnimateColor.java	2012-06-17 23:56:57.672545050 +0200
@@ -0,0 +1,105 @@
+/*
+ * Animate.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on August 15, 2004, 2:51 AM
+ */
+
+package com.kitfox.svg.animation;
+
+import com.kitfox.svg.SVGElement;
+import com.kitfox.svg.SVGException;
+import com.kitfox.svg.SVGLoaderHelper;
+import com.kitfox.svg.animation.parser.AnimTimeParser;
+import com.kitfox.svg.xml.ColorTable;
+import com.kitfox.svg.xml.StyleAttribute;
+import java.awt.Color;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+
+/**
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+public class AnimateColor extends AnimateBase implements AnimateColorIface
+{
+    
+    protected Color fromValue;
+    protected Color toValue;
+    
+    /** Creates a new instance of Animate */
+    public AnimateColor()
+    {
+    }
+    
+    public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) throws SAXException
+    {
+		//Load style string
+        super.loaderStartElement(helper, attrs, parent);
+
+        String strn = attrs.getValue("from");
+        fromValue = ColorTable.parseColor(strn);
+
+        strn = attrs.getValue("to");
+        toValue = ColorTable.parseColor(strn);
+    }
+
+    
+    /**
+     * Evaluates this animation element for the passed interpolation time.  Interp
+     * must be on [0..1].
+     */
+    public Color evalColor(double interp)
+    {
+        int r1 = fromValue.getRed();
+        int g1 = fromValue.getGreen();
+        int b1 = fromValue.getBlue();
+        int r2 = toValue.getRed();
+        int g2 = toValue.getGreen();
+        int b2 = toValue.getBlue();
+        double invInterp = 1.0 - interp;
+        
+        return new Color((int)(r1 * invInterp + r2 * interp), 
+            (int)(g1 * invInterp + g2 * interp), 
+            (int)(b1 * invInterp + b2 * interp));
+    }
+
+    protected void rebuild(AnimTimeParser animTimeParser) throws SVGException
+    {
+        super.rebuild(animTimeParser);
+
+        StyleAttribute sty = new StyleAttribute();
+
+        if (getPres(sty.setName("from")))
+        {
+            String strn = sty.getStringValue();
+            fromValue = ColorTable.parseColor(strn);
+        }
+
+        if (getPres(sty.setName("to")))
+        {
+            String strn = sty.getStringValue();
+            toValue = ColorTable.parseColor(strn);
+        }
+    }
+}
Index: core/src/com/kitfox/svg/animation/AnimateColorIface.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/AnimateColorIface.java	2012-06-17 23:56:57.672545050 +0200
@@ -0,0 +1,38 @@
+/*
+ * AnimateColorIface.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on January 16, 2005, 6:24 AM
+ */
+
+package com.kitfox.svg.animation;
+
+import java.awt.*;
+
+/**
+ *
+ * @author kitfox
+ */
+public interface AnimateColorIface
+{
+    public Color evalColor(double interp);
+}
Index: core/src/com/kitfox/svg/animation/AnimateMotion.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/AnimateMotion.java	2012-06-17 23:56:57.672545050 +0200
@@ -0,0 +1,282 @@
+/*
+ * Animate.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on August 15, 2004, 2:51 AM
+ */
+
+package com.kitfox.svg.animation;
+
+import com.kitfox.svg.SVGElement;
+import com.kitfox.svg.SVGException;
+import com.kitfox.svg.SVGLoaderHelper;
+import com.kitfox.svg.animation.parser.AnimTimeParser;
+import com.kitfox.svg.xml.StyleAttribute;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.GeneralPath;
+import java.awt.geom.PathIterator;
+import java.awt.geom.Point2D;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+
+/**
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+public class AnimateMotion extends AnimateXform
+{
+    static final Matcher matchPoint = Pattern.compile("\\s*(\\d+)[^\\d]+(\\d+)\\s*").matcher("");
+    
+//    protected double fromValue;
+//    protected double toValue;
+    GeneralPath path;
+    int rotateType = RT_ANGLE;
+    double rotate;  //Static angle to rotate by
+    
+    public static final int RT_ANGLE = 0;  //Rotate by constant 'rotate' degrees
+    public static final int RT_AUTO = 1;  //Rotate to reflect tangent of position on path
+    
+    final ArrayList bezierSegs = new ArrayList();
+    double curveLength;
+    
+    /** Creates a new instance of Animate */
+    public AnimateMotion()
+    {
+    }
+    
+    public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) throws SAXException
+    {
+		//Load style string
+        super.loaderStartElement(helper, attrs, parent);
+        
+        //Motion element implies animating the transform element
+        if (attribName == null) 
+        {
+            attribName = "transform";
+            attribType = AT_AUTO;
+            additiveType = AD_SUM;
+        }
+        
+
+        String path = attrs.getValue("path");
+        if (path != null)
+        {
+            this.path = buildPath(path, GeneralPath.WIND_NON_ZERO);
+        }
+        
+        //Now parse rotation style
+        String rotate = attrs.getValue("rotate");
+        if (rotate != null)
+        {
+            if (rotate.equals("auto"))
+            {
+                this.rotateType = RT_AUTO;
+            }
+            else
+            {
+                try { this.rotate = Math.toRadians(Float.parseFloat(rotate)); } catch (Exception e) {}
+            }
+        }
+
+        //Determine path
+        String from = attrs.getValue("from");
+        String to = attrs.getValue("to");
+
+        buildPath(from, to);
+    }
+    
+    protected static void setPoint(Point2D.Float pt, String x, String y)
+    {
+        try { pt.x = Float.parseFloat(x); } catch (Exception e) {};
+        
+        try { pt.y = Float.parseFloat(y); } catch (Exception e) {};
+    }
+
+    private void buildPath(String from, String to)
+    {
+        if (from != null && to != null)
+        {
+            Point2D.Float ptFrom = new Point2D.Float(), ptTo = new Point2D.Float();
+
+            matchPoint.reset(from);
+            if (matchPoint.matches())
+            {
+                setPoint(ptFrom, matchPoint.group(1), matchPoint.group(2));
+            }
+
+            matchPoint.reset(to);
+            if (matchPoint.matches())
+            {
+                setPoint(ptFrom, matchPoint.group(1), matchPoint.group(2));
+            }
+
+            if (ptFrom != null && ptTo != null)
+            {
+                path = new GeneralPath();
+                path.moveTo(ptFrom.x, ptFrom.y);
+                path.lineTo(ptTo.x, ptTo.y);
+            }
+        }
+
+        paramaterizePath();
+    }
+    
+    private void paramaterizePath()
+    {
+        bezierSegs.clear();
+        curveLength = 0;
+        
+        double[] coords = new double[6];
+        double sx = 0, sy = 0;
+        
+        for (PathIterator pathIt = path.getPathIterator(new AffineTransform()); !pathIt.isDone(); pathIt.next())
+        {
+            Bezier bezier = null;
+                    
+            int segType = pathIt.currentSegment(coords);
+            
+            switch (segType)
+            {
+                case PathIterator.SEG_LINETO: 
+                {
+                    bezier = new Bezier(sx, sy, coords, 1);
+                    sx = coords[0];
+                    sy = coords[1];
+                    break;
+                }
+                case PathIterator.SEG_QUADTO:
+                {
+                    bezier = new Bezier(sx, sy, coords, 2);
+                    sx = coords[2];
+                    sy = coords[3];
+                    break;
+                }
+                case PathIterator.SEG_CUBICTO:
+                {
+                    bezier = new Bezier(sx, sy, coords, 3);
+                    sx = coords[4];
+                    sy = coords[5];
+                    break;
+                }
+                case PathIterator.SEG_MOVETO:
+                {
+                    sx = coords[0];
+                    sy = coords[1];
+                    break;
+                }
+                case PathIterator.SEG_CLOSE:
+                    //Do nothing
+                    break;
+                
+            }
+
+            if (bezier != null)
+            {
+                bezierSegs.add(bezier);
+                curveLength += bezier.getLength();
+            }
+        }
+    }
+    
+    /**
+     * Evaluates this animation element for the passed interpolation time.  Interp
+     * must be on [0..1].
+     */
+    public AffineTransform eval(AffineTransform xform, double interp)
+    {
+        Point2D.Double point = new Point2D.Double();
+        
+        if (interp >= 1)
+        {
+            Bezier last = (Bezier)bezierSegs.get(bezierSegs.size() - 1);
+            last.getFinalPoint(point);
+            xform.setToTranslation(point.x, point.y);
+            return xform;
+        }
+        
+        double curLength = curveLength * interp;
+        for (Iterator it = bezierSegs.iterator(); it.hasNext();)
+        {
+            Bezier bez = (Bezier)it.next();
+            
+            double bezLength = bez.getLength();
+            if (curLength < bezLength)
+            {
+                double param = curLength / bezLength;
+                bez.eval(param, point);
+                break;
+            }
+            
+            curLength -= bezLength;
+        }
+        
+        xform.setToTranslation(point.x, point.y);
+        
+        return xform;
+    }
+    
+
+    protected void rebuild(AnimTimeParser animTimeParser) throws SVGException
+    {
+        super.rebuild(animTimeParser);
+
+        StyleAttribute sty = new StyleAttribute();
+
+        if (getPres(sty.setName("path")))
+        {
+            String strn = sty.getStringValue();
+            this.path = buildPath(strn, GeneralPath.WIND_NON_ZERO);
+        }
+
+        if (getPres(sty.setName("rotate")))
+        {
+            String strn = sty.getStringValue();
+            if (strn.equals("auto"))
+            {
+                this.rotateType = RT_AUTO;
+            }
+            else
+            {
+                try { this.rotate = Math.toRadians(Float.parseFloat(strn)); } catch (Exception e) {}
+            }
+        }
+
+        String from = null;
+        if (getPres(sty.setName("from")))
+        {
+            from = sty.getStringValue();
+        }
+
+        String to = null;
+        if (getPres(sty.setName("to")))
+        {
+            to = sty.getStringValue();
+        }
+        
+        buildPath(from, to);
+    }
+}
Index: core/src/com/kitfox/svg/animation/AnimateTransform.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/AnimateTransform.java	2012-06-17 23:56:57.676545050 +0200
@@ -0,0 +1,302 @@
+/*
+ * Animate.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on August 15, 2004, 2:51 AM
+ */
+
+package com.kitfox.svg.animation;
+
+import com.kitfox.svg.SVGElement;
+import com.kitfox.svg.SVGException;
+import com.kitfox.svg.SVGLoaderHelper;
+import com.kitfox.svg.animation.parser.AnimTimeParser;
+import com.kitfox.svg.xml.StyleAttribute;
+import com.kitfox.svg.xml.XMLParseUtil;
+import java.awt.geom.AffineTransform;
+import java.util.regex.Pattern;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+
+/**
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+public class AnimateTransform extends AnimateXform
+{
+//    protected AffineTransform fromValue;
+//    protected AffineTransform toValue;
+//    protected double[] fromValue;  //Transform parameters
+//    protected double[] toValue;
+    protected double[][] values;
+    protected double[] keyTimes;
+
+    public static final int AT_REPLACE = 0;
+    public static final int AT_SUM = 1;
+
+    protected int additive = AT_REPLACE;
+
+    public static final int TR_TRANSLATE = 0;
+    public static final int TR_ROTATE = 1;
+    public static final int TR_SCALE = 2;
+    public static final int TR_SKEWY = 3;
+    public static final int TR_SKEWX = 4;
+    public static final int TR_INVALID = 5;
+
+    protected int xformType = TR_INVALID;
+
+    /** Creates a new instance of Animate */
+    public AnimateTransform()
+    {
+    }
+
+    public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) throws SAXException
+    {
+		//Load style string
+        super.loaderStartElement(helper, attrs, parent);
+
+        //Type of matrix of transform.  Should be one of the known names used to
+        // define matrix transforms
+        // valid types: translate, scale, rotate, skewX, skewY
+        // 'matrix' not valid for animation
+        String type = attrs.getValue("type").toLowerCase();
+        if (type.equals("translate")) xformType = TR_TRANSLATE;
+        if (type.equals("rotate")) xformType = TR_ROTATE;
+        if (type.equals("scale")) xformType = TR_SCALE;
+        if (type.equals("skewx")) xformType = TR_SKEWX;
+        if (type.equals("skewy")) xformType = TR_SKEWY;
+
+        String fromStrn = attrs.getValue("from");
+        String toStrn = attrs.getValue("to");
+        if (fromStrn != null && toStrn != null)
+        {
+            //fromValue = parseSingleTransform(type + "(" + strn + ")");
+            double[] fromValue = XMLParseUtil.parseDoubleList(fromStrn);
+            fromValue = validate(fromValue);
+
+    //        toValue = parseSingleTransform(type + "(" + strn + ")");
+            double[] toValue = XMLParseUtil.parseDoubleList(toStrn);
+            toValue = validate(toValue);
+            
+            values = new double[][]{fromValue, toValue};
+            keyTimes = new double[]{0, 1};
+        }
+
+        String keyTimeStrn = attrs.getValue("keyTimes");
+        String valuesStrn = attrs.getValue("values");
+        if (keyTimeStrn != null && valuesStrn != null)
+        {
+            keyTimes = XMLParseUtil.parseDoubleList(keyTimeStrn);
+            
+            String[] valueList = Pattern.compile(";").split(valuesStrn);
+            values = new double[valueList.length][];
+            for (int i = 0; i < valueList.length; i++)
+            {
+                double[] list = XMLParseUtil.parseDoubleList(valueList[i]);
+                values[i] = validate(list);
+            }
+        }
+        
+        //Check our additive state
+        String additive = attrs.getValue("additive");
+        if (additive != null)
+        {
+            if (additive.equals("sum")) this.additive = AT_SUM;
+        }
+    }
+
+    /**
+     * Check list size against current xform type and ensure list
+     * is expanded to a standard list size
+     */
+    private double[] validate(double[] paramList)
+    {
+        switch (xformType)
+        {
+            case TR_SCALE:
+            {
+                if (paramList == null)
+                {
+                    paramList = new double[]{1, 1};
+                }
+                else if (paramList.length == 1)
+                {
+                    paramList = new double[]{paramList[0], paramList[0]};
+                    
+//                    double[] tmp = paramList;
+//                    paramList = new double[2];
+//                    paramList[0] = paramList[1] = tmp[0];
+                }
+            }
+        }
+
+        return paramList;
+    }
+
+    /**
+     * Evaluates this animation element for the passed interpolation time.  Interp
+     * must be on [0..1].
+     */
+    public AffineTransform eval(AffineTransform xform, double interp)
+    {
+        int idx = 0;
+        for (; idx < keyTimes.length - 1; idx++)
+        {
+            if (interp >= keyTimes[idx])
+            {
+                idx--;
+                if (idx < 0) idx = 0;
+                break;
+            }
+        }
+        
+        double spanStartTime = keyTimes[idx];
+        double spanEndTime = keyTimes[idx + 1];
+//        double span = spanStartTime - spanEndTime;
+        
+        interp = (interp - spanStartTime) / (spanEndTime - spanStartTime);
+        double[] fromValue = values[idx];
+        double[] toValue = values[idx + 1];
+        
+        switch (xformType)
+        {
+            case TR_TRANSLATE:
+            {
+                double x = (1.0 - interp) * fromValue[0] + interp * toValue[0];
+                double y = (1.0 - interp) * fromValue[1] + interp * toValue[1];
+                xform.setToTranslation(x, y);
+                break;
+            }
+            case TR_ROTATE:
+            {
+                double x1 = fromValue.length == 3 ? fromValue[1] : 0;
+                double y1 = fromValue.length == 3 ? fromValue[2] : 0;
+                double x2 = toValue.length == 3 ? toValue[1] : 0;
+                double y2 = toValue.length == 3 ? toValue[2] : 0;
+                
+                double theta = (1.0 - interp) * fromValue[0] + interp * toValue[0];
+                double x = (1.0 - interp) * x1 + interp * x2;
+                double y = (1.0 - interp) * y1 + interp * y2;
+                xform.setToRotation(Math.toRadians(theta), x, y);
+                break;
+            }
+            case TR_SCALE:
+            {
+                double x = (1.0 - interp) * fromValue[0] + interp * toValue[0];
+                double y = (1.0 - interp) * fromValue[1] + interp * toValue[1];
+                xform.setToScale(x, y);
+                break;
+            }
+            case TR_SKEWX:
+            {
+                double x = (1.0 - interp) * fromValue[0] + interp * toValue[0];
+                xform.setToShear(Math.toRadians(x), 0.0);
+                break;
+            }
+            case TR_SKEWY:
+            {
+                double y = (1.0 - interp) * fromValue[0] + interp * toValue[0];
+                xform.setToShear(0.0, Math.toRadians(y));
+                break;
+            }
+            default:
+                xform.setToIdentity();
+                break;
+        }
+
+        return xform;
+    }
+
+    protected void rebuild(AnimTimeParser animTimeParser) throws SVGException
+    {
+        super.rebuild(animTimeParser);
+
+        StyleAttribute sty = new StyleAttribute();
+
+        if (getPres(sty.setName("type")))
+        {
+            String strn = sty.getStringValue().toLowerCase();
+            if (strn.equals("translate")) xformType = TR_TRANSLATE;
+            if (strn.equals("rotate")) xformType = TR_ROTATE;
+            if (strn.equals("scale")) xformType = TR_SCALE;
+            if (strn.equals("skewx")) xformType = TR_SKEWX;
+            if (strn.equals("skewy")) xformType = TR_SKEWY;
+        }
+
+        String fromStrn = null;
+        if (getPres(sty.setName("from")))
+        {
+            fromStrn = sty.getStringValue();
+        }
+
+        String toStrn = null;
+        if (getPres(sty.setName("to")))
+        {
+            toStrn = sty.getStringValue();
+        }
+
+        if (fromStrn != null && toStrn != null)
+        {
+            double[] fromValue = XMLParseUtil.parseDoubleList(fromStrn);
+            fromValue = validate(fromValue);
+
+            double[] toValue = XMLParseUtil.parseDoubleList(toStrn);
+            toValue = validate(toValue);
+
+            values = new double[][]{fromValue, toValue};
+        }
+
+        String keyTimeStrn = null;
+        if (getPres(sty.setName("keyTimes")))
+        {
+            keyTimeStrn = sty.getStringValue();
+        }
+
+        String valuesStrn = null;
+        if (getPres(sty.setName("values")))
+        {
+            valuesStrn = sty.getStringValue();
+        }
+
+        if (keyTimeStrn != null && valuesStrn != null)
+        {
+            keyTimes = XMLParseUtil.parseDoubleList(keyTimeStrn);
+
+            String[] valueList = Pattern.compile(";").split(valuesStrn);
+            values = new double[valueList.length][];
+            for (int i = 0; i < valueList.length; i++)
+            {
+                double[] list = XMLParseUtil.parseDoubleList(valueList[i]);
+                values[i] = validate(list);
+            }
+        }
+
+        //Check our additive state
+
+        if (getPres(sty.setName("additive")))
+        {
+            String strn = sty.getStringValue().toLowerCase();
+            if (strn.equals("sum")) this.additive = AT_SUM;
+        }
+    }
+}
Index: core/src/com/kitfox/svg/animation/AnimateXform.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/AnimateXform.java	2012-06-17 23:56:57.676545050 +0200
@@ -0,0 +1,54 @@
+/*
+ * AnimateXform.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on January 14, 2005, 6:46 AM
+ */
+
+package com.kitfox.svg.animation;
+
+import com.kitfox.svg.SVGElement;
+import com.kitfox.svg.SVGLoaderHelper;
+import java.awt.geom.AffineTransform;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+
+
+/**
+ *
+ * @author kitfox
+ */
+abstract public class AnimateXform extends AnimateBase
+{
+    public AnimateXform()
+    {
+    }
+    
+    public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) throws SAXException
+    {
+        super.loaderStartElement(helper, attrs, parent);
+    }
+    
+    abstract public AffineTransform eval(AffineTransform xform, double interp);
+    
+}
Index: core/src/com/kitfox/svg/animation/AnimationElement.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/AnimationElement.java	2012-06-17 23:56:57.676545050 +0200
@@ -0,0 +1,414 @@
+/*
+ * AnimateEle.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on August 15, 2004, 2:52 AM
+ */
+
+package com.kitfox.svg.animation;
+
+import com.kitfox.svg.SVGElement;
+import com.kitfox.svg.SVGException;
+import com.kitfox.svg.SVGLoaderHelper;
+import com.kitfox.svg.animation.parser.AnimTimeParser;
+import com.kitfox.svg.animation.parser.ParseException;
+import com.kitfox.svg.xml.StyleAttribute;
+import java.io.StringReader;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+
+/**
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+public abstract class AnimationElement extends SVGElement
+{
+    protected String attribName;
+//    protected String attribType;
+    protected int attribType = AT_AUTO;
+
+    public static final int AT_CSS = 0;
+    public static final int AT_XML = 1;
+    public static final int AT_AUTO = 2;  //Check CSS first, then XML
+
+    protected TimeBase beginTime;
+    protected TimeBase durTime;
+    protected TimeBase endTime;
+    protected int fillType = FT_AUTO;
+
+    /** <a href="http://www.w3.org/TR/smil20/smil-timing.html#adef-fill">More about the <b>fill</b> attribute</a> */
+    public static final int FT_REMOVE = 0;
+    public static final int FT_FREEZE = 1;
+    public static final int FT_HOLD = 2;
+    public static final int FT_TRANSITION = 3;
+    public static final int FT_AUTO = 4;
+    public static final int FT_DEFAULT = 5;
+
+    /** Additive state of track */
+    public static final int AD_REPLACE = 0;
+    public static final int AD_SUM = 1;
+
+    int additiveType = AD_REPLACE;
+    
+    /** Accumlative state */
+    public static final int AC_REPLACE = 0;
+    public static final int AC_SUM = 1;
+
+    int accumulateType = AC_REPLACE;
+
+    /** Creates a new instance of AnimateEle */
+    public AnimationElement()
+    {
+    }
+
+    public static String animationElementToString(int attrValue)
+    {
+        switch (attrValue)
+        {
+            case AT_CSS:
+                return "CSS";
+            case AT_XML:
+                return "XML";
+            case AT_AUTO:
+                return "AUTO";
+            default:
+                throw new RuntimeException("Unknown element type");
+        }
+    }
+    
+    public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) throws SAXException
+    {
+		//Load style string
+        super.loaderStartElement(helper, attrs, parent);
+
+        attribName = attrs.getValue("attributeName");
+        String attribType = attrs.getValue("attributeType");
+        if (attribType != null)
+        {
+            attribType = attribType.toLowerCase();
+            if (attribType.equals("css")) this.attribType = AT_CSS;
+            else if (attribType.equals("xml")) this.attribType = AT_XML;
+        }
+
+        String beginTime = attrs.getValue("begin");
+        String durTime = attrs.getValue("dur");
+        String endTime = attrs.getValue("end");
+
+        try
+        {
+            if (beginTime != null)
+            {
+                helper.animTimeParser.ReInit(new StringReader(beginTime));
+                this.beginTime = helper.animTimeParser.Expr();
+                this.beginTime.setParentElement(this);
+            }
+
+            if (durTime != null)
+            {
+                helper.animTimeParser.ReInit(new StringReader(durTime));
+                this.durTime = helper.animTimeParser.Expr();
+                this.durTime.setParentElement(this);
+            }
+
+            if (endTime != null)
+            {
+                helper.animTimeParser.ReInit(new StringReader(endTime));
+                this.endTime = helper.animTimeParser.Expr();
+                this.endTime.setParentElement(this);
+            }
+        }
+        catch (Exception e)
+        {
+            throw new SAXException(e);
+        }
+        
+//        this.beginTime = TimeBase.parseTime(beginTime);
+//        this.durTime = TimeBase.parseTime(durTime);
+//        this.endTime = TimeBase.parseTime(endTime);
+
+        String fill = attrs.getValue("fill");
+
+        if (fill != null)
+        {
+            if (fill.equals("remove")) this.fillType = FT_REMOVE;
+            if (fill.equals("freeze")) this.fillType = FT_FREEZE;
+            if (fill.equals("hold")) this.fillType = FT_HOLD;
+            if (fill.equals("transiton")) this.fillType = FT_TRANSITION;
+            if (fill.equals("auto")) this.fillType = FT_AUTO;
+            if (fill.equals("default")) this.fillType = FT_DEFAULT;
+        }
+        
+        String additiveStrn = attrs.getValue("additive");
+        
+        if (additiveStrn != null)
+        {
+            if (additiveStrn.equals("replace")) this.additiveType = AD_REPLACE;
+            if (additiveStrn.equals("sum")) this.additiveType = AD_SUM;
+        }
+        
+        String accumulateStrn = attrs.getValue("accumulate");
+        
+        if (accumulateStrn != null)
+        {
+            if (accumulateStrn.equals("replace")) this.accumulateType = AC_REPLACE;
+            if (accumulateStrn.equals("sum")) this.accumulateType = AC_SUM;
+        }
+    }
+
+    public String getAttribName() { return attribName; }
+    public int getAttribType() { return attribType; }
+    public int getAdditiveType() { return additiveType; }
+    public int getAccumulateType() { return accumulateType; }
+
+    public void evalParametric(AnimationTimeEval state, double curTime)
+    {
+        evalParametric(state, curTime, Double.NaN, Double.NaN);
+    }
+
+    /**
+     * Compares current time to start and end times and determines what degree
+     * of time interpolation this track currently represents.  Returns
+     * Float.NaN if this track cannot be evaluated at the passed time (ie,
+     * it is before or past the end of the track, or it depends upon
+     * an unknown event)
+     * @param state - A structure that will be filled with information
+     * regarding the applicability of this animatoin element at the passed
+     * time.
+     * @param curTime - Current time in seconds
+     * @param repeatCount - Optional number of repetitions of length 'dur' to
+     * do.  Set to Double.NaN to not consider this in the calculation.
+     * @param repeatDur - Optional amoun tof time to repeat the animaiton.
+     * Set to Double.NaN to not consider this in the calculation.
+     */
+    protected void evalParametric(AnimationTimeEval state, double curTime, double repeatCount, double repeatDur)
+    {
+        double begin = (beginTime == null) ? 0 : beginTime.evalTime();
+        if (Double.isNaN(begin) || begin > curTime)
+        {
+            state.set(Double.NaN, 0);
+            return;
+        }
+
+        double dur = (durTime == null) ? Double.NaN : durTime.evalTime();
+        if (Double.isNaN(dur))
+        {
+            state.set(Double.NaN, 0);
+            return;
+        }
+
+        //Determine end point of this animation
+        double end = (endTime == null) ? Double.NaN : endTime.evalTime();
+        double repeat;
+//        if (Double.isNaN(repeatDur))
+//        {
+//            repeatDur = dur;
+//        }
+        if (Double.isNaN(repeatCount) && Double.isNaN(repeatDur))
+        {
+            repeat = Double.NaN;
+        }
+        else
+        {
+            repeat = Math.min(
+                Double.isNaN(repeatCount) ? Double.POSITIVE_INFINITY : dur * repeatCount,
+                Double.isNaN(repeatDur) ? Double.POSITIVE_INFINITY : repeatDur);
+        }
+        if (Double.isNaN(repeat) && Double.isNaN(end))
+        {
+            //If neither and end point nor a repeat is specified, end point is 
+            // implied by duration.
+            end = begin + dur;
+        }
+
+        double finishTime;
+        if (Double.isNaN(end))
+        {
+            finishTime = begin + repeat;
+        }
+        else if (Double.isNaN(repeat))
+        {
+            finishTime = end;
+        }
+        else
+        {
+            finishTime = Math.min(end, repeat);
+        }
+        
+        double evalTime = Math.min(curTime, finishTime);
+//        if (curTime > finishTime) evalTime = finishTime;
+        
+        
+//        double evalTime = curTime;
+
+//        boolean pastEnd = curTime > evalTime;
+        
+//        if (!Double.isNaN(end) && curTime > end) { pastEnd = true; evalTime = Math.min(evalTime, end); }
+//        if (!Double.isNaN(repeat) && curTime > repeat) { pastEnd = true; evalTime = Math.min(evalTime, repeat); }
+        
+        double ratio = (evalTime - begin) / dur;
+        int rep = (int)ratio;
+        double interp = ratio - rep;
+        
+        //Adjust for roundoff
+        if (interp < 0.00001) interp = 0;
+
+//        state.set(interp, rep);
+//        if (!pastEnd)
+//        {
+//            state.set(interp, rep, false);
+//            return;
+//        }
+
+        //If we are still within the clip, return value
+        if (curTime == evalTime)
+        {
+            state.set(interp, rep);
+            return;
+        }
+        
+        //We are past end of clip.  Determine to clamp or ignore.
+        switch (fillType)
+        {
+            default:
+            case FT_REMOVE:
+            case FT_AUTO:
+            case FT_DEFAULT:
+                state.set(Double.NaN, rep);
+                return;
+            case FT_FREEZE:
+            case FT_HOLD:
+            case FT_TRANSITION:
+                state.set(interp == 0 ? 1 : interp, rep);
+                return;
+        }
+
+    }
+
+    double evalStartTime()
+    {
+        return beginTime == null ? Double.NaN : beginTime.evalTime();
+    }
+
+    double evalDurTime()
+    {
+        return durTime == null ? Double.NaN : durTime.evalTime();
+    }
+
+    /**
+     * Evaluates the ending time of this element.  Returns 0 if not specified.
+     *
+     * @see hasEndTime
+     */
+    double evalEndTime()
+    {
+        return endTime == null ? Double.NaN : endTime.evalTime();
+    }
+
+    /**
+     * Checks to see if an end time has been specified for this element.
+     */
+    boolean hasEndTime() { return endTime != null; }
+
+    /**
+     * 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
+     */
+    public boolean updateTime(double curTime)
+    {
+        //Animation elements to not change with time
+        return false;
+    }
+
+    public void rebuild() throws SVGException
+    {
+        AnimTimeParser animTimeParser = new AnimTimeParser(new StringReader(""));
+
+        rebuild(animTimeParser);
+    }
+
+    protected void rebuild(AnimTimeParser animTimeParser) throws SVGException
+    {
+        StyleAttribute sty = new StyleAttribute();
+
+        if (getPres(sty.setName("begin")))
+        {
+            String newVal = sty.getStringValue();
+            animTimeParser.ReInit(new StringReader(newVal));
+            try {
+                this.beginTime = animTimeParser.Expr();
+            } catch (ParseException ex) {
+                ex.printStackTrace();
+            }
+        }
+
+        if (getPres(sty.setName("dur")))
+        {
+            String newVal = sty.getStringValue();
+            animTimeParser.ReInit(new StringReader(newVal));
+            try {
+                this.durTime = animTimeParser.Expr();
+            } catch (ParseException ex) {
+                ex.printStackTrace();
+            }
+        }
+
+        if (getPres(sty.setName("end")))
+        {
+            String newVal = sty.getStringValue();
+            animTimeParser.ReInit(new StringReader(newVal));
+            try {
+                this.endTime = animTimeParser.Expr();
+            } catch (ParseException ex) {
+                ex.printStackTrace();
+            }
+        }
+
+        if (getPres(sty.setName("fill")))
+        {
+            String newVal = sty.getStringValue();
+            if (newVal.equals("remove")) this.fillType = FT_REMOVE;
+            if (newVal.equals("freeze")) this.fillType = FT_FREEZE;
+            if (newVal.equals("hold")) this.fillType = FT_HOLD;
+            if (newVal.equals("transiton")) this.fillType = FT_TRANSITION;
+            if (newVal.equals("auto")) this.fillType = FT_AUTO;
+            if (newVal.equals("default")) this.fillType = FT_DEFAULT;
+        }
+
+        if (getPres(sty.setName("additive")))
+        {
+            String newVal = sty.getStringValue();
+            if (newVal.equals("replace")) this.additiveType = AD_REPLACE;
+            if (newVal.equals("sum")) this.additiveType = AD_SUM;
+        }
+
+        if (getPres(sty.setName("accumulate")))
+        {
+            String newVal = sty.getStringValue();
+            if (newVal.equals("replace")) this.accumulateType = AC_REPLACE;
+            if (newVal.equals("sum")) this.accumulateType = AC_SUM;
+        }
+
+    }
+}
Index: core/src/com/kitfox/svg/animation/AnimationTimeEval.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/AnimationTimeEval.java	2012-06-17 23:56:57.676545050 +0200
@@ -0,0 +1,65 @@
+/*
+ * AnimateTimeEval.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ *
+ * Created on September 21, 2004, 1:31 PM
+ */
+
+package com.kitfox.svg.animation;
+
+/**
+ *
+ * @author  kitfox
+ */
+public class AnimationTimeEval
+{
+    /**
+     * Value on [0..1] representing the interpolation value of queried animation
+     * element, or Double.NaN if element does not provide a valid evalutaion
+     */
+    public double interp;
+    
+    /**
+     * Number of completed repetitions
+     */
+    public int rep;
+    
+    /**
+     * True if this evaluation is in a frozen state; ie, past the end of the
+     * track and held in the "freeze" state.
+     */
+//    public boolean pastEnd;
+    
+    /** Creates a new instance of AnimateTimeEval */
+    public AnimationTimeEval()
+    {
+    }
+    
+//    public void set(double interp, int rep, boolean pastEnd)
+    public void set(double interp, int rep)
+    {
+        this.interp = interp;
+        this.rep = rep;
+//        this.pastEnd = pastEnd;
+    }
+}
Index: core/src/com/kitfox/svg/animation/Bezier.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/Bezier.java	2012-06-17 23:56:57.680545050 +0200
@@ -0,0 +1,201 @@
+/*
+ * Bezier.java
+ *
+
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on January 14, 2005, 4:08 AM
+ */
+
+package com.kitfox.svg.animation;
+
+import java.awt.geom.*;
+
+/**
+ * http://mathworld.wolfram.com/BezierCurve.html
+ * @author kitfox
+ */
+public class Bezier
+{
+    double length;
+    double[] coord;
+
+    public Bezier(double sx, double sy, double[] coords, int numCoords)
+    {
+        setCoords(sx, sy, coords, numCoords);
+    }
+    
+    public void setCoords(double sx, double sy, double[] coords, int numCoords)
+    {
+        coord = new double[numCoords * 2 + 2];
+        coord[0] = sx;
+        coord[1] = sy;
+        for (int i = 0; i < numCoords; i++)
+        {
+            coord[i * 2 + 2] = coords[i * 2];
+            coord[i * 2 + 3] = coords[i * 2 + 1];
+        }
+        
+        calcLength();        
+    }
+    
+    /**
+     * Retuns aproximation of the length of the bezier
+     */
+    public double getLength()
+    {
+        return length;
+    }
+    
+    private void calcLength()
+    {
+        length = 0;
+        for (int i = 2; i < coord.length; i += 2)
+        {
+            length += lineLength(coord[i - 2], coord[i - 1], coord[i], coord[i + 1]);
+        }
+    }
+    
+    private double lineLength(double x1, double y1, double x2, double y2)
+    {
+        double dx = x2 - x1, dy = y2 - y1;
+        return Math.sqrt(dx * dx + dy * dy);
+    }
+    
+    public Point2D.Double getFinalPoint(Point2D.Double point)
+    {
+        point.x = coord[coord.length - 2];
+        point.y = coord[coord.length - 1];
+        return point;
+    }
+    
+    public Point2D.Double eval(double param, Point2D.Double point)
+    {
+        point.x = 0;
+        point.y = 0;
+        int numKnots = coord.length / 2;
+        
+        for (int i = 0; i < numKnots; i++)
+        {
+            double scale = bernstein(numKnots - 1, i, param);
+            point.x += coord[i * 2] * scale;
+            point.y += coord[i * 2 + 1] * scale;
+        }
+        
+        return point;
+    }
+    
+    /**
+     * Calculates the bernstein polynomial for evaluating parametric bezier
+     * @param numKnots - one less than number of knots in this curve hull
+     * @param knotNo - knot we are evaluating Bernstein for
+     * @param param - Parametric value we are evaluating at
+     */
+    private double bernstein(int numKnots, int knotNo, double param)
+    {
+        double iParam = 1 - param;
+        //Faster evaluation for easy cases:
+        switch (numKnots)
+        {
+            case 0:
+                return 1;
+            case 1:
+            {
+                switch (knotNo)
+                {
+                    case 0:
+                        return iParam;
+                    case 1:
+                        return param;
+                }
+                break;
+            }
+            case 2:
+            {
+                switch (knotNo)
+                {
+                    case 0:
+                        return iParam * iParam;
+                    case 1:
+                        return 2 * iParam * param;
+                    case 2:
+                        return param * param;
+                }
+                break;
+            }
+            case 3:
+            {
+                switch (knotNo)
+                {
+                    case 0:
+                        return iParam * iParam * iParam;
+                    case 1:
+                        return 3 * iParam * iParam * param;
+                    case 2:
+                        return 3 * iParam * param * param;
+                    case 3:
+                        return param * param * param;
+                }
+                break;
+            }
+        }
+        
+        //If this bezier has more than four points, calculate bernstein the hard way
+        double retVal = 1;
+        for (int i = 0; i < knotNo; i++)
+        {
+            retVal *= param;
+        }
+        for (int i = 0; i < numKnots - knotNo; i++)
+        {
+            retVal *= iParam;
+        }
+        retVal *= choose(numKnots, knotNo);
+        
+        return retVal;
+    }
+    
+    
+    
+    private int choose(int num, int denom)
+    {
+        int denom2 = num - denom;
+        if (denom < denom2)
+        {
+            int tmp = denom;
+            denom = denom2;
+            denom2 = tmp;
+        }
+        
+        int prod = 1;
+        for (int i = num; i > denom; i--)
+        {
+            prod *= num;
+        }
+        
+        for (int i = 2; i <= denom2; i++)
+        {
+            prod /= i;
+        }
+        
+        return prod;
+    }
+}
Index: core/src/com/kitfox/svg/animation/SetSmil.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/SetSmil.java	2012-06-17 23:56:57.680545050 +0200
@@ -0,0 +1,73 @@
+/*
+ * Set.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on August 15, 2004, 2:51 AM
+ */
+
+package com.kitfox.svg.animation;
+
+import com.kitfox.svg.SVGElement;
+import com.kitfox.svg.SVGException;
+import com.kitfox.svg.SVGLoaderHelper;
+import com.kitfox.svg.animation.parser.AnimTimeParser;
+import com.kitfox.svg.xml.StyleAttribute;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+
+/**
+ * Set is used to set a textual value; most likely for a style element.
+ *
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+public class SetSmil extends AnimationElement
+{
+    String toValue;
+    
+    /** Creates a new instance of Set */
+    public SetSmil()
+    {
+    }
+    
+    public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) throws SAXException
+    {
+		//Load style string
+        super.loaderStartElement(helper, attrs, parent);
+
+        toValue = attrs.getValue("to");
+    }
+
+    protected void rebuild(AnimTimeParser animTimeParser) throws SVGException
+    {
+        super.rebuild(animTimeParser);
+
+        StyleAttribute sty = new StyleAttribute();
+
+        if (getPres(sty.setName("to")))
+        {
+            String newVal = sty.getStringValue();
+            toValue = newVal;
+        }
+    }
+}
Index: core/src/com/kitfox/svg/animation/TimeBase.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/TimeBase.java	2012-06-17 23:56:57.680545050 +0200
@@ -0,0 +1,99 @@
+/*
+ * TimeBase.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on August 15, 2004, 3:31 AM
+ */
+
+package com.kitfox.svg.animation;
+
+import java.util.regex.*;
+
+/**
+ * SVG has a complicated way of specifying time.  Potentially, a time could
+ * be represened as a summation of discrete times and times of other animation
+ * events.  This provides a root for the many elements we will need to define
+ * time.
+ *
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+abstract public class TimeBase
+{
+    static final Matcher matchIndefinite = Pattern.compile("\\s*indefinite\\s*").matcher("");
+    static final Matcher matchUnitTime = Pattern.compile("\\s*([-+]?((\\d*\\.\\d+)|(\\d+))([-+]?[eE]\\d+)?)\\s*(h|min|s|ms)?\\s*").matcher("");
+    
+    /*
+    public static TimeBase parseTime(String text) 
+    { 
+        if (text == null) return null;
+        
+        if (text.indexOf('+') == -1)
+        {
+            return parseTimeComponent(text);
+        }
+        
+        return new TimeCompound(text);
+    }
+     */
+    
+    protected static TimeBase parseTimeComponent(String text)
+    {
+        matchIndefinite.reset(text);
+        if (matchIndefinite.matches()) return new TimeIndefinite();
+        
+        matchUnitTime.reset(text);
+        if (matchUnitTime.matches())
+        {
+            String val = matchUnitTime.group(1);
+            String units = matchUnitTime.group(6);
+            
+            double time = 0;
+            try { time = Double.parseDouble(val); }
+            catch (Exception e) {}
+            
+            if (units.equals("ms")) time *= .001;
+            else if (units.equals("min")) time *= 60;
+            else if (units.equals("h")) time *= 3600;
+            
+            return new TimeDiscrete(time);
+        }
+        
+        return null;
+    }
+    
+    /**
+     * Calculates the (greater than or equal to 0) time in seconds this 
+     * time represents.  If the time cannot be determined, returns 
+     * Double.NaN.  If this represents an infinte amount of time, returns 
+     * Double.POSITIVE_INFINITY.
+     */
+    abstract public double evalTime();
+    
+    /**
+     * Some time elements need to refer to the animation element that contains 
+     * them to evaluate correctly
+     */
+    public void setParentElement(AnimationElement ele)
+    {
+    }
+}
Index: core/src/com/kitfox/svg/animation/TimeCompound.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/TimeCompound.java	2012-06-17 23:56:57.680545050 +0200
@@ -0,0 +1,84 @@
+/*
+ * TimeDiscrete.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on August 15, 2004, 3:33 AM
+ */
+
+package com.kitfox.svg.animation;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Pattern;
+
+
+/**
+ * This represents a summation of other time elements.  It is used for complex
+ * timing events with offsets.
+ *
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+public class TimeCompound extends TimeBase
+{
+    static final Pattern patPlus = Pattern.compile("\\+");
+    
+    /**
+     * This is a list of times.  This element's time is calculated as the greatest
+     * member that is less than the current time.
+    */
+    final List componentTimes;
+
+    private AnimationElement parent;
+    
+    /** Creates a new instance of TimeDiscrete */
+    public TimeCompound(List timeBases)
+    {
+        componentTimes = Collections.unmodifiableList(timeBases);
+    }
+    
+    public double evalTime()
+    {
+        double agg = 0.0;
+        
+        for (Iterator it = componentTimes.iterator(); it.hasNext();)
+        {
+            TimeBase timeEle = (TimeBase)it.next();
+            double time = timeEle.evalTime();
+            agg += time;
+        }
+        
+        return agg;
+    }
+    
+    public void setParentElement(AnimationElement ele)
+    {
+        this.parent = ele;
+        
+        for (Iterator it = componentTimes.iterator(); it.hasNext();)
+        {
+            TimeBase timeEle = (TimeBase)it.next();
+            timeEle.setParentElement(ele);
+        }
+    }
+}
Index: core/src/com/kitfox/svg/animation/TimeDiscrete.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/TimeDiscrete.java	2012-06-17 23:56:57.680545050 +0200
@@ -0,0 +1,51 @@
+/*
+ * TimeDiscrete.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on August 15, 2004, 3:33 AM
+ */
+
+package com.kitfox.svg.animation;
+
+/**
+ * This is a time that represents a specific number of milliseconds
+ *
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+public class TimeDiscrete extends TimeBase
+{
+    //Milliseconds of delay
+    double secs;
+    
+    /** Creates a new instance of TimeDiscrete */
+    public TimeDiscrete(double secs)
+    {
+        this.secs = secs;
+    }
+    
+    public double evalTime()
+    {
+        return secs;
+    }
+    
+}
Index: core/src/com/kitfox/svg/animation/TimeIndefinite.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/TimeIndefinite.java	2012-06-17 23:56:57.680545050 +0200
@@ -0,0 +1,48 @@
+/*
+ * TimeDiscrete.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on August 15, 2004, 3:33 AM
+ */
+
+package com.kitfox.svg.animation;
+
+/**
+ * This represents the indefinite (infinite) amount of time.
+ *
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+public class TimeIndefinite extends TimeBase
+{
+    
+    /** Creates a new instance of TimeDiscrete */
+    public TimeIndefinite()
+    {
+    }
+    
+    public double evalTime()
+    {
+        return Double.POSITIVE_INFINITY;
+    }
+    
+}
Index: core/src/com/kitfox/svg/animation/TimeLookup.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/TimeLookup.java	2012-06-17 23:56:57.684545050 +0200
@@ -0,0 +1,75 @@
+/*
+ * TimeDiscrete.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on August 15, 2004, 3:33 AM
+ */
+
+package com.kitfox.svg.animation;
+
+/**
+ * This is a time that represents a specific number of milliseconds
+ *
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+public class TimeLookup extends TimeBase
+{
+    /** 
+     * This time can only be resolved in relation to it's parent
+     */
+    private AnimationElement parent;
+    
+    /**
+     * Node this lookup acts upon
+     */
+    String node;
+    
+    /**
+     * Event to evalutae on this node
+     */
+    String event;
+    
+    /**
+     * Optional parameter used by some events
+     */
+    String paramList;
+    
+    /** Creates a new instance of TimeDiscrete */
+    public TimeLookup(AnimationElement parent, String node, String event, String paramList)
+    {
+        this.parent = parent;
+        this.node = node;
+        this.event = event;
+        this.paramList = paramList;
+    }
+    
+    public double evalTime()
+    {
+        return 0.0;
+    }
+    
+    public void setParentElement(AnimationElement ele)
+    {
+        parent = ele;
+    }
+}
Index: core/src/com/kitfox/svg/animation/TimeSum.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/TimeSum.java	2012-06-17 23:56:57.684545050 +0200
@@ -0,0 +1,60 @@
+/*
+ * TimeDiscrete.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on August 15, 2004, 3:33 AM
+ */
+
+package com.kitfox.svg.animation;
+
+/**
+ * This is a time that represents a specific number of milliseconds
+ *
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+public class TimeSum extends TimeBase
+{
+    //Milliseconds of delay
+    TimeBase t1;
+    TimeBase t2;
+    boolean add;
+    
+    /** Creates a new instance of TimeDiscrete */
+    public TimeSum(TimeBase t1, TimeBase t2, boolean add)
+    {
+        this.t1 = t1;
+        this.t2 = t2;
+        this.add = add;
+    }
+    
+    public double evalTime()
+    {
+        return add ? t1.evalTime() + t2.evalTime() : t1.evalTime() - t2.evalTime();
+    }
+    
+    public void setParentElement(AnimationElement ele)
+    {
+        t1.setParentElement(ele);
+        t2.setParentElement(ele);
+    }
+}
Index: core/src/com/kitfox/svg/animation/TrackBase.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/TrackBase.java	2012-06-17 23:56:57.684545050 +0200
@@ -0,0 +1,103 @@
+/*
+ * TrackManager.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on August 15, 2004, 11:34 PM
+ */
+
+package com.kitfox.svg.animation;
+
+import java.util.*;
+
+import com.kitfox.svg.xml.*;
+import com.kitfox.svg.*;
+
+/**
+ * A track holds the animation events for a single parameter of a single SVG 
+ * element.  It also contains the default value for the element, should the
+ * user want to see the 'unanimated' value.
+ *
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+abstract public class TrackBase
+{
+    protected final String attribName;
+    protected final int attribType;  //AnimationElement.AT_*
+ 
+    /** Element we're animating */
+    protected final SVGElement parent;
+    
+    //It doesn't make sense to sort this, since some events will depend on
+    // other events - in many cases, there will be no meaningful sorted order.
+    final ArrayList animEvents = new ArrayList();
+    
+    /** Creates a new instance of TrackManager */
+//    public TrackBase(SVGElement parent)
+//    {
+//        this(parent, "", AnimationElement.AT_AUTO);
+//    }
+    
+    /**
+     * Creates a track that would be valid for the name and type of element
+     * passed in.  Does not actually add this elemnt to the track.
+     */
+    public TrackBase(SVGElement parent, AnimationElement ele) throws SVGElementException
+    {
+        this(parent, ele.getAttribName(), ele.getAttribType());
+    }
+    
+    public TrackBase(SVGElement parent, String attribName, int attribType) throws SVGElementException
+    {
+        this.parent = parent;
+        this.attribName = attribName;
+        this.attribType = attribType;
+        
+        //Make sure parent has an attribute we will write to
+        if (attribType == AnimationElement.AT_AUTO 
+            && !parent.hasAttribute(attribName, AnimationElement.AT_CSS)
+            && !parent.hasAttribute(attribName, AnimationElement.AT_XML))
+        {
+            parent.addAttribute(attribName, AnimationElement.AT_CSS, "");
+        }
+        else if (!parent.hasAttribute(attribName, attribType))
+        {
+            parent.addAttribute(attribName, attribType, "");
+        }
+    }
+    
+    public String getAttribName() { return attribName; }
+    public int getAttribType() { return attribType; }
+    
+    public void addElement(AnimationElement ele)
+    {
+        animEvents.add(ele);
+    }
+    
+    /**
+     * Returns a StyleAttribute representing the value of this track at the
+     * passed time.  If this track does not apply, returns null.
+     * @return - True if successful, false if a value could not be obtained
+     */
+    abstract public boolean getValue(StyleAttribute attrib, double curTime) throws SVGException;
+    
+}
Index: core/src/com/kitfox/svg/animation/TrackColor.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/TrackColor.java	2012-06-17 23:56:57.684545050 +0200
@@ -0,0 +1,95 @@
+/*
+ * TrackManager.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on September 21, 2004, 11:34 PM
+ */
+
+package com.kitfox.svg.animation;
+
+import com.kitfox.svg.xml.StyleAttribute;
+import java.awt.*;
+import java.util.*;
+
+import com.kitfox.svg.*;
+import com.kitfox.svg.xml.*;
+
+/**
+ * A track holds the animation events for a single parameter of a single SVG
+ * element.  It also contains the default value for the element, should the
+ * user want to see the 'unanimated' value.
+ *
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+public class TrackColor extends TrackBase
+{
+
+    public TrackColor(AnimationElement ele) throws SVGElementException
+    {
+        super(ele.getParent(), ele);
+    }
+
+    public boolean getValue(StyleAttribute attrib, double curTime)
+    {
+        Color col = getValue(curTime);
+        if (col == null) return false;
+
+        attrib.setStringValue("#" + Integer.toHexString(col.getRGB()));
+        return true;
+    }
+
+    public Color getValue(double curTime)
+    {
+        Color retVal = null;
+        AnimationTimeEval state = new AnimationTimeEval();
+
+        for (Iterator it = animEvents.iterator(); it.hasNext();)
+        {
+            AnimateBase ele = (AnimateBase)it.next();
+            AnimateColorIface eleColor = (AnimateColorIface)ele;
+            ele.evalParametric(state, curTime);
+
+            //Reject value if it is in the invalid state
+            if (Double.isNaN(state.interp)) continue;
+
+            if (retVal == null)
+            {
+                retVal = eleColor.evalColor(state.interp);
+                continue;
+            }
+            
+            Color curCol = eleColor.evalColor(state.interp);
+            switch (ele.getAdditiveType())
+            {
+                case AnimationElement.AD_REPLACE:
+                    retVal = curCol;
+                    break;
+                case AnimationElement.AD_SUM:
+                    retVal = new Color(curCol.getRed() + retVal.getRed(), curCol.getGreen() + retVal.getGreen(), curCol.getBlue() + retVal.getBlue());
+                    break;
+            }
+        }
+
+        return retVal;
+    }
+}
Index: core/src/com/kitfox/svg/animation/TrackDouble.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/TrackDouble.java	2012-06-17 23:56:57.684545050 +0200
@@ -0,0 +1,119 @@
+/*
+ * TrackManager.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on August 15, 2004, 11:34 PM
+ */
+
+package com.kitfox.svg.animation;
+
+import com.kitfox.svg.xml.StyleAttribute;
+import java.util.*;
+
+import com.kitfox.svg.*;
+import com.kitfox.svg.xml.*;
+
+/**
+ * A track holds the animation events for a single parameter of a single SVG
+ * element.  It also contains the default value for the element, should the
+ * user want to see the 'unanimated' value.
+ *
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+public class TrackDouble extends TrackBase
+{
+    public TrackDouble(AnimationElement ele) throws SVGElementException
+    {
+        super(ele.getParent(), ele);
+    }
+
+    public boolean getValue(StyleAttribute attrib, double curTime)
+    {
+        double val = getValue(curTime);
+        if (Double.isNaN(val)) return false;
+
+        attrib.setStringValue("" + val);
+        return true;
+    }
+
+    public double getValue(double curTime)
+    {
+        double retVal = Double.NaN;
+        
+        StyleAttribute attr = null;
+        switch (attribType)
+        {
+            case AnimationElement.AT_CSS:
+                attr = parent.getStyleAbsolute(attribName);
+                retVal = attr.getDoubleValue();
+                break;
+            case AnimationElement.AT_XML:
+                attr = parent.getPresAbsolute(attribName);
+                retVal = attr.getDoubleValue();
+                break;
+            case AnimationElement.AT_AUTO:
+                attr = parent.getStyleAbsolute(attribName);
+                if (attr == null) attr = parent.getPresAbsolute(attribName);
+                retVal = attr.getDoubleValue();
+                break;
+        }
+        
+        
+        
+        AnimationTimeEval state = new AnimationTimeEval();
+//        boolean pastEnd = true;
+
+        for (Iterator it = animEvents.iterator(); it.hasNext();)
+        {
+            Animate ele = (Animate)it.next();
+            ele.evalParametric(state, curTime);
+
+            //Go to next element if this one does not affect processing
+            if (Double.isNaN(state.interp)) continue;
+
+            switch (ele.getAdditiveType())
+            {
+                case AnimationElement.AD_SUM:
+                    retVal += ele.eval(state.interp);
+                    break;
+                case AnimationElement.AD_REPLACE:
+                    retVal = ele.eval(state.interp);
+                    break;
+            }
+            
+            //Evalutae accumulation if applicable
+            if (state.rep > 0)
+            {
+                switch (ele.getAccumulateType())
+                {
+                    case AnimationElement.AC_SUM:
+                        retVal += ele.repeatSkipSize(state.rep);
+                        break;
+                }
+                
+            }
+        }
+
+        return retVal;
+    }
+}
Index: core/src/com/kitfox/svg/animation/TrackManager.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/TrackManager.java	2012-06-17 23:56:57.684545050 +0200
@@ -0,0 +1,153 @@
+/*
+ * TrackManager.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on August 15, 2004, 11:34 PM
+ */
+
+package com.kitfox.svg.animation;
+
+import java.util.*;
+
+import com.kitfox.svg.*;
+import java.io.Serializable;
+
+/**
+ * Every element contains tracks, which manage the animation.  There is one track
+ * for every parameter with animation, and each track in turn is composed of
+ * many events.
+ *
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+public class TrackManager implements Serializable
+{
+    public static final long serialVersionUID = 0;
+    
+    static class TrackKey
+    {
+        String name;
+        int type;
+        
+        TrackKey(AnimationElement base)
+        {
+            this(base.getAttribName(), base.getAttribType());
+        }
+        
+        TrackKey(String name, int type)
+        {
+            this.name = name;
+            this.type = type;
+        }
+        
+        public int hashCode() { return name.hashCode() ^ type; } 
+        public boolean equals(Object obj) 
+        {
+            if (!(obj instanceof TrackKey)) return false;
+            TrackKey key = (TrackKey)obj;
+            return key.type == type && key.name.equals(name);
+        }
+    }
+    
+    HashMap tracks = new HashMap();
+    
+    /** Creates a new instance of TrackManager */
+    public TrackManager()
+    {
+    }
+    
+    /**
+     * Adds a new animation element to this track
+     */
+    public void addTrackElement(AnimationElement element) throws SVGElementException
+    {
+        TrackKey key = new TrackKey(element);
+        
+        TrackBase track = (TrackBase)tracks.get(key);
+        
+        if (track == null)
+        {
+            //Create a track for this element
+            if (element instanceof Animate)
+            {
+                switch (((Animate)element).getDataType())
+                {
+                    case Animate.DT_REAL:
+                        track = new TrackDouble(element);
+                        break;
+                    case Animate.DT_COLOR:
+                        track = new TrackColor(element);
+                        break;
+                    case Animate.DT_PATH:
+                        track = new TrackPath(element);
+                        break;
+                    default:
+                        throw new RuntimeException("");
+                }
+            }
+            else if (element instanceof AnimateColor)
+            {
+                track = new TrackColor(element);
+            }
+            else if (element instanceof AnimateTransform || element instanceof AnimateMotion)
+            {
+                track = new TrackTransform(element);
+            }
+            
+            tracks.put(key, track);
+        }
+  
+        track.addElement(element);
+    }
+    
+    public TrackBase getTrack(String name, int type)
+    {
+        //Handle AUTO, which will match either CSS or XML (in that order)
+        if (type == AnimationElement.AT_AUTO)
+        {
+            TrackBase t = getTrack(name, AnimationElement.AT_CSS);
+            if (t != null) return t;
+            t = getTrack(name, AnimationElement.AT_XML);
+            if (t != null) return t;
+            return null;
+        }
+        
+        //Get requested attribute
+        TrackKey key = new TrackKey(name, type);
+        TrackBase t = (TrackBase)tracks.get(key);
+        if (t != null) return t;
+        
+        //If that didn't exist, see if one exists of type AUTO
+        key = new TrackKey(name, AnimationElement.AT_AUTO);
+        return (TrackBase)tracks.get(key);
+    }
+    
+    public int getNumTracks()
+    {
+        return tracks.size();
+    }
+    
+    public Iterator iterator()
+    {
+        return tracks.values().iterator();
+    }
+}
Index: core/src/com/kitfox/svg/animation/TrackMotion.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/TrackMotion.java	2012-06-17 23:56:57.684545050 +0200
@@ -0,0 +1,128 @@
+/*
+ * TrackManager.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on September 21, 2004, 11:34 PM
+ */
+
+package com.kitfox.svg.animation;
+
+import com.kitfox.svg.xml.StyleAttribute;
+import java.awt.geom.*;
+import java.util.*;
+
+import com.kitfox.svg.*;
+import com.kitfox.svg.xml.*;
+
+/**
+ * A track holds the animation events for a single parameter of a single SVG
+ * element.  It also contains the default value for the element, should the
+ * user want to see the 'unanimated' value.
+ *
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+public class TrackMotion extends TrackBase
+{
+    public TrackMotion(AnimationElement ele) throws SVGElementException
+    {
+        //The motion element implies a CSS attribute of transform
+//        super(ele.getParent(), "transform", AnimationElement.AT_CSS);
+        super(ele.getParent(), ele);
+    }
+
+    public boolean getValue(StyleAttribute attrib, double curTime) throws SVGException
+    {
+        AffineTransform retVal = new AffineTransform();
+        retVal = getValue(retVal, curTime);
+//        AffineTransform val = getValue(curTime);
+//        if (val == null) return false;
+
+        double[] mat = new double[6];
+        retVal.getMatrix(mat);
+        attrib.setStringValue("matrix(" + mat[0] + " " + mat[1] + " " + mat[2] + " " + mat[3] + " " + mat[4] + " " + mat[5] + ")");
+        return true;
+    }
+
+    public AffineTransform getValue(AffineTransform retVal, double curTime) throws SVGException
+    {
+        //Init transform with default state
+        StyleAttribute attr = null;
+        switch (attribType)
+        {
+            case AnimationElement.AT_CSS:
+                attr = parent.getStyleAbsolute(attribName);
+                retVal.setTransform(SVGElement.parseSingleTransform(attr.getStringValue()));
+                break;
+            case AnimationElement.AT_XML:
+                attr = parent.getPresAbsolute(attribName);
+                retVal.setTransform(SVGElement.parseSingleTransform(attr.getStringValue()));
+                break;
+            case AnimationElement.AT_AUTO:
+                attr = parent.getStyleAbsolute(attribName);
+                if (attr == null) attr = parent.getPresAbsolute(attribName);
+                retVal.setTransform(SVGElement.parseSingleTransform(attr.getStringValue()));
+                break;
+        }
+
+
+        //Update transform with time based information
+        AnimationTimeEval state = new AnimationTimeEval();
+        AffineTransform xform = new AffineTransform();
+//        boolean pastEnd = true;
+
+        for (Iterator it = animEvents.iterator(); it.hasNext();)
+        {
+            AnimateMotion ele = (AnimateMotion)it.next();
+            ele.evalParametric(state, curTime);
+
+            //Go to next element if this one does not affect processing
+            if (Double.isNaN(state.interp)) continue;
+
+            switch (ele.getAdditiveType())
+            {
+                case AnimationElement.AD_SUM:
+                    retVal.concatenate(ele.eval(xform, state.interp));
+                    break;
+                case AnimationElement.AD_REPLACE:
+                    retVal.setTransform(ele.eval(xform, state.interp));
+                    break;
+            }
+
+            //Evaluate accumulation if applicable
+/*
+            if (state.rep > 0)
+            {
+                switch (ele.getAccumulateType())
+                {
+                    case AnimationElement.AC_SUM:
+                        retVal += ele.repeatSkipSize(state.rep);
+                        break;
+                }
+
+            }
+*/
+        }
+
+        return retVal;
+    }
+}
Index: core/src/com/kitfox/svg/animation/TrackPath.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/TrackPath.java	2012-06-17 23:56:57.684545050 +0200
@@ -0,0 +1,100 @@
+/*
+ * TrackManager.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on September 21, 2004, 11:34 PM
+ */
+
+package com.kitfox.svg.animation;
+
+import com.kitfox.svg.xml.StyleAttribute;
+import java.awt.*;
+import java.awt.geom.*;
+import java.util.*;
+
+import com.kitfox.svg.pathcmd.*;
+import com.kitfox.svg.*;
+import com.kitfox.svg.xml.*;
+
+/**
+ * A track holds the animation events for a single parameter of a single SVG
+ * element.  It also contains the default value for the element, should the
+ * user want to see the 'unanimated' value.
+ *
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+public class TrackPath extends TrackBase
+{
+
+    public TrackPath(AnimationElement ele) throws SVGElementException
+    {
+        super(ele.getParent(), ele);
+    }
+
+    public boolean getValue(StyleAttribute attrib, double curTime)
+    {
+        GeneralPath path = getValue(curTime);
+        if (path == null) return false;
+
+        attrib.setStringValue(PathUtil.buildPathString(path));
+        return true;
+    }
+
+    public GeneralPath getValue(double curTime)
+    {
+        GeneralPath retVal = null;
+        AnimationTimeEval state = new AnimationTimeEval();
+
+        for (Iterator it = animEvents.iterator(); it.hasNext();)
+        {
+            AnimateBase ele = (AnimateBase)it.next();
+            Animate eleAnim = (Animate)ele;
+            ele.evalParametric(state, curTime);
+
+            //Reject value if it is in the invalid state
+            if (Double.isNaN(state.interp)) continue;
+
+            if (retVal == null)
+            {
+                retVal = eleAnim.evalPath(state.interp);
+                continue;
+            }
+            
+            GeneralPath curPath = eleAnim.evalPath(state.interp);
+            switch (ele.getAdditiveType())
+            {
+                case AnimationElement.AD_REPLACE:
+                    retVal = curPath;
+                    break;
+                case AnimationElement.AD_SUM:
+                    throw new RuntimeException("Not implemented");
+//                    retVal = new Color(curCol.getRed() + retVal.getRed(), curCol.getGreen() + retVal.getGreen(), curCol.getBlue() + retVal.getBlue());
+//                    break;
+                default:
+                    throw new RuntimeException();
+            }
+        }
+
+        return retVal;
+    }
+}
Index: core/src/com/kitfox/svg/animation/TrackTransform.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/animation/TrackTransform.java	2012-06-17 23:56:57.684545050 +0200
@@ -0,0 +1,112 @@
+/*
+ * TrackManager.java
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on September 21, 2004, 11:34 PM
+ */
+
+package com.kitfox.svg.animation;
+
+import com.kitfox.svg.xml.StyleAttribute;
+import java.awt.geom.*;
+import java.util.*;
+
+import com.kitfox.svg.*;
+import com.kitfox.svg.xml.*;
+
+/**
+ * A track holds the animation events for a single parameter of a single SVG
+ * element.  It also contains the default value for the element, should the
+ * user want to see the 'unanimated' value.
+ *
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+public class TrackTransform extends TrackBase
+{
+    public TrackTransform(AnimationElement ele) throws SVGElementException
+    {
+        super(ele.getParent(), ele);
+    }
+
+    public boolean getValue(StyleAttribute attrib, double curTime) throws SVGException
+    {
+        AffineTransform retVal = new AffineTransform();
+        retVal = getValue(retVal, curTime);
+//        AffineTransform val = getValue(curTime);
+//        if (val == null) return false;
+
+        double[] mat = new double[6];
+        retVal.getMatrix(mat);
+        attrib.setStringValue("matrix(" + mat[0] + " " + mat[1] + " " + mat[2] + " " + mat[3] + " " + mat[4] + " " + mat[5] + ")");
+        return true;
+    }
+
+    public AffineTransform getValue(AffineTransform retVal, double curTime) throws SVGException
+    {
+        //Init transform with default state
+        StyleAttribute attr = null;
+        switch (attribType)
+        {
+            case AnimationElement.AT_CSS:
+                attr = parent.getStyleAbsolute(attribName);
+                retVal.setTransform(SVGElement.parseSingleTransform(attr.getStringValue()));
+                break;
+            case AnimationElement.AT_XML:
+                attr = parent.getPresAbsolute(attribName);
+                retVal.setTransform(SVGElement.parseSingleTransform(attr.getStringValue()));
+                break;
+            case AnimationElement.AT_AUTO:
+                attr = parent.getStyleAbsolute(attribName);
+                if (attr == null) attr = parent.getPresAbsolute(attribName);
+                retVal.setTransform(SVGElement.parseSingleTransform(attr.getStringValue()));
+                break;
+        }
+
+
+        //Update transform with time based information
+        AnimationTimeEval state = new AnimationTimeEval();
+        AffineTransform xform = new AffineTransform();
+
+        for (Iterator it = animEvents.iterator(); it.hasNext();)
+        {
+            AnimateXform ele = (AnimateXform)it.next();
+            ele.evalParametric(state, curTime);
+
+            //Go to next element if this one does not affect processing
+            if (Double.isNaN(state.interp)) continue;
+
+            switch (ele.getAdditiveType())
+            {
+                case AnimationElement.AD_SUM:
+                    retVal.concatenate(ele.eval(xform, state.interp));
+                    break;
+                case AnimationElement.AD_REPLACE:
+                    retVal.setTransform(ele.eval(xform, state.interp));
+                    break;
+            }
+
+        }
+
+        return retVal;
+    }
+}
Index: core/src/com/kitfox/svg/SVGElement.java
===================================================================
--- core.orig/src/com/kitfox/svg/SVGElement.java	2012-05-11 17:03:52.404311564 +0200
+++ core/src/com/kitfox/svg/SVGElement.java	2012-06-17 23:56:57.732545052 +0200
@@ -33,6 +33,7 @@
 import java.awt.geom.*;
 
 import org.xml.sax.*;
+import com.kitfox.svg.animation.*;
 import com.kitfox.svg.pathcmd.*;
 import com.kitfox.svg.xml.*;
 import java.io.Serializable;
@@ -100,6 +101,8 @@
      */
 //    protected SVGUniverse universe;
     
+    protected final TrackManager trackManager = new TrackManager();
+
     boolean dirty = true;
 
 //    public static final Matcher adobeId = Pattern.compile("(.*)_1_").matcher("");
@@ -265,6 +268,46 @@
         }
     }
     
+    public void addAttribute(String name, int attribType, String value) throws SVGElementException
+    {
+        if (hasAttribute(name, attribType)) throw new SVGElementException(this, "Attribute " + name + "(" + AnimationElement.animationElementToString(attribType) + ") already exists");
+        
+        //Alter layout for id attribute
+        if ("id".equals(name) && diagram != null)
+        {
+            diagram.removeElement(this.id);
+            this.id = name;
+            diagram.setElement(this.id, this);
+        }
+        
+        switch (attribType)
+        {
+            case AnimationElement.AT_CSS:
+                inlineStyles.put(name, new StyleAttribute(name, value));
+                return;
+            case AnimationElement.AT_XML:
+                presAttribs.put(name, new StyleAttribute(name, value));
+                return;
+        }
+        
+        throw new SVGElementException(this, "Invalid attribute type " + attribType);
+    }
+    
+    public boolean hasAttribute(String name, int attribType) throws SVGElementException
+    {
+        switch (attribType)
+        {
+            case AnimationElement.AT_CSS:
+                return inlineStyles.containsKey(name);
+            case AnimationElement.AT_XML:
+                return presAttribs.containsKey(name);
+            case AnimationElement.AT_AUTO:
+                return inlineStyles.containsKey(name) || presAttribs.containsKey(name);
+        }
+        
+        throw new SVGElementException(this, "Invalid attribute type " + attribType);
+    }
+    
     /**
      * @return a set of Strings that corespond to CSS attributes on this element
      */
@@ -290,6 +333,12 @@
         children.add(child);
         child.parent = this;
         child.setDiagram(diagram);
+        
+        //Add info to track if we've scanned animation element
+        if (child instanceof AnimationElement)
+        {
+            trackManager.addTrackElement((AnimationElement)child);
+        }
     }
     
     private void setDiagram(SVGDiagram diagram)
@@ -408,6 +457,54 @@
         return getStyle(attrib, true);
     }
     
+    
+    public void setAttribute(String name, int attribType, String value) throws SVGElementException
+    {
+        StyleAttribute styAttr;
+        
+        
+        switch (attribType)
+        {
+            case AnimationElement.AT_CSS:
+            {
+                styAttr = (StyleAttribute)inlineStyles.get(name);
+                break;
+            }
+            case AnimationElement.AT_XML:
+            {
+                styAttr = (StyleAttribute)presAttribs.get(name);
+                break;
+            }
+            case AnimationElement.AT_AUTO:
+            {
+                styAttr = (StyleAttribute)inlineStyles.get(name);
+                
+                if (styAttr == null)
+                {
+                    styAttr = (StyleAttribute)presAttribs.get(name);
+                }
+                break;
+            }
+            default:
+                throw new SVGElementException(this, "Invalid attribute type " + attribType);
+        }
+        
+        if (styAttr == null)
+        {
+            throw new SVGElementException(this, "Could not find attribute " + name + "(" + AnimationElement.animationElementToString(attribType) + ").  Make sure to create attribute before setting it.");
+        }
+        
+        //Alter layout for relevant attributes
+        if ("id".equals(styAttr.getName()))
+        {
+            diagram.removeElement(this.id);
+            this.id = name;
+            diagram.setElement(this.id, this);
+        }
+        
+        styAttr.setStringValue(value);
+    }
+    
     /**
      * Copies the current style into the passed style attribute.  Checks for
      * inline styles first, then internal and extranal style sheets, and
@@ -427,6 +524,14 @@
         
         attrib.setStringValue(styAttr == null ? "" : styAttr.getStringValue());
         
+        //Evalutate coresponding track, if one exists
+        TrackBase track = trackManager.getTrack(styName, AnimationElement.AT_CSS);
+        if (track != null)
+        {
+            track.getValue(attrib, diagram.getUniverse().getCurTime());
+            return true;
+        }
+        
         //Return if we've found a non animated style
         if (styAttr != null) return true;
         
@@ -439,6 +544,14 @@
         
         attrib.setStringValue(presAttr == null ? "" : presAttr.getStringValue());
         
+        //Evalutate coresponding track, if one exists
+        track = trackManager.getTrack(styName, AnimationElement.AT_XML);
+        if (track != null)
+        {
+            track.getValue(attrib, diagram.getUniverse().getCurTime());
+            return true;
+        }
+        
         //Return if we've found a presentation attribute instead
         if (presAttr != null) return true;
         
@@ -480,6 +593,14 @@
         //Copy presentation value directly
         attrib.setStringValue(presAttr == null ? "" : presAttr.getStringValue());
         
+        //Evalutate coresponding track, if one exists
+        TrackBase track = trackManager.getTrack(presName, AnimationElement.AT_XML);
+        if (track != null)
+        {
+            track.getValue(attrib, diagram.getUniverse().getCurTime());
+            return true;
+        }
+        
         //Return if we found presentation attribute
         if (presAttr != null) return true;
         
Index: core/src/com/kitfox/svg/SVGLoader.java
===================================================================
--- core.orig/src/com/kitfox/svg/SVGLoader.java	2012-05-11 17:03:52.404311564 +0200
+++ core/src/com/kitfox/svg/SVGLoader.java	2012-06-17 23:56:57.732545052 +0200
@@ -33,6 +33,8 @@
 import org.xml.sax.*;
 import org.xml.sax.helpers.DefaultHandler;
 
+import com.kitfox.svg.animation.*;
+
 /**
  * @author Mark McKay
  * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
@@ -75,6 +77,10 @@
 
         //Compile a list of important builder classes
         nodeClasses.put("a", A.class);
+        nodeClasses.put("animate", Animate.class);
+        nodeClasses.put("animatecolor", AnimateColor.class);
+        nodeClasses.put("animatemotion", AnimateMotion.class);
+        nodeClasses.put("animatetransform", AnimateTransform.class);
         nodeClasses.put("circle", Circle.class);
         nodeClasses.put("clippath", ClipPath.class);
         nodeClasses.put("defs", Defs.class);
@@ -98,6 +104,7 @@
         nodeClasses.put("polyline", Polyline.class);
         nodeClasses.put("radialgradient", RadialGradient.class);
         nodeClasses.put("rect", Rect.class);
+        nodeClasses.put("set", SetSmil.class);
         nodeClasses.put("shape", ShapeElement.class);
         nodeClasses.put("stop", Stop.class);
         nodeClasses.put("style", Style.class);
Index: core/src/com/kitfox/svg/SVGLoaderHelper.java
===================================================================
--- core.orig/src/com/kitfox/svg/SVGLoaderHelper.java	2012-05-11 17:03:52.404311564 +0200
+++ core/src/com/kitfox/svg/SVGLoaderHelper.java	2012-06-17 23:56:57.740545051 +0200
@@ -28,6 +28,9 @@
 package com.kitfox.svg;
 
 import java.net.*;
+import java.io.*;
+
+import com.kitfox.svg.animation.parser.*;
 
 /**
  * @author Mark McKay
@@ -47,6 +50,11 @@
 
     public final URI xmlBase;
 
+    /**
+     * Animate nodes use this to parse their time strings
+     */
+    public final AnimTimeParser animTimeParser = new AnimTimeParser(new StringReader(""));
+    
     /** Creates a new instance of SVGLoaderHelper */
     public SVGLoaderHelper(URI xmlBase, SVGUniverse universe, SVGDiagram diagram)
     {
Index: core/src/com/kitfox/svg/app/MainFrame.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/app/MainFrame.java	2012-06-17 23:56:57.748545052 +0200
@@ -0,0 +1,134 @@
+/*
+ * MainFrame.java
+ *
+ * Created on September 6, 2004, 1:19 AM
+ */
+
+package com.kitfox.svg.app;
+
+/**
+ *
+ * @author  kitfox
+ */
+public class MainFrame extends javax.swing.JFrame
+{
+    public static final long serialVersionUID = 1;
+    
+    /** Creates new form MainFrame */
+    public MainFrame()
+    {
+        initComponents();
+    }
+    
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    private void initComponents()//GEN-BEGIN:initComponents
+    {
+        jPanel1 = new javax.swing.JPanel();
+        bn_svgViewer = new javax.swing.JButton();
+        bn_svgViewer1 = new javax.swing.JButton();
+        jPanel2 = new javax.swing.JPanel();
+        bn_quit = new javax.swing.JButton();
+
+        setTitle("SVG Salamander - Application Launcher");
+        addWindowListener(new java.awt.event.WindowAdapter()
+        {
+            public void windowClosing(java.awt.event.WindowEvent evt)
+            {
+                exitForm(evt);
+            }
+        });
+
+        jPanel1.setLayout(new javax.swing.BoxLayout(jPanel1, javax.swing.BoxLayout.Y_AXIS));
+
+        bn_svgViewer.setText("SVG Viewer (No animation)");
+        bn_svgViewer.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                bn_svgViewerActionPerformed(evt);
+            }
+        });
+
+        jPanel1.add(bn_svgViewer);
+
+        bn_svgViewer1.setText("SVG Player (Animation)");
+        bn_svgViewer1.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                bn_svgViewer1ActionPerformed(evt);
+            }
+        });
+
+        jPanel1.add(bn_svgViewer1);
+
+        getContentPane().add(jPanel1, java.awt.BorderLayout.CENTER);
+
+        bn_quit.setText("Quit");
+        bn_quit.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                bn_quitActionPerformed(evt);
+            }
+        });
+
+        jPanel2.add(bn_quit);
+
+        getContentPane().add(jPanel2, java.awt.BorderLayout.SOUTH);
+
+        pack();
+    }//GEN-END:initComponents
+
+    private void bn_svgViewer1ActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_bn_svgViewer1ActionPerformed
+    {//GEN-HEADEREND:event_bn_svgViewer1ActionPerformed
+        SVGPlayer.main(null);
+
+        close();
+    }//GEN-LAST:event_bn_svgViewer1ActionPerformed
+
+    private void bn_svgViewerActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_bn_svgViewerActionPerformed
+    {//GEN-HEADEREND:event_bn_svgViewerActionPerformed
+        SVGViewer.main(null);
+
+        close();
+    }//GEN-LAST:event_bn_svgViewerActionPerformed
+
+    private void bn_quitActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_bn_quitActionPerformed
+    {//GEN-HEADEREND:event_bn_quitActionPerformed
+        exitForm(null);
+    }//GEN-LAST:event_bn_quitActionPerformed
+    
+    /** Exit the Application */
+    private void exitForm(java.awt.event.WindowEvent evt)//GEN-FIRST:event_exitForm
+    {
+        System.exit(0);
+    }//GEN-LAST:event_exitForm
+    
+    private void close()
+    {
+        this.setVisible(false);
+        this.dispose();
+    }
+    
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String args[])
+    {
+        new MainFrame().setVisible(true);
+    }
+    
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JButton bn_quit;
+    private javax.swing.JButton bn_svgViewer;
+    private javax.swing.JButton bn_svgViewer1;
+    private javax.swing.JPanel jPanel1;
+    private javax.swing.JPanel jPanel2;
+    // End of variables declaration//GEN-END:variables
+    
+}
Index: core/src/com/kitfox/svg/app/PlayerDialog.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/app/PlayerDialog.java	2012-06-17 23:56:57.748545052 +0200
@@ -0,0 +1,289 @@
+/*
+ * PlayerDialog.java
+ *
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on September 28, 2004, 9:56 PM
+ */
+
+package com.kitfox.svg.app;
+
+/**
+ *
+ * @author  kitfox
+ */
+public class PlayerDialog extends javax.swing.JDialog implements PlayerThreadListener
+{
+    public static final long serialVersionUID = 1;
+    
+    PlayerThread thread;
+    
+    final SVGPlayer parent;
+    
+    /** Creates new form PlayerDialog */
+    public PlayerDialog(SVGPlayer parent)
+    {
+        super(parent, false);
+        initComponents();
+        
+        this.parent = parent;
+        
+        thread = new PlayerThread();
+        thread.addListener(this);
+        
+        text_timeStepActionPerformed(null);
+    }
+    
+    public void updateTime(double curTime, double timeStep, int playState)
+    {
+        if (playState == PlayerThread.PS_STOP) return;
+        
+        text_curTime.setText("" + (float)curTime);
+        parent.updateTime(curTime);
+//        text_timeStep.setText("" + (int)(1.0 / timeStep));
+    }
+    
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
+    private void initComponents()
+    {
+        jPanel1 = new javax.swing.JPanel();
+        bn_playBack = new javax.swing.JButton();
+        bn_stop = new javax.swing.JButton();
+        bn_playFwd = new javax.swing.JButton();
+        jPanel2 = new javax.swing.JPanel();
+        jPanel3 = new javax.swing.JPanel();
+        jLabel1 = new javax.swing.JLabel();
+        text_curTime = new javax.swing.JTextField();
+        bn_time0 = new javax.swing.JButton();
+        jPanel4 = new javax.swing.JPanel();
+        jLabel2 = new javax.swing.JLabel();
+        text_timeStep = new javax.swing.JTextField();
+
+        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
+        setTitle("Player");
+        addWindowListener(new java.awt.event.WindowAdapter()
+        {
+            public void windowClosed(java.awt.event.WindowEvent evt)
+            {
+                formWindowClosed(evt);
+            }
+        });
+
+        bn_playBack.setText("<");
+        bn_playBack.setToolTipText("Play backwards");
+        bn_playBack.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                bn_playBackActionPerformed(evt);
+            }
+        });
+
+        jPanel1.add(bn_playBack);
+
+        bn_stop.setText("||");
+        bn_stop.setToolTipText("Stop playback");
+        bn_stop.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                bn_stopActionPerformed(evt);
+            }
+        });
+
+        jPanel1.add(bn_stop);
+
+        bn_playFwd.setText(">");
+        bn_playFwd.setToolTipText("Play Forwards");
+        bn_playFwd.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                bn_playFwdActionPerformed(evt);
+            }
+        });
+
+        jPanel1.add(bn_playFwd);
+
+        getContentPane().add(jPanel1, java.awt.BorderLayout.NORTH);
+
+        jPanel2.setLayout(new javax.swing.BoxLayout(jPanel2, javax.swing.BoxLayout.Y_AXIS));
+
+        jLabel1.setText("Cur Time");
+        jPanel3.add(jLabel1);
+
+        text_curTime.setHorizontalAlignment(javax.swing.JTextField.LEFT);
+        text_curTime.setText("0");
+        text_curTime.setPreferredSize(new java.awt.Dimension(100, 21));
+        text_curTime.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                text_curTimeActionPerformed(evt);
+            }
+        });
+        text_curTime.addFocusListener(new java.awt.event.FocusAdapter()
+        {
+            public void focusLost(java.awt.event.FocusEvent evt)
+            {
+                text_curTimeFocusLost(evt);
+            }
+        });
+
+        jPanel3.add(text_curTime);
+
+        bn_time0.setText("Time 0");
+        bn_time0.setToolTipText("Reset time to first frame");
+        bn_time0.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                bn_time0ActionPerformed(evt);
+            }
+        });
+
+        jPanel3.add(bn_time0);
+
+        jPanel2.add(jPanel3);
+
+        jLabel2.setText("Frames Per Second");
+        jPanel4.add(jLabel2);
+
+        text_timeStep.setHorizontalAlignment(javax.swing.JTextField.RIGHT);
+        text_timeStep.setText("60");
+        text_timeStep.setPreferredSize(new java.awt.Dimension(100, 21));
+        text_timeStep.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                text_timeStepActionPerformed(evt);
+            }
+        });
+        text_timeStep.addFocusListener(new java.awt.event.FocusAdapter()
+        {
+            public void focusLost(java.awt.event.FocusEvent evt)
+            {
+                text_timeStepFocusLost(evt);
+            }
+        });
+
+        jPanel4.add(text_timeStep);
+
+        jPanel2.add(jPanel4);
+
+        getContentPane().add(jPanel2, java.awt.BorderLayout.CENTER);
+
+        pack();
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void bn_time0ActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_bn_time0ActionPerformed
+    {//GEN-HEADEREND:event_bn_time0ActionPerformed
+        thread.setCurTime(0);
+    }//GEN-LAST:event_bn_time0ActionPerformed
+
+    private void bn_playFwdActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_bn_playFwdActionPerformed
+    {//GEN-HEADEREND:event_bn_playFwdActionPerformed
+        thread.setPlayState(PlayerThread.PS_PLAY_FWD);
+    }//GEN-LAST:event_bn_playFwdActionPerformed
+
+    private void bn_stopActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_bn_stopActionPerformed
+    {//GEN-HEADEREND:event_bn_stopActionPerformed
+        thread.setPlayState(PlayerThread.PS_STOP);
+    }//GEN-LAST:event_bn_stopActionPerformed
+
+    private void bn_playBackActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_bn_playBackActionPerformed
+    {//GEN-HEADEREND:event_bn_playBackActionPerformed
+        thread.setPlayState(PlayerThread.PS_PLAY_BACK);
+    }//GEN-LAST:event_bn_playBackActionPerformed
+
+    private void formWindowClosed(java.awt.event.WindowEvent evt)//GEN-FIRST:event_formWindowClosed
+    {//GEN-HEADEREND:event_formWindowClosed
+//        thread.exit();
+    }//GEN-LAST:event_formWindowClosed
+
+    private void text_timeStepFocusLost(java.awt.event.FocusEvent evt)//GEN-FIRST:event_text_timeStepFocusLost
+    {//GEN-HEADEREND:event_text_timeStepFocusLost
+        text_timeStepActionPerformed(null);
+    }//GEN-LAST:event_text_timeStepFocusLost
+
+    private void text_timeStepActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_text_timeStepActionPerformed
+    {//GEN-HEADEREND:event_text_timeStepActionPerformed
+        try
+        {
+            int val = Integer.parseInt(text_timeStep.getText());
+            thread.setTimeStep(1.0 / val);
+        }
+        catch (Exception e)
+        {
+        }
+        
+        double d = thread.getTimeStep();
+        String newStrn = "" + (int)(1f / d);
+        if (newStrn.equals(text_timeStep.getText())) return;
+        text_timeStep.setText(newStrn);
+        
+//        text_timeStepActionPerformed(null);
+    }//GEN-LAST:event_text_timeStepActionPerformed
+
+    private void text_curTimeActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_text_curTimeActionPerformed
+    {//GEN-HEADEREND:event_text_curTimeActionPerformed
+        try
+        {
+            double val = Double.parseDouble(text_curTime.getText());
+            thread.setCurTime(val);
+        }
+        catch (Exception e)
+        {
+        }
+        
+        double d = thread.getCurTime();
+        text_curTime.setText("" + (float)d);
+        
+        text_timeStepActionPerformed(null);
+    }//GEN-LAST:event_text_curTimeActionPerformed
+
+    private void text_curTimeFocusLost(java.awt.event.FocusEvent evt)//GEN-FIRST:event_text_curTimeFocusLost
+    {//GEN-HEADEREND:event_text_curTimeFocusLost
+        text_curTimeActionPerformed(null);
+    }//GEN-LAST:event_text_curTimeFocusLost
+    
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JButton bn_playBack;
+    private javax.swing.JButton bn_playFwd;
+    private javax.swing.JButton bn_stop;
+    private javax.swing.JButton bn_time0;
+    private javax.swing.JLabel jLabel1;
+    private javax.swing.JLabel jLabel2;
+    private javax.swing.JPanel jPanel1;
+    private javax.swing.JPanel jPanel2;
+    private javax.swing.JPanel jPanel3;
+    private javax.swing.JPanel jPanel4;
+    private javax.swing.JTextField text_curTime;
+    private javax.swing.JTextField text_timeStep;
+    // End of variables declaration//GEN-END:variables
+    
+}
Index: core/src/com/kitfox/svg/app/PlayerThread.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/app/PlayerThread.java	2012-06-17 23:56:57.748545052 +0200
@@ -0,0 +1,129 @@
+/*
+ * PlayerThread.java
+ *
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on September 28, 2004, 10:07 PM
+ */
+
+
+package com.kitfox.svg.app;
+
+import java.util.*;
+
+/**
+ *
+ * @author  kitfox
+ */
+public class PlayerThread implements Runnable
+{
+    HashSet listeners = new HashSet();
+    
+    double curTime = 0;
+    double timeStep = .2;
+    
+    public static final int PS_STOP = 0;
+    public static final int PS_PLAY_FWD = 1;
+    public static final int PS_PLAY_BACK = 2;
+    
+    int playState = PS_STOP;
+    
+    Thread thread;
+    
+    /** Creates a new instance of PlayerThread */
+    public PlayerThread()
+    {
+        thread = new Thread(this);
+        thread.start();
+    }
+    
+    public void run()
+    {
+        while (thread != null)
+        {
+            synchronized (this)
+            {
+                switch (playState)
+                {
+                    case PS_PLAY_FWD:
+                        curTime += timeStep;
+                        break;
+                    case PS_PLAY_BACK:
+                        curTime -= timeStep;
+                        if (curTime < 0) curTime = 0;
+                        break;
+                    default:
+                    case PS_STOP:
+                        break;
+                }
+                
+                fireTimeUpdateEvent();
+            }
+            
+            try
+            {
+                Thread.sleep((long)(timeStep * 1000));
+            }
+            catch (Exception e) 
+            { 
+                throw new RuntimeException(e); 
+            }
+        }
+    }
+    
+    public void exit() { thread = null; }
+    public synchronized void addListener(PlayerThreadListener listener) 
+    {
+        listeners.add(listener); 
+    }
+    
+    public synchronized double getCurTime() { return curTime; }
+    
+    public synchronized void setCurTime(double time)
+    {
+        curTime = time;
+    }
+    
+    public synchronized double getTimeStep() { return timeStep; }
+    
+    public synchronized void setTimeStep(double time)
+    {
+        timeStep = time;
+        if (timeStep < .01) timeStep = .01;
+    }
+    
+    public synchronized int getPlayState() { return playState; }
+    
+    public synchronized void setPlayState(int playState)
+    {
+        this.playState = playState;
+    }
+    
+    private void fireTimeUpdateEvent()
+    {
+        for (Iterator it = listeners.iterator(); it.hasNext();)
+        {
+            PlayerThreadListener listener = (PlayerThreadListener)it.next();
+            listener.updateTime(curTime, timeStep, playState);
+        }
+    }
+}
Index: core/src/com/kitfox/svg/app/PlayerThreadListener.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/app/PlayerThreadListener.java	2012-06-17 23:56:57.748545052 +0200
@@ -0,0 +1,37 @@
+/*
+ * PlayerThreadListener.java
+ *
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on September 28, 2004, 10:15 PM
+ */
+
+package com.kitfox.svg.app;
+
+/**
+ *
+ * @author  kitfox
+ */
+public interface PlayerThreadListener
+{
+    public void updateTime(double curTime, double timeStep, int playState);
+}
Index: core/src/com/kitfox/svg/app/SVGPlayer.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/app/SVGPlayer.java	2012-06-17 23:56:57.748545052 +0200
@@ -0,0 +1,436 @@
+/*
+ * SVGViewer.java
+ *
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on April 3, 2004, 5:28 PM
+ */
+
+package com.kitfox.svg.app;
+
+
+import com.kitfox.svg.SVGDiagram;
+import com.kitfox.svg.SVGDisplayPanel;
+import com.kitfox.svg.SVGElement;
+import com.kitfox.svg.SVGException;
+import com.kitfox.svg.SVGUniverse;
+import java.awt.Color;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.geom.Point2D;
+import java.io.File;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.security.AccessControlException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+
+/**
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+public class SVGPlayer extends javax.swing.JFrame
+{
+    public static final long serialVersionUID = 1;
+
+    SVGDisplayPanel svgDisplayPanel = new SVGDisplayPanel();
+
+    final PlayerDialog playerDialog;
+    
+    SVGUniverse universe;
+    
+    /** FileChooser for running in trusted environments */
+    final JFileChooser fileChooser;
+    {
+//        fileChooser = new JFileChooser(new File("."));
+        JFileChooser fc = null;
+        try
+        {
+            fc = new JFileChooser();
+            fc.setFileFilter(
+                new javax.swing.filechooser.FileFilter() {
+                    final Matcher matchLevelFile = Pattern.compile(".*\\.svg[z]?").matcher("");
+
+                    public boolean accept(File file)
+                    {
+                        if (file.isDirectory()) return true;
+
+                        matchLevelFile.reset(file.getName());
+                        return matchLevelFile.matches();
+                    }
+
+                    public String getDescription() { return "SVG file (*.svg, *.svgz)"; }
+                }
+            );
+        }
+        catch (AccessControlException ex)
+        {
+            //Do not create file chooser if webstart refuses permissions
+        }
+        fileChooser = fc;
+    }
+
+    /** Backup file service for opening files in WebStart situations */
+    /*
+    final FileOpenService fileOpenService;
+    {
+        try 
+        { 
+            fileOpenService = (FileOpenService)ServiceManager.lookup("javax.jnlp.FileOpenService"); 
+        } 
+        catch (UnavailableServiceException e) 
+        { 
+            fileOpenService = null; 
+        } 
+    }
+     */
+    
+    /** Creates new form SVGViewer */
+    public SVGPlayer() {
+        initComponents();
+
+        setSize(800, 600);
+
+        svgDisplayPanel.setBgColor(Color.white);
+        svgDisplayPanel.addMouseListener(new MouseAdapter()
+        {
+            public void mouseClicked(MouseEvent evt)
+            {
+                SVGDiagram diagram = svgDisplayPanel.getDiagram();
+                if (diagram == null) return;
+                
+                System.out.println("Picking at cursor (" + evt.getX() + ", " + evt.getY() + ")");
+                try
+                {
+                    List paths = diagram.pick(new Point2D.Float(evt.getX(), evt.getY()), null);
+                    for (int i = 0; i < paths.size(); i++)
+                    {
+                        ArrayList path = (ArrayList)paths.get(i);
+                        System.out.println(pathToString(path));
+                    }
+                }
+                catch (SVGException ex)
+                {
+                    ex.printStackTrace();
+                }
+            }
+        }
+        );
+        
+        svgDisplayPanel.setPreferredSize(getSize());
+        scrollPane_svgArea.setViewportView(svgDisplayPanel);
+        
+        playerDialog = new PlayerDialog(this);
+    }
+    
+    private String pathToString(List path)
+    {
+        if (path.size() == 0) return "";
+        
+        StringBuffer sb = new StringBuffer();
+        sb.append(path.get(0));
+        for (int i = 1; i < path.size(); i++)
+        {
+            sb.append("/");
+            sb.append(((SVGElement)path.get(i)).getId());
+        }
+        return sb.toString();
+    }
+    
+    public void updateTime(double curTime)
+    {
+        try
+        {
+            if (universe != null)
+            {
+                universe.setCurTime(curTime);
+                universe.updateTime();
+    //            svgDisplayPanel.updateTime(curTime);
+                repaint();
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    private void loadURL(URL url)
+    {
+        boolean verbose = cmCheck_verbose.isSelected();
+
+        universe = new SVGUniverse();
+        universe.setVerbose(verbose);
+        SVGDiagram diagram = null;
+
+        if (!CheckBoxMenuItem_anonInputStream.isSelected())
+        {
+            //Load from a disk with a valid URL
+            URI uri = universe.loadSVG(url);
+
+            if (verbose) System.err.println(uri.toString());
+
+            diagram = universe.getDiagram(uri);
+        }
+        else
+        {
+            //Load from a stream with no particular valid URL
+            try
+            {
+                InputStream is = url.openStream();
+                URI uri = universe.loadSVG(is, "defaultName");
+
+                if (verbose) System.err.println(uri.toString());
+
+                diagram = universe.getDiagram(uri);
+            }
+            catch (Exception e)
+            {
+                e.printStackTrace();
+            }
+        }
+
+        svgDisplayPanel.setDiagram(diagram);
+        repaint();
+    }
+    
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
+    private void initComponents()
+    {
+        scrollPane_svgArea = new javax.swing.JScrollPane();
+        jMenuBar1 = new javax.swing.JMenuBar();
+        menu_file = new javax.swing.JMenu();
+        cm_loadFile = new javax.swing.JMenuItem();
+        cm_loadUrl = new javax.swing.JMenuItem();
+        menu_window = new javax.swing.JMenu();
+        cm_player = new javax.swing.JMenuItem();
+        jSeparator2 = new javax.swing.JSeparator();
+        cm_800x600 = new javax.swing.JMenuItem();
+        CheckBoxMenuItem_anonInputStream = new javax.swing.JCheckBoxMenuItem();
+        cmCheck_verbose = new javax.swing.JCheckBoxMenuItem();
+        menu_help = new javax.swing.JMenu();
+        cm_about = new javax.swing.JMenuItem();
+
+        setTitle("SVG Player - Salamander Project");
+        addWindowListener(new java.awt.event.WindowAdapter()
+        {
+            public void windowClosing(java.awt.event.WindowEvent evt)
+            {
+                exitForm(evt);
+            }
+        });
+
+        getContentPane().add(scrollPane_svgArea, java.awt.BorderLayout.CENTER);
+
+        menu_file.setMnemonic('f');
+        menu_file.setText("File");
+        cm_loadFile.setMnemonic('l');
+        cm_loadFile.setText("Load File...");
+        cm_loadFile.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                cm_loadFileActionPerformed(evt);
+            }
+        });
+
+        menu_file.add(cm_loadFile);
+
+        cm_loadUrl.setText("Load URL...");
+        cm_loadUrl.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                cm_loadUrlActionPerformed(evt);
+            }
+        });
+
+        menu_file.add(cm_loadUrl);
+
+        jMenuBar1.add(menu_file);
+
+        menu_window.setText("Window");
+        cm_player.setText("Player");
+        cm_player.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                cm_playerActionPerformed(evt);
+            }
+        });
+
+        menu_window.add(cm_player);
+
+        menu_window.add(jSeparator2);
+
+        cm_800x600.setText("800 x 600");
+        cm_800x600.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                cm_800x600ActionPerformed(evt);
+            }
+        });
+
+        menu_window.add(cm_800x600);
+
+        CheckBoxMenuItem_anonInputStream.setText("Anonymous Input Stream");
+        menu_window.add(CheckBoxMenuItem_anonInputStream);
+
+        cmCheck_verbose.setText("Verbose");
+        cmCheck_verbose.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                cmCheck_verboseActionPerformed(evt);
+            }
+        });
+
+        menu_window.add(cmCheck_verbose);
+
+        jMenuBar1.add(menu_window);
+
+        menu_help.setText("Help");
+        cm_about.setText("About...");
+        cm_about.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                cm_aboutActionPerformed(evt);
+            }
+        });
+
+        menu_help.add(cm_about);
+
+        jMenuBar1.add(menu_help);
+
+        setJMenuBar(jMenuBar1);
+
+        pack();
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void cm_loadUrlActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_cm_loadUrlActionPerformed
+    {//GEN-HEADEREND:event_cm_loadUrlActionPerformed
+        String urlStrn = JOptionPane.showInputDialog(this, "Enter URL of SVG file");
+        if (urlStrn == null) return;
+        
+        try
+        {
+            URL url = new URL(URLEncoder.encode(urlStrn, "UTF-8"));
+            loadURL(url);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+
+    }//GEN-LAST:event_cm_loadUrlActionPerformed
+
+    private void cmCheck_verboseActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_cmCheck_verboseActionPerformed
+    {//GEN-HEADEREND:event_cmCheck_verboseActionPerformed
+// TODO add your handling code here:
+    }//GEN-LAST:event_cmCheck_verboseActionPerformed
+
+    private void cm_playerActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_cm_playerActionPerformed
+    {//GEN-HEADEREND:event_cm_playerActionPerformed
+        playerDialog.setVisible(true);
+    }//GEN-LAST:event_cm_playerActionPerformed
+
+    private void cm_aboutActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_cm_aboutActionPerformed
+    {//GEN-HEADEREND:event_cm_aboutActionPerformed
+        VersionDialog dia = new VersionDialog(this, true, cmCheck_verbose.isSelected());
+        dia.setVisible(true);
+//        JOptionPane.showMessageDialog(this, "Salamander SVG - Created by Mark McKay\nhttp://www.kitfox.com");
+    }//GEN-LAST:event_cm_aboutActionPerformed
+
+    private void cm_800x600ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cm_800x600ActionPerformed
+        setSize(800, 600);
+    }//GEN-LAST:event_cm_800x600ActionPerformed
+    
+    private void cm_loadFileActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_cm_loadFileActionPerformed
+    {//GEN-HEADEREND:event_cm_loadFileActionPerformed
+        boolean verbose = cmCheck_verbose.isSelected();
+        
+        try
+        {
+            int retVal = fileChooser.showOpenDialog(this);
+            if (retVal == JFileChooser.APPROVE_OPTION)
+            {
+                File chosenFile = fileChooser.getSelectedFile();
+
+                URL url = chosenFile.toURI().toURL();
+
+                loadURL(url);
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+
+    }//GEN-LAST:event_cm_loadFileActionPerformed
+
+    /** Exit the Application */
+    private void exitForm(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_exitForm
+        System.exit(0);
+    }//GEN-LAST:event_exitForm
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String args[]) {
+        new SVGPlayer().setVisible(true);
+    }
+
+    public void updateTime(double curTime, double timeStep, int playState)
+    {
+    }
+    
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JCheckBoxMenuItem CheckBoxMenuItem_anonInputStream;
+    private javax.swing.JCheckBoxMenuItem cmCheck_verbose;
+    private javax.swing.JMenuItem cm_800x600;
+    private javax.swing.JMenuItem cm_about;
+    private javax.swing.JMenuItem cm_loadFile;
+    private javax.swing.JMenuItem cm_loadUrl;
+    private javax.swing.JMenuItem cm_player;
+    private javax.swing.JMenuBar jMenuBar1;
+    private javax.swing.JSeparator jSeparator2;
+    private javax.swing.JMenu menu_file;
+    private javax.swing.JMenu menu_help;
+    private javax.swing.JMenu menu_window;
+    private javax.swing.JScrollPane scrollPane_svgArea;
+    // End of variables declaration//GEN-END:variables
+
+}
Index: core/src/com/kitfox/svg/app/SVGViewer.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/app/SVGViewer.java	2012-06-17 23:56:57.752545052 +0200
@@ -0,0 +1,432 @@
+/*
+ * SVGViewer.java
+ *
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on April 3, 2004, 5:28 PM
+ */
+
+package com.kitfox.svg.app;
+
+import com.kitfox.svg.SVGCache;
+import com.kitfox.svg.SVGDiagram;
+import com.kitfox.svg.SVGDisplayPanel;
+import com.kitfox.svg.SVGElement;
+import com.kitfox.svg.SVGException;
+import com.kitfox.svg.SVGUniverse;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Point;
+import java.io.File;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.security.AccessControlException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.swing.JFileChooser;
+import javax.swing.JOptionPane;
+
+
+/**
+ * @author Mark McKay
+ * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
+ */
+public class SVGViewer extends javax.swing.JFrame 
+{
+    public static final long serialVersionUID = 1;
+
+    SVGDisplayPanel svgDisplayPanel = new SVGDisplayPanel();
+
+    /** FileChooser for running in trusted environments */
+    final JFileChooser fileChooser;
+    {
+//        fileChooser = new JFileChooser(new File("."));
+        JFileChooser fc = null;
+        try
+        {
+            fc = new JFileChooser();
+            fc.setFileFilter(
+                new javax.swing.filechooser.FileFilter() {
+                    final Matcher matchLevelFile = Pattern.compile(".*\\.svg[z]?").matcher("");
+
+                    public boolean accept(File file)
+                    {
+                        if (file.isDirectory()) return true;
+
+                        matchLevelFile.reset(file.getName());
+                        return matchLevelFile.matches();
+                    }
+
+                    public String getDescription() { return "SVG file (*.svg, *.svgz)"; }
+                }
+            );
+        }
+        catch (AccessControlException ex)
+        {
+            //Do not create file chooser if webstart refuses permissions
+        }
+        fileChooser = fc;
+    }
+
+    /** Backup file service for opening files in WebStart situations */
+    /*
+    final FileOpenService fileOpenService;
+    {
+        try 
+        { 
+            fileOpenService = (FileOpenService)ServiceManager.lookup("javax.jnlp.FileOpenService"); 
+        } 
+        catch (UnavailableServiceException e) 
+        { 
+            fileOpenService = null; 
+        } 
+    }
+     */
+    
+    /** Creates new form SVGViewer */
+    public SVGViewer() {
+        initComponents();
+
+        setSize(800, 600);
+
+        svgDisplayPanel.setBgColor(Color.white);
+
+        svgDisplayPanel.setPreferredSize(getSize());
+        panel_svgArea.add(svgDisplayPanel, BorderLayout.CENTER);
+//        scrollPane_svgArea.setViewportView(svgDisplayPanel);
+    }
+
+    private void loadURL(URL url)
+    {
+        boolean verbose = cmCheck_verbose.isSelected();
+        
+//                SVGUniverse universe = new SVGUniverse();
+        SVGUniverse universe = SVGCache.getSVGUniverse();
+        SVGDiagram diagram = null;
+        URI uri;
+
+        if (!CheckBoxMenuItem_anonInputStream.isSelected())
+        {
+            //Load from a disk with a valid URL
+            uri = universe.loadSVG(url);
+
+            if (verbose) System.err.println("Loading document " + uri.toString());
+
+            diagram = universe.getDiagram(uri);
+        }
+        else
+        {
+            //Load from a stream with no particular valid URL
+            try
+            {
+                InputStream is = url.openStream();
+                uri = universe.loadSVG(is, "defaultName");
+
+                if (verbose) System.err.println("Loading document " + uri.toString());
+            }
+            catch (Exception e)
+            {
+                e.printStackTrace();
+                return;
+            }
+        }
+/*
+ByteArrayOutputStream bs = new ByteArrayOutputStream();
+ObjectOutputStream os = new ObjectOutputStream(bs);
+os.writeObject(universe);
+os.close();
+
+ByteArrayInputStream bin = new ByteArrayInputStream(bs.toByteArray());
+ObjectInputStream is = new ObjectInputStream(bin);
+universe = (SVGUniverse)is.readObject();
+is.close();
+*/
+
+        diagram = universe.getDiagram(uri);
+
+        svgDisplayPanel.setDiagram(diagram);
+        repaint();
+    }
+    
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
+    private void initComponents()
+    {
+        scrollPane_svgArea = new javax.swing.JScrollPane();
+        panel_svgArea = new javax.swing.JPanel();
+        jMenuBar1 = new javax.swing.JMenuBar();
+        menu_file = new javax.swing.JMenu();
+        cm_loadFile = new javax.swing.JMenuItem();
+        cm_loadUrl = new javax.swing.JMenuItem();
+        menu_window = new javax.swing.JMenu();
+        cm_800x600 = new javax.swing.JMenuItem();
+        CheckBoxMenuItem_anonInputStream = new javax.swing.JCheckBoxMenuItem();
+        cmCheck_verbose = new javax.swing.JCheckBoxMenuItem();
+        menu_help = new javax.swing.JMenu();
+        cm_about = new javax.swing.JMenuItem();
+
+        setTitle("SVG Viewer - Salamander Project");
+        addWindowListener(new java.awt.event.WindowAdapter()
+        {
+            public void windowClosing(java.awt.event.WindowEvent evt)
+            {
+                exitForm(evt);
+            }
+        });
+
+        panel_svgArea.setLayout(new java.awt.BorderLayout());
+
+        panel_svgArea.addMouseListener(new java.awt.event.MouseAdapter()
+        {
+            public void mousePressed(java.awt.event.MouseEvent evt)
+            {
+                panel_svgAreaMousePressed(evt);
+            }
+            public void mouseReleased(java.awt.event.MouseEvent evt)
+            {
+                panel_svgAreaMouseReleased(evt);
+            }
+        });
+
+        scrollPane_svgArea.setViewportView(panel_svgArea);
+
+        getContentPane().add(scrollPane_svgArea, java.awt.BorderLayout.CENTER);
+
+        menu_file.setMnemonic('f');
+        menu_file.setText("File");
+        cm_loadFile.setMnemonic('l');
+        cm_loadFile.setText("Load File...");
+        cm_loadFile.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                cm_loadFileActionPerformed(evt);
+            }
+        });
+
+        menu_file.add(cm_loadFile);
+
+        cm_loadUrl.setText("Load URL...");
+        cm_loadUrl.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                cm_loadUrlActionPerformed(evt);
+            }
+        });
+
+        menu_file.add(cm_loadUrl);
+
+        jMenuBar1.add(menu_file);
+
+        menu_window.setText("Window");
+        cm_800x600.setText("800 x 600");
+        cm_800x600.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                cm_800x600ActionPerformed(evt);
+            }
+        });
+
+        menu_window.add(cm_800x600);
+
+        CheckBoxMenuItem_anonInputStream.setText("Anonymous Input Stream");
+        menu_window.add(CheckBoxMenuItem_anonInputStream);
+
+        cmCheck_verbose.setText("Verbose");
+        cmCheck_verbose.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                cmCheck_verboseActionPerformed(evt);
+            }
+        });
+
+        menu_window.add(cmCheck_verbose);
+
+        jMenuBar1.add(menu_window);
+
+        menu_help.setText("Help");
+        cm_about.setText("About...");
+        cm_about.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                cm_aboutActionPerformed(evt);
+            }
+        });
+
+        menu_help.add(cm_about);
+
+        jMenuBar1.add(menu_help);
+
+        setJMenuBar(jMenuBar1);
+
+        pack();
+    }// </editor-fold>//GEN-END:initComponents
+
+    private void cm_loadUrlActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_cm_loadUrlActionPerformed
+    {//GEN-HEADEREND:event_cm_loadUrlActionPerformed
+        String urlStrn = JOptionPane.showInputDialog(this, "Enter URL of SVG file");
+        if (urlStrn == null) return;
+        
+        try
+        {
+            URL url = new URL(URLEncoder.encode(urlStrn, "UTF-8"));
+            loadURL(url);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+
+    }//GEN-LAST:event_cm_loadUrlActionPerformed
+
+    private void panel_svgAreaMouseReleased(java.awt.event.MouseEvent evt)//GEN-FIRST:event_panel_svgAreaMouseReleased
+    {//GEN-HEADEREND:event_panel_svgAreaMouseReleased
+        SVGDiagram diagram = svgDisplayPanel.getDiagram();
+        List pickedElements;
+        try
+        {
+            pickedElements = diagram.pick(new Point(evt.getX(), evt.getY()), null);
+        } 
+        catch (SVGException ex)
+        {
+            ex.printStackTrace();
+            return;
+        }
+        
+        System.out.println("Pick results:");
+        for (Iterator it = pickedElements.iterator(); it.hasNext();)
+        {
+            ArrayList path = (ArrayList)it.next();
+            
+            System.out.print("  Path: ");
+            
+            for (Iterator it2 = path.iterator(); it2.hasNext();)
+            {
+                SVGElement ele = (SVGElement)it2.next();
+
+                System.out.print("" + ele.getId() + "(" + ele.getClass().getName() + ") ");
+            }
+            System.out.println();
+        }
+    }//GEN-LAST:event_panel_svgAreaMouseReleased
+
+    private void panel_svgAreaMousePressed(java.awt.event.MouseEvent evt)//GEN-FIRST:event_panel_svgAreaMousePressed
+    {//GEN-HEADEREND:event_panel_svgAreaMousePressed
+
+    }//GEN-LAST:event_panel_svgAreaMousePressed
+
+    private void cmCheck_verboseActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_cmCheck_verboseActionPerformed
+    {//GEN-HEADEREND:event_cmCheck_verboseActionPerformed
+        SVGCache.getSVGUniverse().setVerbose(cmCheck_verbose.isSelected());
+    }//GEN-LAST:event_cmCheck_verboseActionPerformed
+
+    private void cm_aboutActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_cm_aboutActionPerformed
+    {//GEN-HEADEREND:event_cm_aboutActionPerformed
+        //JOptionPane.showMessageDialog(this, "Salamander SVG - Created by Mark McKay\nhttp://www.kitfox.com");
+        VersionDialog dlg = new VersionDialog(this, true, cmCheck_verbose.isSelected());
+        dlg.setVisible(true);
+    }//GEN-LAST:event_cm_aboutActionPerformed
+
+    private void cm_800x600ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cm_800x600ActionPerformed
+        setSize(800, 600);
+    }//GEN-LAST:event_cm_800x600ActionPerformed
+    
+    private void cm_loadFileActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_cm_loadFileActionPerformed
+    {//GEN-HEADEREND:event_cm_loadFileActionPerformed
+        try
+        {
+            int retVal = fileChooser.showOpenDialog(this);
+            if (retVal == JFileChooser.APPROVE_OPTION)
+            {
+                File chosenFile = fileChooser.getSelectedFile();
+
+                URL url = chosenFile.toURI().toURL();
+
+                loadURL(url);
+            }
+        }
+        /*
+        catch (IOException ioe)
+        {
+            try
+            {
+                //We may be in a WebStart app.  Try again with a FileOpenService
+                FileContents fc = fileOpenService.openFileDialog(null, new String[]{"svg"});
+                InputStream is = fc.getInputStream();
+                String name = fc.getName();
+            }
+            catch (Exception e)
+            {
+                e.printStackTrace();
+            }
+        }
+         */
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+
+    }//GEN-LAST:event_cm_loadFileActionPerformed
+
+    /** Exit the Application */
+    private void exitForm(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_exitForm
+//        setVisible(false);
+//        dispose();
+        System.exit(0);
+    }//GEN-LAST:event_exitForm
+
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String args[]) {
+        new SVGViewer().setVisible(true);
+    }
+
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JCheckBoxMenuItem CheckBoxMenuItem_anonInputStream;
+    private javax.swing.JCheckBoxMenuItem cmCheck_verbose;
+    private javax.swing.JMenuItem cm_800x600;
+    private javax.swing.JMenuItem cm_about;
+    private javax.swing.JMenuItem cm_loadFile;
+    private javax.swing.JMenuItem cm_loadUrl;
+    private javax.swing.JMenuBar jMenuBar1;
+    private javax.swing.JMenu menu_file;
+    private javax.swing.JMenu menu_help;
+    private javax.swing.JMenu menu_window;
+    private javax.swing.JPanel panel_svgArea;
+    private javax.swing.JScrollPane scrollPane_svgArea;
+    // End of variables declaration//GEN-END:variables
+
+}
Index: core/src/com/kitfox/svg/app/VersionDialog.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/app/VersionDialog.java	2012-06-17 23:56:57.752545052 +0200
@@ -0,0 +1,151 @@
+/*
+ * VersionDialog.java
+ *
+ *
+ *  The Salamander Project - 2D and 3D graphics libraries in Java
+ *  Copyright (C) 2004 Mark McKay
+ *
+ *  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
+ *
+ * Created on January 13, 2005, 7:23 AM
+ */
+
+package com.kitfox.svg.app;
+
+import java.net.*;
+import java.io.*;
+import java.util.*;
+import javax.swing.event.*;
+import javax.swing.*;
+import javax.swing.text.html.*;
+
+
+/**
+ *
+ * @author  kitfox
+ */
+public class VersionDialog extends javax.swing.JDialog
+{
+    public static final long serialVersionUID = 1;
+    
+    final boolean verbose;
+    
+    /** Creates new form VersionDialog */
+    public VersionDialog(java.awt.Frame parent, boolean modal, boolean verbose)
+    {
+        super(parent, modal);
+        initComponents();
+        
+        this.verbose = verbose;
+        
+        textpane_text.setContentType("text/html");
+        
+        StringBuffer sb = new StringBuffer();
+        try
+        {
+            URL url = getClass().getResource("/res/help/about/about.html");
+            if (verbose)
+            {
+                System.err.println("" + getClass() + " trying to load about html " + url);
+            }
+            
+            BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
+            while (true)
+            {
+                String line = reader.readLine();
+                if (line == null) break;
+                sb.append(line);
+            }
+            
+            textpane_text.setText(sb.toString());
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        
+    }
+    
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
+    private void initComponents()
+    {
+        jPanel1 = new javax.swing.JPanel();
+        textpane_text = new javax.swing.JTextPane();
+        jPanel2 = new javax.swing.JPanel();
+        bn_close = new javax.swing.JButton();
+
+        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
+        setTitle("About SVG Salamander");
+        jPanel1.setLayout(new java.awt.BorderLayout());
+
+        textpane_text.setEditable(false);
+        textpane_text.setPreferredSize(new java.awt.Dimension(400, 300));
+        jPanel1.add(textpane_text, java.awt.BorderLayout.CENTER);
+
+        getContentPane().add(jPanel1, java.awt.BorderLayout.CENTER);
+
+        bn_close.setText("Close");
+        bn_close.addActionListener(new java.awt.event.ActionListener()
+        {
+            public void actionPerformed(java.awt.event.ActionEvent evt)
+            {
+                bn_closeActionPerformed(evt);
+            }
+        });
+
+        jPanel2.add(bn_close);
+
+        getContentPane().add(jPanel2, java.awt.BorderLayout.SOUTH);
+
+        pack();
+    }
+    // </editor-fold>//GEN-END:initComponents
+
+    private void bn_closeActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_bn_closeActionPerformed
+    {//GEN-HEADEREND:event_bn_closeActionPerformed
+        setVisible(false);
+        dispose();
+    }//GEN-LAST:event_bn_closeActionPerformed
+    
+    /**
+     * @param args the command line arguments
+     */
+    public static void main(String args[])
+    {
+        java.awt.EventQueue.invokeLater(new Runnable()
+        {
+            public void run()
+            {
+                new VersionDialog(new javax.swing.JFrame(), true, true).setVisible(true);
+            }
+        });
+    }
+    
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JButton bn_close;
+    private javax.swing.JPanel jPanel1;
+    private javax.swing.JPanel jPanel2;
+    private javax.swing.JTextPane textpane_text;
+    // End of variables declaration//GEN-END:variables
+    
+}
Index: core/src/com/kitfox/svg/app/ant/SVGToImageAntTask.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/app/ant/SVGToImageAntTask.java	2012-06-17 23:56:57.752545052 +0200
@@ -0,0 +1,251 @@
+/*
+ * IndexLoadObjectsAntTask.java
+ *
+ * Created on January 22, 2005, 10:30 AM
+ */
+
+package com.kitfox.svg.app.ant;
+
+import java.awt.*;
+import java.awt.image.*;
+import java.util.*;
+import java.util.regex.*;
+import java.io.*;
+import javax.imageio.*;
+
+//import com.kitfox.util.*;
+//import com.kitfox.util.indexedObject.*;
+
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.types.*;
+
+import com.kitfox.svg.app.beans.*;
+import com.kitfox.svg.*;
+import com.kitfox.svg.xml.ColorTable;
+
+/**
+ * <p>Translates a group of SVG files into images.</p>
+ * 
+ * <p>Parameters:</p>
+ * <p><ul>
+ * <li/>destDir - If present, specifices a directory to write SVG files to.  Otherwise
+ * writes images to directory SVG file was found in
+ * verbose - If true, prints processing information to the console
+ * <li/>format - File format for output images.  The java core javax.imageio.ImageIO
+ * class is used for creating images, so format strings will depend on what
+ * files your system is configured to handle.  By default, "gif", "jpg" and "png"
+ * files are guaranteed to be present.  If omitted, "png" is used by default.
+ * <li/>backgroundColor - Optional background color.  Color can be specified as a standard 
+ * HTML color.  That is, as the name of a standard color such as "blue" or 
+ * "limegreen", using the # notaion as in #ff00ff for magenta, or in rgb format
+ * listing the components as in rgb(255, 192, 192) for pink.  If omitted,
+ * background is transparent.
+ * <li/>antiAlias - If set, shapes are drawn using antialiasing.  Defaults to true.
+ * <li/>interpolation - String describing image interpolation alrogithm.  Can
+ * be one of "nearest neighbor", "bilinear" or "bicubic".  Defaults to "bicubic".
+ * <li/>width - If greater than 0, determines the width of the written image.  Otherwise,
+ * the width is obtained from the SVG document.  Defaults to -1;
+ * <li/>height - If greater than 0, determines the height of the written image.  Otherwise,
+ * the height is obtained from the SVG document.  Defaults to -1.
+ * <li/>sizeToFit - If true and the width and height of the output image differ
+ * from that of the SVG image, the valid area of the SVG image will be resized 
+ * to fit the specified size.
+ * <li/>verbose - IF true, prints out diagnostic infromation about processing.  
+ * Defaults to false.
+ * </ul></p>
+ * 
+ * Example:
+ * &lt;SVGToImage destDir="${index.java}" format="jpg" verbose="true"&gt;
+ *    &lt;fileset dir="${dir1}"&gt;
+ *        &lt;include name="*.svg"/&gt;
+ *    &lt;/fileset&gt;
+ *    &lt;fileset dir="${dir2}"&gt;
+ *        &lt;include name="*.svg"/&gt;
+ *    &lt;/fileset&gt;
+ * &lt;/SVGToImage&gt;
+ * 
+ * 
+ * 
+ * @author kitfox
+ */
+public class SVGToImageAntTask extends Task
+{
+    private ArrayList filesets = new ArrayList();
+    boolean verbose = false;
+    File destDir;
+    private String format = "png";
+    Color backgroundColor = null;
+    int width = -1;
+    int height = -1;
+    boolean antiAlias = true;
+    String interpolation = "bicubic";
+    boolean clipToViewBox = false;
+    boolean sizeToFit = true;
+    
+    /** Creates a new instance of IndexLoadObjectsAntTask */
+    public SVGToImageAntTask()
+    {
+    }
+    
+    
+    public String getFormat()
+    {
+        return format;
+    }
+    
+    public void setFormat(String format)
+    {
+        this.format = format;
+    }
+    
+    public void setBackgroundColor(String bgColor)
+    {
+        this.backgroundColor = ColorTable.parseColor(bgColor);
+    }
+    
+    public void setHeight(int height)
+    {
+        this.height = height;
+    }
+    
+    public void setWidth(int width)
+    {
+        this.width = width;
+    }
+    
+    public void setAntiAlias(boolean antiAlias)
+    {
+        this.antiAlias = antiAlias;
+    }
+    
+    public void setInterpolation(String interpolation)
+    {
+        this.interpolation = interpolation;
+    }
+    
+    public void setSizeToFit(boolean sizeToFit)
+    {
+        this.sizeToFit = sizeToFit;
+    }
+    
+    public void setClipToViewBox(boolean clipToViewBox)
+    {
+        this.clipToViewBox = clipToViewBox;
+    }
+    
+    public void setVerbose(boolean verbose)
+    {
+        this.verbose = verbose;
+    }
+    
+    public void setDestDir(File destDir)
+    {
+        this.destDir = destDir;
+    }
+    
+    /**
+     * Adds a set of files.
+     */
+    public void addFileset(FileSet set)
+    {
+        filesets.add(set);
+    }
+    
+    
+    
+    public void execute()
+    {
+        if (verbose) log("Building SVG images");
+        
+        for (Iterator it = filesets.iterator(); it.hasNext();)
+        {
+            FileSet fs = (FileSet)it.next();
+            FileScanner scanner = fs.getDirectoryScanner(getProject());
+            String[] files = scanner.getIncludedFiles();
+            
+            try
+            {
+                File basedir = scanner.getBasedir();
+                
+                if (verbose) log("Scaning " + basedir);
+                
+                for (int i = 0; i < files.length; i++)
+                {
+//System.out.println("File " + files[i]);
+//System.out.println("BaseDir " + basedir);
+                    translate(basedir, files[i]);
+                }
+            }
+            catch (Exception e)
+            {
+                throw new BuildException(e);
+            }
+        }
+    }
+    
+    private void translate(File baseDir, String shortName) throws BuildException
+    {
+        File source = new File(baseDir, shortName);
+        
+        if (verbose) log("Reading file: " + source);
+        
+        Matcher matchName = Pattern.compile("(.*)\\.svg", Pattern.CASE_INSENSITIVE).matcher(shortName);
+        if (matchName.matches())
+        {
+            shortName = matchName.group(1);
+        }
+        shortName += "." + format;
+        
+        SVGIcon icon = new SVGIcon();
+        icon.setSvgURI(source.toURI());
+        icon.setAntiAlias(antiAlias);
+        if (interpolation.equals("nearest neighbor"))
+        {
+            icon.setInterpolation(SVGIcon.INTERP_NEAREST_NEIGHBOR);
+        }
+        else if (interpolation.equals("bilinear"))
+        {
+            icon.setInterpolation(SVGIcon.INTERP_BILINEAR);
+        }
+        else if (interpolation.equals("bicubic"))
+        {
+            icon.setInterpolation(SVGIcon.INTERP_BICUBIC);
+        }
+        
+        int iconWidth = width > 0 ? width : icon.getIconWidth();
+        int iconHeight = height > 0 ? height : icon.getIconHeight();
+        icon.setClipToViewbox(clipToViewBox);
+        icon.setPreferredSize(new Dimension(iconWidth, iconHeight));
+        icon.setScaleToFit(sizeToFit);
+        BufferedImage image = new BufferedImage(iconWidth, iconHeight, BufferedImage.TYPE_INT_ARGB);
+        Graphics2D g = image.createGraphics();
+        
+        if (backgroundColor != null)
+        {
+            g.setColor(backgroundColor);
+            g.fillRect(0, 0, iconWidth, iconHeight);
+        }
+        
+        g.setClip(0, 0, iconWidth, iconHeight);
+//        g.fillRect(10, 10, 100, 100);
+        icon.paintIcon(null, g, 0, 0);
+        g.dispose();
+        
+        File outFile = destDir == null ? new File(baseDir, shortName) : new File(destDir, shortName);
+        if (verbose) log("Writing file: " + outFile);
+        
+        try
+        {
+            ImageIO.write(image, format, outFile);
+        }
+        catch (IOException e)
+        {
+            log("Error writing image: " + e.getMessage());
+            throw new BuildException(e);
+        }
+        
+        
+        SVGCache.getSVGUniverse().clear();
+    }
+    
+}
Index: core/src/com/kitfox/svg/app/beans/ProportionalLayoutPanel.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/app/beans/ProportionalLayoutPanel.java	2012-06-17 23:56:57.784545052 +0200
@@ -0,0 +1,91 @@
+/*
+ * ProportionalLayoutPanel.java
+ *
+ * Created on May 7, 2005, 4:15 AM
+ */
+
+package com.kitfox.svg.app.beans;
+
+import java.awt.*;
+import java.util.*;
+import javax.swing.*;
+
+/**
+ * Panel based on the null layout.  Allows editing with absolute layout.  When
+ * instanced, records layout dimensions of all subcomponents.  Then, if the
+ * panel is ever resized, scales all children to fit new size.
+ *
+ * @author  kitfox
+ */
+public class ProportionalLayoutPanel extends javax.swing.JPanel
+{
+    public static final long serialVersionUID = 1;
+
+    //Margins to leave on sides of panel, expressed in fractions [0 1]
+    float topMargin;
+    float bottomMargin;
+    float leftMargin;
+    float rightMargin;
+    
+    /** Creates new form ProportionalLayoutPanel */
+    public ProportionalLayoutPanel()
+    {
+        initComponents();
+    }
+    
+    public void addNotify()
+    {
+        super.addNotify();
+        
+        Rectangle rect = this.getBounds();
+        JOptionPane.showMessageDialog(this, "" + rect);
+    }
+    
+    
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
+    private void initComponents()
+    {
+        jPanel1 = new javax.swing.JPanel();
+
+        setLayout(null);
+
+        addComponentListener(new java.awt.event.ComponentAdapter()
+        {
+            public void componentResized(java.awt.event.ComponentEvent evt)
+            {
+                formComponentResized(evt);
+            }
+            public void componentShown(java.awt.event.ComponentEvent evt)
+            {
+                formComponentShown(evt);
+            }
+        });
+
+        add(jPanel1);
+        jPanel1.setBounds(80, 90, 280, 160);
+
+    }
+    // </editor-fold>//GEN-END:initComponents
+
+    private void formComponentShown(java.awt.event.ComponentEvent evt)//GEN-FIRST:event_formComponentShown
+    {//GEN-HEADEREND:event_formComponentShown
+        JOptionPane.showMessageDialog(this, "" + getWidth() + ", " + getHeight());
+
+    }//GEN-LAST:event_formComponentShown
+
+    private void formComponentResized(java.awt.event.ComponentEvent evt)//GEN-FIRST:event_formComponentResized
+    {//GEN-HEADEREND:event_formComponentResized
+// TODO add your handling code here:
+    }//GEN-LAST:event_formComponentResized
+    
+    
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JPanel jPanel1;
+    // End of variables declaration//GEN-END:variables
+    
+}
Index: core/src/com/kitfox/svg/app/beans/SVGPanel.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/app/beans/SVGPanel.java	2012-06-17 23:56:57.784545052 +0200
@@ -0,0 +1,243 @@
+/*
+ * SVGIcon.java
+ *
+ * Created on April 21, 2005, 10:43 AM
+ */
+
+package com.kitfox.svg.app.beans;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.geom.*;
+import java.net.*;
+import java.beans.*;
+
+import com.kitfox.svg.*;
+
+/**
+ *
+ * @author  kitfox
+ */
+public class SVGPanel extends JPanel
+{
+    public static final long serialVersionUID = 1;
+
+
+    SVGUniverse svgUniverse = SVGCache.getSVGUniverse();
+    
+    private boolean antiAlias;
+    
+//    private String svgPath;
+    URI svgURI;
+
+    private boolean scaleToFit;
+    AffineTransform scaleXform = new AffineTransform();
+    
+    /** Creates new form SVGIcon */
+    public SVGPanel()
+    {
+        initComponents();
+    }
+        
+    public int getSVGHeight()
+    {
+        if (scaleToFit) return getPreferredSize().height;
+        
+        SVGDiagram diagram = svgUniverse.getDiagram(svgURI);
+        if (diagram == null) return 0;
+        return (int)diagram.getHeight();
+    }
+    
+    public int getSVGWidth()
+    {
+        if (scaleToFit) return getPreferredSize().width;
+        
+        SVGDiagram diagram = svgUniverse.getDiagram(svgURI);
+        if (diagram == null) return 0;
+        return (int)diagram.getWidth();
+    }
+    
+//          Draw the icon at the specified location.
+    public void paintComponent(Graphics gg)
+    {
+        super.paintComponent(gg);
+        
+        Graphics2D g = (Graphics2D)gg;
+        
+        Object oldAliasHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
+        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antiAlias ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
+
+        
+        SVGDiagram diagram = svgUniverse.getDiagram(svgURI);
+        if (diagram == null) return;
+        
+        if (!scaleToFit)
+        {
+            try
+            {
+                diagram.render(g);
+                g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldAliasHint);
+            }
+            catch (SVGException e)
+            {
+                throw new RuntimeException(e);
+            }
+            return;
+        }
+        
+        Dimension dim = getSize();
+        final int width = dim.width;
+        final int height = dim.height;
+//        int width = getWidth();
+//        int height = getHeight();
+        
+//        if (width == 0 || height == 0)
+//        {
+//           //Chances are we're rendering offscreen
+//            Dimension dim = getSize();
+//            width = dim.width;
+//            height = dim.height;
+//            return;
+//        }
+        
+//        g.setClip(0, 0, dim.width, dim.height);
+
+            
+        final Rectangle2D.Double rect = new Rectangle2D.Double();
+        diagram.getViewRect(rect);
+        
+        scaleXform.setToScale(width / rect.width, height / rect.height);
+        
+        AffineTransform oldXform = g.getTransform();
+        g.transform(scaleXform);
+
+        try
+        {
+            diagram.render(g);
+        }
+        catch (SVGException e)
+        {
+            throw new RuntimeException(e);
+        }
+        
+        g.setTransform(oldXform);
+        
+        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldAliasHint);
+    }
+    
+    public SVGUniverse getSvgUniverse()
+    {
+        return svgUniverse;
+    }
+
+    public void setSvgUniverse(SVGUniverse svgUniverse)
+    {
+        SVGUniverse old = this.svgUniverse;
+        this.svgUniverse = svgUniverse;
+        firePropertyChange("svgUniverse", old, svgUniverse);
+    }
+
+    public URI getSvgURI()
+    {
+        return svgURI;
+    }
+
+    public void setSvgURI(URI svgURI)
+    {
+        URI old = this.svgURI;
+        this.svgURI = svgURI;
+        firePropertyChange("svgURI", old, svgURI);
+    }
+    
+    /**
+     * Most resources your component will want to access will be resources on your classpath.  
+     * This method will interpret the passed string as a path in the classpath and use
+     * Class.getResource() to determine the URI of the SVG.
+     */
+    public void setSvgResourcePath(String resourcePath) throws SVGException
+    {
+        URI old = this.svgURI;
+        
+        try
+        {
+            svgURI = new URI(getClass().getResource(resourcePath).toString());
+//System.err.println("SVGPanel: new URI " + svgURI + " from path " + resourcePath);
+            
+            firePropertyChange("svgURI", old, svgURI);
+            
+            repaint();
+        }
+        catch (Exception e)
+        {
+            throw new SVGException("Could not resolve path " + resourcePath, e);
+//            svgURI = old;
+        }
+    }
+    
+    public boolean isScaleToFit()
+    {
+        return scaleToFit;
+    }
+
+    public void setScaleToFit(boolean scaleToFit)
+    {
+        boolean old = this.scaleToFit;
+        this.scaleToFit = scaleToFit;
+        firePropertyChange("scaleToFit", old, scaleToFit);
+    }
+    
+    /**
+     * @return true if antiAliasing is turned on.
+     * @deprecated
+     */
+    public boolean getUseAntiAlias()
+    {
+        return getAntiAlias();
+    }
+
+    /**
+     * @param antiAlias true to use antiAliasing.
+     * @deprecated
+     */
+    public void setUseAntiAlias(boolean antiAlias)
+    {
+        setAntiAlias(antiAlias);
+    }
+    
+    /**
+     * @return true if antiAliasing is turned on.
+     */
+    public boolean getAntiAlias()
+    {
+        return antiAlias;
+    }
+
+    /**
+     * @param antiAlias true to use antiAliasing.
+     */
+    public void setAntiAlias(boolean antiAlias)
+    {
+        boolean old = this.antiAlias;
+        this.antiAlias = antiAlias;
+        firePropertyChange("antiAlias", old, antiAlias);
+    }
+    
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    // <editor-fold defaultstate="collapsed" desc=" Generated Code ">//GEN-BEGIN:initComponents
+    private void initComponents()
+    {
+
+        setLayout(new java.awt.BorderLayout());
+
+    }
+    // </editor-fold>//GEN-END:initComponents
+    
+    
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    // End of variables declaration//GEN-END:variables
+    
+}
Index: core/src/com/kitfox/svg/app/data/HandlerFactory.java
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ core/src/com/kitfox/svg/app/data/HandlerFactory.java	2012-06-17 23:56:57.784545052 +0200
@@ -0,0 +1,27 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package com.kitfox.svg.app.data;
+
+import java.net.URLStreamHandler;
+import java.net.URLStreamHandlerFactory;
+
+/**
+ *
+ * @author kitfox
+ */
+public class HandlerFactory implements URLStreamHandlerFactory
+{
+    static Handler handler = new Handler();
+
+    public URLStreamHandler createURLStreamHandler(String protocol)
+    {
+        if ("data".equals(protocol))
+        {
+            return handler;
+        }
+        return null;
+    }
+}
