diff --git a/svg-core/src/main/java/com/kitfox/svg/SVGAnimatedCache.java b/svg-core/src/main/java/com/kitfox/svg/SVGAnimatedCache.java
new file mode 100644
index 0000000..c571e63
--- /dev/null
+++ b/svg-core/src/main/java/com/kitfox/svg/SVGAnimatedCache.java
@@ -0,0 +1,49 @@
+package com.kitfox.svg;
+
+public class SVGAnimatedCache {
+ private static SVGAnimatedCache INSTANCE;
+ private static final SVGUniverse svgUniverse = new SVGUniverse();
+ private static double svgTimer = 0d, svgStep = 0.05d;
+ private static final Thread svgThreadTimer = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ svgTimer += svgStep;
+ try {
+ svgUniverse.setCurTime(svgTimer);
+ svgUniverse.updateTime();
+ } catch (SVGException e1) {
+ e1.printStackTrace();
+ }
+
+ try {
+ Thread.sleep((long) (svgStep * 1000));
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+
+ private SVGAnimatedCache() {
+ }
+
+ public static SVGUniverse getSVGUniverse() {
+ return svgUniverse;
+ }
+
+ public static void startAnimation(){
+ if(!svgThreadTimer.isAlive())
+ svgThreadTimer.start();
+ }
+
+ public static void stopAnimation(){
+ svgTimer = 0d;
+ svgThreadTimer.interrupt();
+ }
+
+ public static SVGAnimatedCache getInstance() {
+ if(INSTANCE == null){
+ INSTANCE = new SVGAnimatedCache();
+ }
+ return INSTANCE;
+ }
+}
diff --git a/svg-core/src/main/java/com/kitfox/svg/SVGDiagram.java b/svg-core/src/main/java/com/kitfox/svg/SVGDiagram.java
index 873efd1..b5901ff 100644
--- a/svg-core/src/main/java/com/kitfox/svg/SVGDiagram.java
+++ b/svg-core/src/main/java/com/kitfox/svg/SVGDiagram.java
@@ -1,272 +1,272 @@
-/*
- * SVG Salamander
- * Copyright (c) 2004, Mark McKay
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer.
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials
- * provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * Mark McKay can be contacted at mark@kitfox.com. Salamander and other
- * projects can be found at http://www.kitfox.com
- *
- * Created on February 18, 2004, 5:04 PM
- */
-
-package com.kitfox.svg;
-
-import java.awt.Graphics2D;
-import java.awt.Rectangle;
-import java.awt.geom.AffineTransform;
-import java.awt.geom.Point2D;
-import java.awt.geom.Rectangle2D;
-import java.io.Serializable;
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-
-/**
- * Top level structure in an SVG tree.
- *
- * @author Mark McKay
- * @author Mark McKay
- */
-public class SVGDiagram implements Serializable
-{
- public static final long serialVersionUID = 0;
-
- //Indexes elements within this SVG diagram
- final HashMap idMap = new HashMap();
-
- SVGRoot root;
- final SVGUniverse universe;
-
- /**
- * This is used by the SVGRoot to determine the width of the
- */
- private Rectangle deviceViewport = new Rectangle(100, 100);
-
- /**
- * If true, no attempt will be made to discard geometry based on it being
- * out of bounds. This trades potentially drawing many out of bounds
- * shapes with having to recalculate bounding boxes every animation iteration.
- */
- protected boolean ignoreClipHeuristic = false;
-
- /**
- * URL which uniquely identifies this document
- */
-// final URI docRoot;
-
- /**
- * URI that uniquely identifies this document. Also used to resolve
- * relative urls. Default base for document.
- */
- final URI xmlBase;
-
- /**
- * Creates a new instance of SVGDiagram
- * @param xmlBase
- * @param universe
- */
- public SVGDiagram(URI xmlBase, SVGUniverse universe)
- {
- this.universe = universe;
-// this.docRoot = docRoot;
- this.xmlBase = xmlBase;
- }
-
- /**
- * Draws this diagram to the passed graphics context
- * @param g
- * @throws com.kitfox.svg.SVGException
- */
- public void render(Graphics2D g) throws SVGException
- {
- root.renderToViewport(g);
- }
-
- /**
- * Searches thorough the scene graph for all RenderableElements that have
- * shapes that contain the passed point.
- *
- * For every shape which contains the pick point, a List containing the
- * path to the node is added to the return list. That is, the result of
- * SVGElement.getPath() is added for each entry.
- *
- * @param point
- * @param retVec
- * @return the passed in list
- * @throws com.kitfox.svg.SVGException
- */
- public List> pick(Point2D point, List> retVec) throws SVGException
- {
- return pick(point, false, retVec);
- }
-
- public List> pick(Point2D point, boolean boundingBox, List> retVec) throws SVGException
- {
- if (retVec == null)
- {
- retVec = new ArrayList>();
- }
-
- root.pick(point, boundingBox, retVec);
-
- return retVec;
- }
-
- public List> pick(Rectangle2D pickArea, List> retVec) throws SVGException
- {
- return pick(pickArea, false, retVec);
- }
-
- public List> pick(Rectangle2D pickArea, boolean boundingBox, List> retVec) throws SVGException
- {
- if (retVec == null)
- {
- retVec = new ArrayList>();
- }
-
- root.pick(pickArea, new AffineTransform(), boundingBox, retVec);
-
- return retVec;
- }
-
- public SVGUniverse getUniverse()
- {
- return universe;
- }
-
- public URI getXMLBase()
- {
- return xmlBase;
- }
-
-// public URL getDocRoot()
-// {
-// return docRoot;
-// }
-
- public float getWidth()
- {
- if (root == null) return 0;
- return root.getDeviceWidth();
- }
-
- public float getHeight()
- {
- if (root == null) return 0;
- return root.getDeviceHeight();
- }
-
- /**
- * Returns the viewing rectangle of this diagram in device coordinates.
- * @param rect
- * @return
- */
- public Rectangle2D getViewRect(Rectangle2D rect)
- {
- if (root != null) return root.getDeviceRect(rect);
- return rect;
- }
-
- public Rectangle2D getViewRect()
- {
- return getViewRect(new Rectangle2D.Double());
- }
-
- public SVGElement getElement(String name)
- {
- return (SVGElement)idMap.get(name);
- }
-
- public void setElement(String name, SVGElement node)
- {
- idMap.put(name, node);
- }
-
- public void removeElement(String name)
- {
- idMap.remove(name);
- }
-
- public SVGRoot getRoot()
- {
- return root;
- }
-
- public void setRoot(SVGRoot root)
- {
- this.root = root;
- root.setDiagram(this);
- }
-
- public boolean ignoringClipHeuristic() { return ignoreClipHeuristic; }
-
- public void setIgnoringClipHeuristic(boolean ignoreClipHeuristic) { this.ignoreClipHeuristic = ignoreClipHeuristic; }
-
- /**
- * Updates all attributes in this diagram associated with a time event.
- * Ie, all attributes with track information.
- * @param curTime
- * @throws com.kitfox.svg.SVGException
- */
- public void updateTime(double curTime) throws SVGException
- {
- if (root == null) return;
- root.updateTime(curTime);
- }
-
- public Rectangle getDeviceViewport()
- {
- return deviceViewport;
- }
-
- /**
- * Sets the dimensions of the device being rendered into. This is used by
- * SVGRoot when its x, y, width or height parameters are specified as
- * percentages.
- * @param deviceViewport
- */
- public void setDeviceViewport(Rectangle deviceViewport)
- {
- this.deviceViewport.setBounds(deviceViewport);
- if (root != null)
- {
- try
- {
- root.build();
- } catch (SVGException ex)
- {
- Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING,
- "Could not build document", ex);
- }
- }
- }
-}
+/*
+ * SVG Salamander
+ * Copyright (c) 2004, Mark McKay
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Mark McKay can be contacted at mark@kitfox.com. Salamander and other
+ * projects can be found at http://www.kitfox.com
+ *
+ * Created on February 18, 2004, 5:04 PM
+ */
+
+package com.kitfox.svg;
+
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.io.Serializable;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+
+/**
+ * Top level structure in an SVG tree.
+ *
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class SVGDiagram implements Serializable
+{
+ public static final long serialVersionUID = 0;
+
+ //Indexes elements within this SVG diagram
+ final HashMap idMap = new HashMap();
+
+ SVGRoot root;
+ final SVGUniverse universe;
+
+ /**
+ * This is used by the SVGRoot to determine the width of the
+ */
+ private Rectangle deviceViewport = new Rectangle(100, 100);
+
+ /**
+ * If true, no attempt will be made to discard geometry based on it being
+ * out of bounds. This trades potentially drawing many out of bounds
+ * shapes with having to recalculate bounding boxes every animation iteration.
+ */
+ protected boolean ignoreClipHeuristic = false;
+
+ /**
+ * URL which uniquely identifies this document
+ */
+// final URI docRoot;
+
+ /**
+ * URI that uniquely identifies this document. Also used to resolve
+ * relative urls. Default base for document.
+ */
+ final URI xmlBase;
+
+ /**
+ * Creates a new instance of SVGDiagram
+ * @param xmlBase
+ * @param universe
+ */
+ public SVGDiagram(URI xmlBase, SVGUniverse universe)
+ {
+ this.universe = universe;
+// this.docRoot = docRoot;
+ this.xmlBase = xmlBase;
+ }
+
+ /**
+ * Draws this diagram to the passed graphics context
+ * @param g
+ * @throws com.kitfox.svg.SVGException
+ */
+ public void render(Graphics2D g) throws SVGException
+ {
+ root.renderToViewport(g);
+ }
+
+ /**
+ * Searches thorough the scene graph for all RenderableElements that have
+ * shapes that contain the passed point.
+ *
+ * For every shape which contains the pick point, a List containing the
+ * path to the node is added to the return list. That is, the result of
+ * SVGElement.getPath() is added for each entry.
+ *
+ * @param point
+ * @param retVec
+ * @return the passed in list
+ * @throws com.kitfox.svg.SVGException
+ */
+ public List> pick(Point2D point, List> retVec) throws SVGException
+ {
+ return pick(point, false, retVec);
+ }
+
+ public List> pick(Point2D point, boolean boundingBox, List> retVec) throws SVGException
+ {
+ if (retVec == null)
+ {
+ retVec = new ArrayList>();
+ }
+
+ root.pick(point, boundingBox, retVec);
+
+ return retVec;
+ }
+
+ public List> pick(Rectangle2D pickArea, List> retVec) throws SVGException
+ {
+ return pick(pickArea, false, retVec);
+ }
+
+ public List> pick(Rectangle2D pickArea, boolean boundingBox, List> retVec) throws SVGException
+ {
+ if (retVec == null)
+ {
+ retVec = new ArrayList>();
+ }
+
+ root.pick(pickArea, new AffineTransform(), boundingBox, retVec);
+
+ return retVec;
+ }
+
+ public SVGUniverse getUniverse()
+ {
+ return universe;
+ }
+
+ public URI getXMLBase()
+ {
+ return xmlBase;
+ }
+
+// public URL getDocRoot()
+// {
+// return docRoot;
+// }
+
+ public float getWidth()
+ {
+ if (root == null) return 0;
+ return root.getDeviceWidth();
+ }
+
+ public float getHeight()
+ {
+ if (root == null) return 0;
+ return root.getDeviceHeight();
+ }
+
+ /**
+ * Returns the viewing rectangle of this diagram in device coordinates.
+ * @param rect
+ * @return
+ */
+ public Rectangle2D getViewRect(Rectangle2D rect)
+ {
+ if (root != null) return root.getDeviceRect(rect);
+ return rect;
+ }
+
+ public Rectangle2D getViewRect()
+ {
+ return getViewRect(new Rectangle2D.Double());
+ }
+
+ public SVGElement getElement(String name)
+ {
+ return (SVGElement)idMap.get(name);
+ }
+
+ public void setElement(String name, SVGElement node)
+ {
+ idMap.put(name, node);
+ }
+
+ public void removeElement(String name)
+ {
+ idMap.remove(name);
+ }
+
+ public SVGRoot getRoot()
+ {
+ return root;
+ }
+
+ public void setRoot(SVGRoot root)
+ {
+ this.root = root;
+ root.setDiagram(this);
+ }
+
+ public boolean ignoringClipHeuristic() { return ignoreClipHeuristic; }
+
+ public void setIgnoringClipHeuristic(boolean ignoreClipHeuristic) { this.ignoreClipHeuristic = ignoreClipHeuristic; }
+
+ /**
+ * Updates all attributes in this diagram associated with a time event.
+ * Ie, all attributes with track information.
+ * @param curTime
+ * @throws com.kitfox.svg.SVGException
+ */
+ public void updateTime(double curTime) throws SVGException
+ {
+ if (root == null) return;
+ root.updateTime(curTime);
+ }
+
+ public Rectangle getDeviceViewport()
+ {
+ return deviceViewport;
+ }
+
+ /**
+ * Sets the dimensions of the device being rendered into. This is used by
+ * SVGRoot when its x, y, width or height parameters are specified as
+ * percentages.
+ * @param deviceViewport
+ */
+ public void setDeviceViewport(Rectangle deviceViewport)
+ {
+ this.deviceViewport.setBounds(deviceViewport);
+ if (root != null)
+ {
+ try
+ {
+ root.build();
+ } catch (SVGException ex)
+ {
+ Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING,
+ "Could not build document", ex);
+ }
+ }
+ }
+}
diff --git a/svg-core/src/main/java/com/kitfox/svg/app/SVGAnimated.java b/svg-core/src/main/java/com/kitfox/svg/app/SVGAnimated.java
new file mode 100644
index 0000000..88b75e1
--- /dev/null
+++ b/svg-core/src/main/java/com/kitfox/svg/app/SVGAnimated.java
@@ -0,0 +1,246 @@
+package com.kitfox.svg.app;
+
+import com.kitfox.svg.*;
+import com.kitfox.svg.animation.AnimationElement;
+import com.kitfox.svg.app.beans.SVGIcon;
+
+import java.awt.*;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+import java.net.URI;
+
+public class SVGAnimated {
+ public static final int AUTOSIZE_NONE = 0;
+ public static final int AUTOSIZE_HORIZ = 1;
+ public static final int AUTOSIZE_VERT = 2;
+ public static final int AUTOSIZE_BESTFIT = 3;
+ public static final int AUTOSIZE_STRETCH = 4;
+
+ public static final int INTERP_NEAREST_NEIGHBOR = 0;
+ public static final int INTERP_BILINEAR = 1;
+ public static final int INTERP_BICUBIC = 2;
+
+ private boolean antiAlias = true;
+ private int interpolation = INTERP_BILINEAR;
+ private boolean clipToViewbox = false;
+ private int autosize = AUTOSIZE_BESTFIT;
+
+ private SVGAnimatedCache svgCache = SVGAnimatedCache.getInstance(); //new SVGAnimatedCache(); //SVGCache.getSVGUniverse();
+ private SVGDiagram diagram;
+
+ private Dimension preferredSize;
+ private AffineTransform scaleSVG = new AffineTransform();
+
+ private Component parent;
+ private boolean isAnimationRunning = false;
+
+ public SVGAnimated(String svg, Component parent) {
+ this.parent = parent;
+ try {
+ URI svgURI = svgCache.getSVGUniverse().loadSVG(getClass().getResourceAsStream(svg), svg);
+ diagram = svgCache.getSVGUniverse().getDiagram(svgURI);
+ diagram.setParentComponent(parent);
+
+ if (diagram != null) {
+ Dimension size = getPreferredSize();
+ if (size == null) {
+ size = new Dimension((int) diagram.getRoot().getDeviceWidth(), (int) diagram.getRoot().getDeviceHeight());
+ }
+ diagram.setDeviceViewport(new Rectangle(0, 0, size.width, size.height));
+ }
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public Dimension getPreferredSize() {
+ if (preferredSize == null) {
+ setPreferredSize(new Dimension((int) diagram.getWidth(), (int) diagram.getHeight()));
+ }
+
+ return new Dimension(preferredSize);
+ }
+
+ public void setPreferredSize(Dimension preferredSize) {
+ Dimension old = this.preferredSize;
+ this.preferredSize = preferredSize;
+
+ diagram.setDeviceViewport(new Rectangle(0, 0, preferredSize.width, preferredSize.height));
+ calculateScale();
+// changes.firePropertyChange("preferredSize", old, preferredSize);
+ }
+
+ public int getIconWidthIgnoreAutosize() {
+ if (preferredSize != null &&
+ (autosize == AUTOSIZE_HORIZ || autosize == AUTOSIZE_STRETCH
+ || autosize == AUTOSIZE_BESTFIT)) {
+ return preferredSize.width;
+ }
+
+ if (diagram == null) {
+ return 0;
+ }
+ return (int) diagram.getWidth();
+ }
+
+ public int getIconHeightIgnoreAutosize() {
+ if (preferredSize != null &&
+ (autosize == AUTOSIZE_VERT || autosize == AUTOSIZE_STRETCH
+ || autosize == AUTOSIZE_BESTFIT)) {
+ return preferredSize.height;
+ }
+
+ if (diagram == null) {
+ return 0;
+ }
+ return (int) diagram.getHeight();
+ }
+
+ private void calculateScale() {
+ final int width = getIconWidthIgnoreAutosize();
+ final int height = getIconHeightIgnoreAutosize();
+
+ if (width == 0 || height == 0) {
+ scaleSVG.setToScale(1, 1);
+ return;
+ }
+
+ double diaWidth = diagram.getWidth();
+ double diaHeight = diagram.getHeight();
+
+ double scaleW = 1;
+ double scaleH = 1;
+ switch (autosize){
+ case AUTOSIZE_BESTFIT:
+ scaleW = scaleH = (height / diaHeight < width / diaWidth)
+ ? height / diaHeight : width / diaWidth;
+ break;
+ case AUTOSIZE_HORIZ:
+ scaleW = scaleH = width / diaWidth;
+ break;
+ case AUTOSIZE_VERT:
+ scaleW = scaleH = height / diaHeight;
+ break;
+ case AUTOSIZE_STRETCH:
+ scaleW = width / diaWidth;
+ scaleH = height / diaHeight;
+ break;
+ default: break;
+ }
+ scaleSVG.setToScale(scaleW, scaleH);
+ }
+
+ public void render(Graphics2D g, int x, int y) {
+ if(!isAnimationRunning) return;
+ diagram.setRepaintParentComponent(parent, x, y, preferredSize.width, preferredSize.height);
+
+ Object oldAliasHint = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antiAlias ? RenderingHints.VALUE_ANTIALIAS_ON : RenderingHints.VALUE_ANTIALIAS_OFF);
+
+ Object oldInterpolationHint = g.getRenderingHint(RenderingHints.KEY_INTERPOLATION);
+ switch (interpolation) {
+ case SVGIcon.INTERP_NEAREST_NEIGHBOR:
+ g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
+ break;
+ case SVGIcon.INTERP_BILINEAR:
+ g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+ break;
+ case SVGIcon.INTERP_BICUBIC:
+ g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
+ break;
+ }
+
+ if (diagram == null) {
+ System.err.println("No diagram");
+ return;
+ }
+
+ g.translate(x, y);
+ diagram.setIgnoringClipHeuristic(!clipToViewbox);
+ if (clipToViewbox) {
+ g.setClip(new Rectangle2D.Float(0, 0, diagram.getWidth(), diagram.getHeight()));
+ }
+
+ AffineTransform oldXform = g.getTransform();
+ g.transform(scaleSVG);
+
+ try {
+ diagram.render(g);
+ } catch (SVGException e) {
+ throw new RuntimeException(e);
+ }
+
+ g.setTransform(oldXform);
+ g.translate(-x, -y);
+
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, oldAliasHint);
+ g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, oldInterpolationHint);
+ }
+
+ public void startAnimation() {
+ isAnimationRunning = true;
+ svgCache.startAnimation();
+ }
+
+ public void stopAnimation(){
+ isAnimationRunning = false;
+ svgCache.stopAnimation();
+ }
+
+ public void setColor(Color color) {
+ String hexColor = String.format("#%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue());
+ if(diagram != null){
+ for(SVGElement elem : diagram.getRoot().getChildren(null)){
+ try {
+ setColor(hexColor, elem);
+ } catch (SVGElementException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private void setColor(String color, SVGElement element) throws SVGElementException {
+ if(element.hasAttribute("fill", AnimationElement.AT_AUTO)){
+ element.setAttribute("fill", AnimationElement.AT_AUTO, color);
+ }
+
+ for(SVGElement elem : element.getChildren(null)){
+ setColor(color, elem);
+ }
+ }
+
+ public boolean isAntiAlias() {
+ return antiAlias;
+ }
+
+ public void setAntiAlias(boolean antiAlias) {
+ this.antiAlias = antiAlias;
+ }
+
+ public int getInterpolation() {
+ return interpolation;
+ }
+
+ public void setInterpolation(int interpolation) {
+ this.interpolation = interpolation;
+ }
+
+ public boolean isClipToViewbox() {
+ return clipToViewbox;
+ }
+
+ public void setClipToViewbox(boolean clipToViewbox) {
+ this.clipToViewbox = clipToViewbox;
+ }
+
+ public int getAutosize() {
+ return autosize;
+ }
+
+ public void setAutosize(int autosize) {
+ this.autosize = autosize;
+ }
+}
diff --git a/svg-core/src/main/java/com/kitfox/svg/xml/XMLParseUtil.java b/svg-core/src/main/java/com/kitfox/svg/xml/XMLParseUtil.java
index 4f1a1df..1d3367d 100644
--- a/svg-core/src/main/java/com/kitfox/svg/xml/XMLParseUtil.java
+++ b/svg-core/src/main/java/com/kitfox/svg/xml/XMLParseUtil.java
@@ -1,833 +1,833 @@
-/*
- * SVG Salamander
- * Copyright (c) 2004, Mark McKay
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or
- * without modification, are permitted provided that the following
- * conditions are met:
- *
- * - Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer.
- * - Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials
- * provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * Mark McKay can be contacted at mark@kitfox.com. Salamander and other
- * projects can be found at http://www.kitfox.com
- *
- * Created on February 18, 2004, 1:49 PM
- */
-
-package com.kitfox.svg.xml;
-
-import com.kitfox.svg.SVGConst;
-import org.w3c.dom.*;
-import java.awt.*;
-import java.net.*;
-import java.util.*;
-import java.util.regex.*;
-import java.lang.reflect.*;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * @author Mark McKay
- * @author Mark McKay
- */
-public class XMLParseUtil
-{
- static final Matcher fpMatch = Pattern.compile("([-+]?((\\d*\\.\\d+)|(\\d+))([eE][+-]?\\d+)?)(\\%|in|cm|mm|pt|pc|px|em|ex)?").matcher("");
- static final Matcher intMatch = Pattern.compile("[-+]?\\d+").matcher("");
- static final Matcher quoteMatch = Pattern.compile("^'|'$").matcher("");
-
- /** Creates a new instance of XMLParseUtil */
- private XMLParseUtil()
- {
- }
-
- /**
- * Scans the tag's children and returns the first text element found
- */
- public static String getTagText(Element ele)
- {
- NodeList nl = ele.getChildNodes();
- int size = nl.getLength();
-
- Node node = null;
- int i = 0;
- for (; i < size; i++)
- {
- node = nl.item(i);
- if (node instanceof Text) break;
- }
- if (i == size || node == null) return null;
-
- return ((Text)node).getData();
- }
-
- /**
- * Returns the first node that is a direct child of root with the coresponding
- * name. Does not search children of children.
- */
- public static Element getFirstChild(Element root, String name)
- {
- NodeList nl = root.getChildNodes();
- int size = nl.getLength();
- for (int i = 0; i < size; i++)
- {
- Node node = nl.item(i);
- if (!(node instanceof Element)) continue;
- Element ele = (Element)node;
- if (ele.getTagName().equals(name)) return ele;
- }
-
- return null;
- }
-
- public static String[] parseStringList(String list)
- {
-// final Pattern patWs = Pattern.compile("\\s+");
- final Matcher matchWs = Pattern.compile("[^\\s]+").matcher("");
- matchWs.reset(list);
-
- LinkedList matchList = new LinkedList();
- while (matchWs.find())
- {
- matchList.add(matchWs.group());
- }
-
- String[] retArr = new String[matchList.size()];
- return (String[])matchList.toArray(retArr);
- }
-
- public static boolean isDouble(String val)
- {
- fpMatch.reset(val);
- return fpMatch.matches();
- }
-
- public static double parseDouble(String val)
- {
- /*
- if (val == null) return 0.0;
-
- double retVal = 0.0;
- try
- { retVal = Double.parseDouble(val); }
- catch (Exception e)
- {}
- return retVal;
- */
- return findDouble(val);
- }
-
- /**
- * Searches the given string for the first floating point number it contains,
- * parses and returns it.
- */
- public synchronized static double findDouble(String val)
- {
- if (val == null) return 0;
-
- fpMatch.reset(val);
- try
- {
- if (!fpMatch.find()) return 0;
- }
- catch (StringIndexOutOfBoundsException e)
- {
- Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING,
- "XMLParseUtil: regex parse problem: '" + val + "'", e);
- }
-
- val = fpMatch.group(1);
- //System.err.println("Parsing " + val);
-
- double retVal = 0;
- try
- {
- retVal = Double.parseDouble(val);
-
- float pixPerInch;
- try {
- pixPerInch = (float)Toolkit.getDefaultToolkit().getScreenResolution();
- }
- catch (NoClassDefFoundError err)
- {
- //Default value for headless X servers
- pixPerInch = 72;
- }
- final float inchesPerCm = .3936f;
- final String units = fpMatch.group(6);
-
- if ("%".equals(units)) retVal /= 100;
- else if ("in".equals(units))
- {
- retVal *= pixPerInch;
- }
- else if ("cm".equals(units))
- {
- retVal *= inchesPerCm * pixPerInch;
- }
- else if ("mm".equals(units))
- {
- retVal *= inchesPerCm * pixPerInch * .1f;
- }
- else if ("pt".equals(units))
- {
- retVal *= (1f / 72f) * pixPerInch;
- }
- else if ("pc".equals(units))
- {
- retVal *= (1f / 6f) * pixPerInch;
- }
- }
- catch (Exception e)
- {}
- return retVal;
- }
-
- /**
- * Scans an input string for double values. For each value found, places
- * in a list. This method regards any characters not part of a floating
- * point value to be seperators. Thus this will parse whitespace seperated,
- * comma seperated, and many other separation schemes correctly.
- */
- public synchronized static double[] parseDoubleList(String list)
- {
- if (list == null) return null;
-
- fpMatch.reset(list);
-
- LinkedList doubList = new LinkedList();
- while (fpMatch.find())
- {
- String val = fpMatch.group(1);
- doubList.add(Double.valueOf(val));
- }
-
- double[] retArr = new double[doubList.size()];
- Iterator it = doubList.iterator();
- int idx = 0;
- while (it.hasNext())
- {
- retArr[idx++] = ((Double)it.next()).doubleValue();
- }
-
- return retArr;
- }
-
- public static float parseFloat(String val)
- {
- /*
- if (val == null) return 0f;
-
- float retVal = 0f;
- try
- { retVal = Float.parseFloat(val); }
- catch (Exception e)
- {}
- return retVal;
- */
- return findFloat(val);
- }
-
- /**
- * Searches the given string for the first floating point number it contains,
- * parses and returns it.
- */
- public synchronized static float findFloat(String val)
- {
- if (val == null) return 0f;
-
- fpMatch.reset(val);
- if (!fpMatch.find()) return 0f;
-
- val = fpMatch.group(1);
- //System.err.println("Parsing " + val);
-
- float retVal = 0f;
- try
- {
- retVal = Float.parseFloat(val);
- String units = fpMatch.group(6);
- if ("%".equals(units)) retVal /= 100;
- }
- catch (Exception e)
- {}
- return retVal;
- }
-
- public synchronized static float[] parseFloatList(String list)
- {
- if (list == null) return null;
-
- fpMatch.reset(list);
-
- LinkedList floatList = new LinkedList();
- while (fpMatch.find())
- {
- String val = fpMatch.group(1);
- floatList.add(Float.valueOf(val));
- }
-
- float[] retArr = new float[floatList.size()];
- Iterator it = floatList.iterator();
- int idx = 0;
- while (it.hasNext())
- {
- retArr[idx++] = ((Float)it.next()).floatValue();
- }
-
- return retArr;
- }
-
- public static int parseInt(String val)
- {
- if (val == null) return 0;
-
- int retVal = 0;
- try
- { retVal = Integer.parseInt(val); }
- catch (Exception e)
- {}
- return retVal;
- }
-
- /**
- * Searches the given string for the first integer point number it contains,
- * parses and returns it.
- */
- public static int findInt(String val)
- {
- if (val == null) return 0;
-
- intMatch.reset(val);
- if (!intMatch.find()) return 0;
-
- val = intMatch.group();
- //System.err.println("Parsing " + val);
-
- int retVal = 0;
- try
- { retVal = Integer.parseInt(val); }
- catch (Exception e)
- {}
- return retVal;
- }
-
- public static int[] parseIntList(String list)
- {
- if (list == null) return null;
-
- intMatch.reset(list);
-
- LinkedList intList = new LinkedList();
- while (intMatch.find())
- {
- String val = intMatch.group();
- intList.add(Integer.valueOf(val));
- }
-
- int[] retArr = new int[intList.size()];
- Iterator it = intList.iterator();
- int idx = 0;
- while (it.hasNext())
- {
- retArr[idx++] = ((Integer)it.next()).intValue();
- }
-
- return retArr;
- }
-/*
- public static int parseHex(String val)
- {
- int retVal = 0;
-
- for (int i = 0; i < val.length(); i++)
- {
- retVal <<= 4;
-
- char ch = val.charAt(i);
- if (ch >= '0' && ch <= '9')
- {
- retVal |= ch - '0';
- }
- else if (ch >= 'a' && ch <= 'z')
- {
- retVal |= ch - 'a' + 10;
- }
- else if (ch >= 'A' && ch <= 'Z')
- {
- retVal |= ch - 'A' + 10;
- }
- else throw new RuntimeException();
- }
-
- return retVal;
- }
-*/
- /**
- * The input string represents a ratio. Can either be specified as a
- * double number on the range of [0.0 1.0] or as a percentage [0% 100%]
- */
- public static double parseRatio(String val)
- {
- if (val == null || val.equals("")) return 0.0;
-
- if (val.charAt(val.length() - 1) == '%')
- {
- parseDouble(val.substring(0, val.length() - 1));
- }
- return parseDouble(val);
- }
-
- public static NumberWithUnits parseNumberWithUnits(String val)
- {
- if (val == null) return null;
-
- return new NumberWithUnits(val);
- }
-/*
- public static Color parseColor(String val)
- {
- Color retVal = null;
-
- if (val.charAt(0) == '#')
- {
- String hexStrn = val.substring(1);
-
- if (hexStrn.length() == 3)
- {
- hexStrn = "" + hexStrn.charAt(0) + hexStrn.charAt(0) + hexStrn.charAt(1) + hexStrn.charAt(1) + hexStrn.charAt(2) + hexStrn.charAt(2);
- }
- int hexVal = parseHex(hexStrn);
-
- retVal = new Color(hexVal);
- }
- else
- {
- final Matcher rgbMatch = Pattern.compile("rgb\\((\\d+),(\\d+),(\\d+)\\)", Pattern.CASE_INSENSITIVE).matcher("");
-
- rgbMatch.reset(val);
- if (rgbMatch.matches())
- {
- int r = Integer.parseInt(rgbMatch.group(1));
- int g = Integer.parseInt(rgbMatch.group(2));
- int b = Integer.parseInt(rgbMatch.group(3));
- retVal = new Color(r, g, b);
- }
- else
- {
- Color lookupCol = ColorTable.instance().lookupColor(val);
- if (lookupCol != null) retVal = lookupCol;
- }
- }
-
- return retVal;
- }
-*/
- /**
- * Parses the given attribute of this tag and returns it as a String.
- */
- public static String getAttribString(Element ele, String name)
- {
- return ele.getAttribute(name);
- }
-
- /**
- * Parses the given attribute of this tag and returns it as an int.
- */
- public static int getAttribInt(Element ele, String name)
- {
- String sval = ele.getAttribute(name);
- int val = 0;
- try { val = Integer.parseInt(sval); } catch (Exception e) {}
-
- return val;
- }
-
- /**
- * Parses the given attribute of this tag as a hexadecimal encoded string and
- * returns it as an int
- */
- public static int getAttribIntHex(Element ele, String name)
- {
- String sval = ele.getAttribute(name);
- int val = 0;
- try { val = Integer.parseInt(sval, 16); } catch (Exception e) {}
-
- return val;
- }
-
- /**
- * Parses the given attribute of this tag and returns it as a float
- */
- public static float getAttribFloat(Element ele, String name)
- {
- String sval = ele.getAttribute(name);
- float val = 0.0f;
- try { val = Float.parseFloat(sval); } catch (Exception e) {}
-
- return val;
- }
-
- /**
- * Parses the given attribute of this tag and returns it as a double.
- */
- public static double getAttribDouble(Element ele, String name)
- {
- String sval = ele.getAttribute(name);
- double val = 0.0;
- try { val = Double.parseDouble(sval); } catch (Exception e) {}
-
- return val;
- }
-
- /**
- * Parses the given attribute of this tag and returns it as a boolean.
- * Essentially compares the lower case textual value to the string "true"
- */
- public static boolean getAttribBoolean(Element ele, String name)
- {
- String sval = ele.getAttribute(name);
-
- return sval.toLowerCase().equals("true");
- }
-
- public static URL getAttribURL(Element ele, String name, URL docRoot)
- {
- String sval = ele.getAttribute(name);
-
- try
- {
- return new URL(docRoot, sval);
- }
- catch (Exception e)
- {
- return null;
- }
- }
-
- /**
- * Returns the first ReadableXMLElement with the given name
- */
- public static ReadableXMLElement getElement(Class> classType, Element root, String name, URL docRoot)
- {
- if (root == null) return null;
-
- //Do not process if not a LoadableObject
- if (!ReadableXMLElement.class.isAssignableFrom(classType))
- {
- return null;
- }
-
- NodeList nl = root.getChildNodes();
- int size = nl.getLength();
- for (int i = 0; i < size; i++)
- {
- Node node = nl.item(i);
- if (!(node instanceof Element)) continue;
- Element ele = (Element)node;
- if (!ele.getTagName().equals(name)) continue;
-
- ReadableXMLElement newObj = null;
- try
- {
- newObj = (ReadableXMLElement)classType.newInstance();
- }
- catch (Exception e)
- {
- Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e);
- continue;
- }
- newObj.read(ele, docRoot);
-
- if (newObj == null) continue;
-
- return newObj;
- }
-
- return null;
- }
-
- /**
- * Returns a HashMap of nodes that are children of root. All nodes will
- * be of class classType and have a tag name of 'name'. 'key' is
- * an attribute of tag 'name' who's string value will be used as the key
- * in the HashMap
- */
- public static HashMap getElementHashMap(Class> classType, Element root, String name, String key, URL docRoot)
- {
- if (root == null) return null;
-
- //Do not process if not a LoadableObject
- if (!ReadableXMLElement.class.isAssignableFrom(classType))
- {
- return null;
- }
-
- HashMap retMap = new HashMap();
-
- NodeList nl = root.getChildNodes();
- int size = nl.getLength();
- for (int i = 0; i < size; i++)
- {
- Node node = nl.item(i);
- if (!(node instanceof Element)) continue;
- Element ele = (Element)node;
- if (!ele.getTagName().equals(name)) continue;
-
- ReadableXMLElement newObj = null;
- try
- {
- newObj = (ReadableXMLElement)classType.newInstance();
- }
- catch (Exception e)
- {
- Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e);
- continue;
- }
- newObj.read(ele, docRoot);
-
- if (newObj == null) continue;
-
- String keyVal = getAttribString(ele, key);
- retMap.put(keyVal, newObj);
- }
-
- return retMap;
- }
-
- public static HashSet getElementHashSet(Class> classType, Element root, String name, URL docRoot)
- {
- if (root == null) return null;
-
- //Do not process if not a LoadableObject
- if (!ReadableXMLElement.class.isAssignableFrom(classType))
- {
- return null;
- }
-
- HashSet retSet = new HashSet();
-
- NodeList nl = root.getChildNodes();
- int size = nl.getLength();
- for (int i = 0; i < size; i++)
- {
- Node node = nl.item(i);
- if (!(node instanceof Element)) continue;
- Element ele = (Element)node;
- if (!ele.getTagName().equals(name)) continue;
-
- ReadableXMLElement newObj = null;
- try
- {
- newObj = (ReadableXMLElement)classType.newInstance();
- }
- catch (Exception e)
- {
- Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e);
- continue;
- }
- newObj.read(ele, docRoot);
-
- if (newObj == null)
- {
- continue;
- }
-
- retSet.add(newObj);
- }
-
- return retSet;
- }
-
-
- public static LinkedList getElementLinkedList(Class> classType, Element root, String name, URL docRoot)
- {
- if (root == null) return null;
-
- //Do not process if not a LoadableObject
- if (!ReadableXMLElement.class.isAssignableFrom(classType))
- {
- return null;
- }
-
- NodeList nl = root.getChildNodes();
- LinkedList elementCache = new LinkedList();
- int size = nl.getLength();
- for (int i = 0; i < size; i++)
- {
- Node node = nl.item(i);
- if (!(node instanceof Element)) continue;
- Element ele = (Element)node;
- if (!ele.getTagName().equals(name)) continue;
-
- ReadableXMLElement newObj = null;
- try
- {
- newObj = (ReadableXMLElement)classType.newInstance();
- }
- catch (Exception e)
- {
- Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e);
- continue;
- }
- newObj.read(ele, docRoot);
-
- elementCache.addLast(newObj);
- }
-
- return elementCache;
- }
-
- public static Object[] getElementArray(Class> classType, Element root, String name, URL docRoot)
- {
- if (root == null) return null;
-
- //Do not process if not a LoadableObject
- if (!ReadableXMLElement.class.isAssignableFrom(classType))
- {
- return null;
- }
-
- LinkedList elementCache = getElementLinkedList(classType, root, name, docRoot);
-
- Object[] retArr = (Object[])Array.newInstance(classType, elementCache.size());
- return elementCache.toArray(retArr);
- }
-
- /**
- * Takes a number of tags of name 'name' that are children of 'root', and
- * looks for attributes of 'attrib' on them. Converts attributes to an
- * int and returns in an array.
- */
- public static int[] getElementArrayInt(Element root, String name, String attrib)
- {
- if (root == null) return null;
-
- NodeList nl = root.getChildNodes();
- LinkedList elementCache = new LinkedList();
- int size = nl.getLength();
-
- for (int i = 0; i < size; i++)
- {
- Node node = nl.item(i);
- if (!(node instanceof Element)) continue;
- Element ele = (Element)node;
- if (!ele.getTagName().equals(name)) continue;
-
- String valS = ele.getAttribute(attrib);
- int eleVal = 0;
- try { eleVal = Integer.parseInt(valS); }
- catch (Exception e) {}
-
- elementCache.addLast(new Integer(eleVal));
- }
-
- int[] retArr = new int[elementCache.size()];
- Iterator it = elementCache.iterator();
- int idx = 0;
- while (it.hasNext())
- {
- retArr[idx++] = it.next().intValue();
- }
-
- return retArr;
- }
-
- /**
- * Takes a number of tags of name 'name' that are children of 'root', and
- * looks for attributes of 'attrib' on them. Converts attributes to an
- * int and returns in an array.
- */
- public static String[] getElementArrayString(Element root, String name, String attrib)
- {
- if (root == null) return null;
-
- NodeList nl = root.getChildNodes();
- LinkedList elementCache = new LinkedList();
- int size = nl.getLength();
-
- for (int i = 0; i < size; i++)
- {
- Node node = nl.item(i);
- if (!(node instanceof Element)) continue;
- Element ele = (Element)node;
- if (!ele.getTagName().equals(name)) continue;
-
- String valS = ele.getAttribute(attrib);
-
- elementCache.addLast(valS);
- }
-
- String[] retArr = new String[elementCache.size()];
- Iterator it = elementCache.iterator();
- int idx = 0;
- while (it.hasNext())
- {
- retArr[idx++] = it.next();
- }
-
- return retArr;
- }
-
- /**
- * Takes a CSS style string and retursn a hash of them.
- * @param styleString - A CSS formatted string of styles. Eg,
- * "font-size:12;fill:#d32c27;fill-rule:evenodd;stroke-width:1pt;"
- */
- public static HashMap parseStyle(String styleString) {
- return parseStyle(styleString, new HashMap());
- }
-
- /**
- * Takes a CSS style string and returns a hash of them.
- * @param styleString - A CSS formatted string of styles. Eg,
- * "font-size:12;fill:#d32c27;fill-rule:evenodd;stroke-width:1pt;"
- * @param map - A map to which these styles will be added
- */
- public static HashMap parseStyle(String styleString, HashMap map) {
- final Pattern patSemi = Pattern.compile(";");
-
- String[] styles = patSemi.split(styleString);
-
- for (int i = 0; i < styles.length; i++)
- {
- if (styles[i].length() == 0)
- {
- continue;
- }
-
- int colon = styles[i].indexOf(':');
- if (colon == -1)
- {
- continue;
- }
-
- String key = styles[i].substring(0, colon).trim();
- String value = quoteMatch.reset(styles[i].substring(colon + 1).trim()).replaceAll("");
-
- map.put(key, new StyleAttribute(key, value));
- }
-
- return map;
- }
-}
+/*
+ * SVG Salamander
+ * Copyright (c) 2004, Mark McKay
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Mark McKay can be contacted at mark@kitfox.com. Salamander and other
+ * projects can be found at http://www.kitfox.com
+ *
+ * Created on February 18, 2004, 1:49 PM
+ */
+
+package com.kitfox.svg.xml;
+
+import com.kitfox.svg.SVGConst;
+import org.w3c.dom.*;
+import java.awt.*;
+import java.net.*;
+import java.util.*;
+import java.util.regex.*;
+import java.lang.reflect.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class XMLParseUtil
+{
+ static final Matcher fpMatch = Pattern.compile("([-+]?((\\d*\\.\\d+)|(\\d+))([eE][+-]?\\d+)?)(\\%|in|cm|mm|pt|pc|px|em|ex)?").matcher("");
+ static final Matcher intMatch = Pattern.compile("[-+]?\\d+").matcher("");
+ static final Matcher quoteMatch = Pattern.compile("^'|'$").matcher("");
+
+ /** Creates a new instance of XMLParseUtil */
+ private XMLParseUtil()
+ {
+ }
+
+ /**
+ * Scans the tag's children and returns the first text element found
+ */
+ public static String getTagText(Element ele)
+ {
+ NodeList nl = ele.getChildNodes();
+ int size = nl.getLength();
+
+ Node node = null;
+ int i = 0;
+ for (; i < size; i++)
+ {
+ node = nl.item(i);
+ if (node instanceof Text) break;
+ }
+ if (i == size || node == null) return null;
+
+ return ((Text)node).getData();
+ }
+
+ /**
+ * Returns the first node that is a direct child of root with the coresponding
+ * name. Does not search children of children.
+ */
+ public static Element getFirstChild(Element root, String name)
+ {
+ NodeList nl = root.getChildNodes();
+ int size = nl.getLength();
+ for (int i = 0; i < size; i++)
+ {
+ Node node = nl.item(i);
+ if (!(node instanceof Element)) continue;
+ Element ele = (Element)node;
+ if (ele.getTagName().equals(name)) return ele;
+ }
+
+ return null;
+ }
+
+ public static String[] parseStringList(String list)
+ {
+// final Pattern patWs = Pattern.compile("\\s+");
+ final Matcher matchWs = Pattern.compile("[^\\s]+").matcher("");
+ matchWs.reset(list);
+
+ LinkedList matchList = new LinkedList();
+ while (matchWs.find())
+ {
+ matchList.add(matchWs.group());
+ }
+
+ String[] retArr = new String[matchList.size()];
+ return (String[])matchList.toArray(retArr);
+ }
+
+ public static boolean isDouble(String val)
+ {
+ fpMatch.reset(val);
+ return fpMatch.matches();
+ }
+
+ public static double parseDouble(String val)
+ {
+ /*
+ if (val == null) return 0.0;
+
+ double retVal = 0.0;
+ try
+ { retVal = Double.parseDouble(val); }
+ catch (Exception e)
+ {}
+ return retVal;
+ */
+ return findDouble(val);
+ }
+
+ /**
+ * Searches the given string for the first floating point number it contains,
+ * parses and returns it.
+ */
+ public synchronized static double findDouble(String val)
+ {
+ if (val == null) return 0;
+
+ fpMatch.reset(val);
+ try
+ {
+ if (!fpMatch.find()) return 0;
+ }
+ catch (StringIndexOutOfBoundsException e)
+ {
+ Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING,
+ "XMLParseUtil: regex parse problem: '" + val + "'", e);
+ }
+
+ val = fpMatch.group(1);
+ //System.err.println("Parsing " + val);
+
+ double retVal = 0;
+ try
+ {
+ retVal = Double.parseDouble(val);
+
+ float pixPerInch;
+ try {
+ pixPerInch = (float)Toolkit.getDefaultToolkit().getScreenResolution();
+ }
+ catch (NoClassDefFoundError err)
+ {
+ //Default value for headless X servers
+ pixPerInch = 72;
+ }
+ final float inchesPerCm = .3936f;
+ final String units = fpMatch.group(6);
+
+ if ("%".equals(units)) retVal /= 100;
+ else if ("in".equals(units))
+ {
+ retVal *= pixPerInch;
+ }
+ else if ("cm".equals(units))
+ {
+ retVal *= inchesPerCm * pixPerInch;
+ }
+ else if ("mm".equals(units))
+ {
+ retVal *= inchesPerCm * pixPerInch * .1f;
+ }
+ else if ("pt".equals(units))
+ {
+ retVal *= (1f / 72f) * pixPerInch;
+ }
+ else if ("pc".equals(units))
+ {
+ retVal *= (1f / 6f) * pixPerInch;
+ }
+ }
+ catch (Exception e)
+ {}
+ return retVal;
+ }
+
+ /**
+ * Scans an input string for double values. For each value found, places
+ * in a list. This method regards any characters not part of a floating
+ * point value to be seperators. Thus this will parse whitespace seperated,
+ * comma seperated, and many other separation schemes correctly.
+ */
+ public synchronized static double[] parseDoubleList(String list)
+ {
+ if (list == null) return null;
+
+ fpMatch.reset(list);
+
+ LinkedList doubList = new LinkedList();
+ while (fpMatch.find())
+ {
+ String val = fpMatch.group(1);
+ doubList.add(Double.valueOf(val));
+ }
+
+ double[] retArr = new double[doubList.size()];
+ Iterator it = doubList.iterator();
+ int idx = 0;
+ while (it.hasNext())
+ {
+ retArr[idx++] = ((Double)it.next()).doubleValue();
+ }
+
+ return retArr;
+ }
+
+ public static float parseFloat(String val)
+ {
+ /*
+ if (val == null) return 0f;
+
+ float retVal = 0f;
+ try
+ { retVal = Float.parseFloat(val); }
+ catch (Exception e)
+ {}
+ return retVal;
+ */
+ return findFloat(val);
+ }
+
+ /**
+ * Searches the given string for the first floating point number it contains,
+ * parses and returns it.
+ */
+ public synchronized static float findFloat(String val)
+ {
+ if (val == null) return 0f;
+
+ fpMatch.reset(val);
+ if (!fpMatch.find()) return 0f;
+
+ val = fpMatch.group(1);
+ //System.err.println("Parsing " + val);
+
+ float retVal = 0f;
+ try
+ {
+ retVal = Float.parseFloat(val);
+ String units = fpMatch.group(6);
+ if ("%".equals(units)) retVal /= 100;
+ }
+ catch (Exception e)
+ {}
+ return retVal;
+ }
+
+ public synchronized static float[] parseFloatList(String list)
+ {
+ if (list == null) return null;
+
+ fpMatch.reset(list);
+
+ LinkedList floatList = new LinkedList();
+ while (fpMatch.find())
+ {
+ String val = fpMatch.group(1);
+ floatList.add(Float.valueOf(val));
+ }
+
+ float[] retArr = new float[floatList.size()];
+ Iterator it = floatList.iterator();
+ int idx = 0;
+ while (it.hasNext())
+ {
+ retArr[idx++] = ((Float)it.next()).floatValue();
+ }
+
+ return retArr;
+ }
+
+ public static int parseInt(String val)
+ {
+ if (val == null) return 0;
+
+ int retVal = 0;
+ try
+ { retVal = Integer.parseInt(val); }
+ catch (Exception e)
+ {}
+ return retVal;
+ }
+
+ /**
+ * Searches the given string for the first integer point number it contains,
+ * parses and returns it.
+ */
+ public static int findInt(String val)
+ {
+ if (val == null) return 0;
+
+ intMatch.reset(val);
+ if (!intMatch.find()) return 0;
+
+ val = intMatch.group();
+ //System.err.println("Parsing " + val);
+
+ int retVal = 0;
+ try
+ { retVal = Integer.parseInt(val); }
+ catch (Exception e)
+ {}
+ return retVal;
+ }
+
+ public static int[] parseIntList(String list)
+ {
+ if (list == null) return null;
+
+ intMatch.reset(list);
+
+ LinkedList intList = new LinkedList();
+ while (intMatch.find())
+ {
+ String val = intMatch.group();
+ intList.add(Integer.valueOf(val));
+ }
+
+ int[] retArr = new int[intList.size()];
+ Iterator it = intList.iterator();
+ int idx = 0;
+ while (it.hasNext())
+ {
+ retArr[idx++] = ((Integer)it.next()).intValue();
+ }
+
+ return retArr;
+ }
+/*
+ public static int parseHex(String val)
+ {
+ int retVal = 0;
+
+ for (int i = 0; i < val.length(); i++)
+ {
+ retVal <<= 4;
+
+ char ch = val.charAt(i);
+ if (ch >= '0' && ch <= '9')
+ {
+ retVal |= ch - '0';
+ }
+ else if (ch >= 'a' && ch <= 'z')
+ {
+ retVal |= ch - 'a' + 10;
+ }
+ else if (ch >= 'A' && ch <= 'Z')
+ {
+ retVal |= ch - 'A' + 10;
+ }
+ else throw new RuntimeException();
+ }
+
+ return retVal;
+ }
+*/
+ /**
+ * The input string represents a ratio. Can either be specified as a
+ * double number on the range of [0.0 1.0] or as a percentage [0% 100%]
+ */
+ public static double parseRatio(String val)
+ {
+ if (val == null || val.equals("")) return 0.0;
+
+ if (val.charAt(val.length() - 1) == '%')
+ {
+ parseDouble(val.substring(0, val.length() - 1));
+ }
+ return parseDouble(val);
+ }
+
+ public static NumberWithUnits parseNumberWithUnits(String val)
+ {
+ if (val == null) return null;
+
+ return new NumberWithUnits(val);
+ }
+/*
+ public static Color parseColor(String val)
+ {
+ Color retVal = null;
+
+ if (val.charAt(0) == '#')
+ {
+ String hexStrn = val.substring(1);
+
+ if (hexStrn.length() == 3)
+ {
+ hexStrn = "" + hexStrn.charAt(0) + hexStrn.charAt(0) + hexStrn.charAt(1) + hexStrn.charAt(1) + hexStrn.charAt(2) + hexStrn.charAt(2);
+ }
+ int hexVal = parseHex(hexStrn);
+
+ retVal = new Color(hexVal);
+ }
+ else
+ {
+ final Matcher rgbMatch = Pattern.compile("rgb\\((\\d+),(\\d+),(\\d+)\\)", Pattern.CASE_INSENSITIVE).matcher("");
+
+ rgbMatch.reset(val);
+ if (rgbMatch.matches())
+ {
+ int r = Integer.parseInt(rgbMatch.group(1));
+ int g = Integer.parseInt(rgbMatch.group(2));
+ int b = Integer.parseInt(rgbMatch.group(3));
+ retVal = new Color(r, g, b);
+ }
+ else
+ {
+ Color lookupCol = ColorTable.instance().lookupColor(val);
+ if (lookupCol != null) retVal = lookupCol;
+ }
+ }
+
+ return retVal;
+ }
+*/
+ /**
+ * Parses the given attribute of this tag and returns it as a String.
+ */
+ public static String getAttribString(Element ele, String name)
+ {
+ return ele.getAttribute(name);
+ }
+
+ /**
+ * Parses the given attribute of this tag and returns it as an int.
+ */
+ public static int getAttribInt(Element ele, String name)
+ {
+ String sval = ele.getAttribute(name);
+ int val = 0;
+ try { val = Integer.parseInt(sval); } catch (Exception e) {}
+
+ return val;
+ }
+
+ /**
+ * Parses the given attribute of this tag as a hexadecimal encoded string and
+ * returns it as an int
+ */
+ public static int getAttribIntHex(Element ele, String name)
+ {
+ String sval = ele.getAttribute(name);
+ int val = 0;
+ try { val = Integer.parseInt(sval, 16); } catch (Exception e) {}
+
+ return val;
+ }
+
+ /**
+ * Parses the given attribute of this tag and returns it as a float
+ */
+ public static float getAttribFloat(Element ele, String name)
+ {
+ String sval = ele.getAttribute(name);
+ float val = 0.0f;
+ try { val = Float.parseFloat(sval); } catch (Exception e) {}
+
+ return val;
+ }
+
+ /**
+ * Parses the given attribute of this tag and returns it as a double.
+ */
+ public static double getAttribDouble(Element ele, String name)
+ {
+ String sval = ele.getAttribute(name);
+ double val = 0.0;
+ try { val = Double.parseDouble(sval); } catch (Exception e) {}
+
+ return val;
+ }
+
+ /**
+ * Parses the given attribute of this tag and returns it as a boolean.
+ * Essentially compares the lower case textual value to the string "true"
+ */
+ public static boolean getAttribBoolean(Element ele, String name)
+ {
+ String sval = ele.getAttribute(name);
+
+ return sval.toLowerCase().equals("true");
+ }
+
+ public static URL getAttribURL(Element ele, String name, URL docRoot)
+ {
+ String sval = ele.getAttribute(name);
+
+ try
+ {
+ return new URL(docRoot, sval);
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the first ReadableXMLElement with the given name
+ */
+ public static ReadableXMLElement getElement(Class> classType, Element root, String name, URL docRoot)
+ {
+ if (root == null) return null;
+
+ //Do not process if not a LoadableObject
+ if (!ReadableXMLElement.class.isAssignableFrom(classType))
+ {
+ return null;
+ }
+
+ NodeList nl = root.getChildNodes();
+ int size = nl.getLength();
+ for (int i = 0; i < size; i++)
+ {
+ Node node = nl.item(i);
+ if (!(node instanceof Element)) continue;
+ Element ele = (Element)node;
+ if (!ele.getTagName().equals(name)) continue;
+
+ ReadableXMLElement newObj = null;
+ try
+ {
+ newObj = (ReadableXMLElement)classType.newInstance();
+ }
+ catch (Exception e)
+ {
+ Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e);
+ continue;
+ }
+ newObj.read(ele, docRoot);
+
+ if (newObj == null) continue;
+
+ return newObj;
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a HashMap of nodes that are children of root. All nodes will
+ * be of class classType and have a tag name of 'name'. 'key' is
+ * an attribute of tag 'name' who's string value will be used as the key
+ * in the HashMap
+ */
+ public static HashMap getElementHashMap(Class> classType, Element root, String name, String key, URL docRoot)
+ {
+ if (root == null) return null;
+
+ //Do not process if not a LoadableObject
+ if (!ReadableXMLElement.class.isAssignableFrom(classType))
+ {
+ return null;
+ }
+
+ HashMap retMap = new HashMap();
+
+ NodeList nl = root.getChildNodes();
+ int size = nl.getLength();
+ for (int i = 0; i < size; i++)
+ {
+ Node node = nl.item(i);
+ if (!(node instanceof Element)) continue;
+ Element ele = (Element)node;
+ if (!ele.getTagName().equals(name)) continue;
+
+ ReadableXMLElement newObj = null;
+ try
+ {
+ newObj = (ReadableXMLElement)classType.newInstance();
+ }
+ catch (Exception e)
+ {
+ Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e);
+ continue;
+ }
+ newObj.read(ele, docRoot);
+
+ if (newObj == null) continue;
+
+ String keyVal = getAttribString(ele, key);
+ retMap.put(keyVal, newObj);
+ }
+
+ return retMap;
+ }
+
+ public static HashSet getElementHashSet(Class> classType, Element root, String name, URL docRoot)
+ {
+ if (root == null) return null;
+
+ //Do not process if not a LoadableObject
+ if (!ReadableXMLElement.class.isAssignableFrom(classType))
+ {
+ return null;
+ }
+
+ HashSet retSet = new HashSet();
+
+ NodeList nl = root.getChildNodes();
+ int size = nl.getLength();
+ for (int i = 0; i < size; i++)
+ {
+ Node node = nl.item(i);
+ if (!(node instanceof Element)) continue;
+ Element ele = (Element)node;
+ if (!ele.getTagName().equals(name)) continue;
+
+ ReadableXMLElement newObj = null;
+ try
+ {
+ newObj = (ReadableXMLElement)classType.newInstance();
+ }
+ catch (Exception e)
+ {
+ Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e);
+ continue;
+ }
+ newObj.read(ele, docRoot);
+
+ if (newObj == null)
+ {
+ continue;
+ }
+
+ retSet.add(newObj);
+ }
+
+ return retSet;
+ }
+
+
+ public static LinkedList getElementLinkedList(Class> classType, Element root, String name, URL docRoot)
+ {
+ if (root == null) return null;
+
+ //Do not process if not a LoadableObject
+ if (!ReadableXMLElement.class.isAssignableFrom(classType))
+ {
+ return null;
+ }
+
+ NodeList nl = root.getChildNodes();
+ LinkedList elementCache = new LinkedList();
+ int size = nl.getLength();
+ for (int i = 0; i < size; i++)
+ {
+ Node node = nl.item(i);
+ if (!(node instanceof Element)) continue;
+ Element ele = (Element)node;
+ if (!ele.getTagName().equals(name)) continue;
+
+ ReadableXMLElement newObj = null;
+ try
+ {
+ newObj = (ReadableXMLElement)classType.newInstance();
+ }
+ catch (Exception e)
+ {
+ Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, null, e);
+ continue;
+ }
+ newObj.read(ele, docRoot);
+
+ elementCache.addLast(newObj);
+ }
+
+ return elementCache;
+ }
+
+ public static Object[] getElementArray(Class> classType, Element root, String name, URL docRoot)
+ {
+ if (root == null) return null;
+
+ //Do not process if not a LoadableObject
+ if (!ReadableXMLElement.class.isAssignableFrom(classType))
+ {
+ return null;
+ }
+
+ LinkedList elementCache = getElementLinkedList(classType, root, name, docRoot);
+
+ Object[] retArr = (Object[])Array.newInstance(classType, elementCache.size());
+ return elementCache.toArray(retArr);
+ }
+
+ /**
+ * Takes a number of tags of name 'name' that are children of 'root', and
+ * looks for attributes of 'attrib' on them. Converts attributes to an
+ * int and returns in an array.
+ */
+ public static int[] getElementArrayInt(Element root, String name, String attrib)
+ {
+ if (root == null) return null;
+
+ NodeList nl = root.getChildNodes();
+ LinkedList elementCache = new LinkedList();
+ int size = nl.getLength();
+
+ for (int i = 0; i < size; i++)
+ {
+ Node node = nl.item(i);
+ if (!(node instanceof Element)) continue;
+ Element ele = (Element)node;
+ if (!ele.getTagName().equals(name)) continue;
+
+ String valS = ele.getAttribute(attrib);
+ int eleVal = 0;
+ try { eleVal = Integer.parseInt(valS); }
+ catch (Exception e) {}
+
+ elementCache.addLast(new Integer(eleVal));
+ }
+
+ int[] retArr = new int[elementCache.size()];
+ Iterator it = elementCache.iterator();
+ int idx = 0;
+ while (it.hasNext())
+ {
+ retArr[idx++] = it.next().intValue();
+ }
+
+ return retArr;
+ }
+
+ /**
+ * Takes a number of tags of name 'name' that are children of 'root', and
+ * looks for attributes of 'attrib' on them. Converts attributes to an
+ * int and returns in an array.
+ */
+ public static String[] getElementArrayString(Element root, String name, String attrib)
+ {
+ if (root == null) return null;
+
+ NodeList nl = root.getChildNodes();
+ LinkedList elementCache = new LinkedList();
+ int size = nl.getLength();
+
+ for (int i = 0; i < size; i++)
+ {
+ Node node = nl.item(i);
+ if (!(node instanceof Element)) continue;
+ Element ele = (Element)node;
+ if (!ele.getTagName().equals(name)) continue;
+
+ String valS = ele.getAttribute(attrib);
+
+ elementCache.addLast(valS);
+ }
+
+ String[] retArr = new String[elementCache.size()];
+ Iterator it = elementCache.iterator();
+ int idx = 0;
+ while (it.hasNext())
+ {
+ retArr[idx++] = it.next();
+ }
+
+ return retArr;
+ }
+
+ /**
+ * Takes a CSS style string and retursn a hash of them.
+ * @param styleString - A CSS formatted string of styles. Eg,
+ * "font-size:12;fill:#d32c27;fill-rule:evenodd;stroke-width:1pt;"
+ */
+ public static HashMap parseStyle(String styleString) {
+ return parseStyle(styleString, new HashMap());
+ }
+
+ /**
+ * Takes a CSS style string and returns a hash of them.
+ * @param styleString - A CSS formatted string of styles. Eg,
+ * "font-size:12;fill:#d32c27;fill-rule:evenodd;stroke-width:1pt;"
+ * @param map - A map to which these styles will be added
+ */
+ public static HashMap parseStyle(String styleString, HashMap map) {
+ final Pattern patSemi = Pattern.compile(";");
+
+ String[] styles = patSemi.split(styleString);
+
+ for (int i = 0; i < styles.length; i++)
+ {
+ if (styles[i].length() == 0)
+ {
+ continue;
+ }
+
+ int colon = styles[i].indexOf(':');
+ if (colon == -1)
+ {
+ continue;
+ }
+
+ String key = styles[i].substring(0, colon).trim().intern();
+ String value = quoteMatch.reset(styles[i].substring(colon + 1).trim()).replaceAll("").intern();
+
+ map.put(key, new StyleAttribute(key, value));
+ }
+
+ return map;
+ }
+}