From 091a1e0179cb264cc2cab6e3b11ea31045c8536d Mon Sep 17 00:00:00 2001 From: kitfox Date: Tue, 29 May 2007 23:33:23 +0000 Subject: Restoring SVG Salamander to it's original code base, and updating build scripts. git-svn-id: https://svn.java.net/svn/svgsalamander~svn/trunk/svg-core@36 7dc7fa77-23fb-e6ad-8e2e-c86bd48ed22b --- src/main/java/com/kitfox/svg/Circle.java | 175 +++ src/main/java/com/kitfox/svg/ClipPath.java | 159 +++ src/main/java/com/kitfox/svg/Defs.java | 71 + src/main/java/com/kitfox/svg/Desc.java | 58 + src/main/java/com/kitfox/svg/Ellipse.java | 191 +++ src/main/java/com/kitfox/svg/FeDistantLight.java | 100 ++ src/main/java/com/kitfox/svg/FeLight.java | 50 + src/main/java/com/kitfox/svg/FePointLight.java | 114 ++ src/main/java/com/kitfox/svg/FeSpotLight.java | 177 +++ src/main/java/com/kitfox/svg/FillElement.java | 51 + src/main/java/com/kitfox/svg/Filter.java | 229 ++++ src/main/java/com/kitfox/svg/FilterEffects.java | 233 ++++ src/main/java/com/kitfox/svg/Font.java | 249 ++++ src/main/java/com/kitfox/svg/FontFace.java | 215 +++ src/main/java/com/kitfox/svg/Glyph.java | 102 ++ src/main/java/com/kitfox/svg/Gradient.java | 282 ++++ src/main/java/com/kitfox/svg/Group.java | 275 ++++ src/main/java/com/kitfox/svg/ImageSVG.java | 299 ++++ src/main/java/com/kitfox/svg/Line.java | 172 +++ src/main/java/com/kitfox/svg/LinearGradient.java | 210 +++ src/main/java/com/kitfox/svg/Metadata.java | 46 + src/main/java/com/kitfox/svg/MissingGlyph.java | 252 ++++ src/main/java/com/kitfox/svg/Path.java | 153 +++ src/main/java/com/kitfox/svg/PatternSVG.java | 303 +++++ src/main/java/com/kitfox/svg/Polygon.java | 177 +++ src/main/java/com/kitfox/svg/Polyline.java | 155 +++ src/main/java/com/kitfox/svg/RadialGradient.java | 222 +++ src/main/java/com/kitfox/svg/Rect.java | 267 ++++ .../java/com/kitfox/svg/RenderableElement.java | 161 +++ src/main/java/com/kitfox/svg/SVGCache.java | 28 + src/main/java/com/kitfox/svg/SVGDiagram.java | 217 +++ src/main/java/com/kitfox/svg/SVGDisplayPanel.form | 16 + src/main/java/com/kitfox/svg/SVGDisplayPanel.java | 194 +++ src/main/java/com/kitfox/svg/SVGElement.java | 853 ++++++++++++ .../java/com/kitfox/svg/SVGElementException.java | 56 + src/main/java/com/kitfox/svg/SVGException.java | 47 + src/main/java/com/kitfox/svg/SVGLoader.java | 267 ++++ src/main/java/com/kitfox/svg/SVGLoaderHelper.java | 79 ++ .../java/com/kitfox/svg/SVGParseException.java | 47 + src/main/java/com/kitfox/svg/SVGRoot.java | 390 ++++++ src/main/java/com/kitfox/svg/SVGUniverse.java | 533 ++++++++ src/main/java/com/kitfox/svg/ShapeElement.java | 299 ++++ src/main/java/com/kitfox/svg/Stop.java | 129 ++ src/main/java/com/kitfox/svg/Style.java | 83 ++ src/main/java/com/kitfox/svg/Symbol.java | 158 +++ src/main/java/com/kitfox/svg/Text.java | 539 ++++++++ src/main/java/com/kitfox/svg/Title.java | 65 + .../java/com/kitfox/svg/TransformableElement.java | 117 ++ src/main/java/com/kitfox/svg/Tspan.java | 380 ++++++ src/main/java/com/kitfox/svg/Use.java | 255 ++++ .../com/kitfox/svg/animation/AnimTimeParser.jjt | 316 +++++ .../java/com/kitfox/svg/animation/Animate.java | 354 +++++ .../java/com/kitfox/svg/animation/AnimateBase.java | 92 ++ .../com/kitfox/svg/animation/AnimateColor.java | 81 ++ .../kitfox/svg/animation/AnimateColorIface.java | 38 + .../com/kitfox/svg/animation/AnimateMotion.java | 234 ++++ .../com/kitfox/svg/animation/AnimateTransform.java | 225 ++++ .../com/kitfox/svg/animation/AnimateXform.java | 52 + .../com/kitfox/svg/animation/AnimationElement.java | 336 +++++ .../kitfox/svg/animation/AnimationTimeEval.java | 65 + src/main/java/com/kitfox/svg/animation/Bezier.java | 201 +++ .../java/com/kitfox/svg/animation/SetSmil.java | 55 + .../java/com/kitfox/svg/animation/TimeBase.java | 99 ++ .../com/kitfox/svg/animation/TimeCompound.java | 96 ++ .../com/kitfox/svg/animation/TimeDiscrete.java | 51 + .../com/kitfox/svg/animation/TimeIndefinite.java | 48 + .../java/com/kitfox/svg/animation/TimeLookup.java | 75 ++ .../java/com/kitfox/svg/animation/TimeSum.java | 60 + .../java/com/kitfox/svg/animation/TrackBase.java | 104 ++ .../java/com/kitfox/svg/animation/TrackColor.java | 95 ++ .../java/com/kitfox/svg/animation/TrackDouble.java | 119 ++ .../com/kitfox/svg/animation/TrackManager.java | 153 +++ .../java/com/kitfox/svg/animation/TrackMotion.java | 128 ++ .../java/com/kitfox/svg/animation/TrackPath.java | 100 ++ .../com/kitfox/svg/animation/TrackTransform.java | 112 ++ src/main/java/com/kitfox/svg/app/MainFrame.form | 65 + src/main/java/com/kitfox/svg/app/MainFrame.java | 134 ++ src/main/java/com/kitfox/svg/app/PlayerDialog.form | 133 ++ src/main/java/com/kitfox/svg/app/PlayerDialog.java | 289 ++++ src/main/java/com/kitfox/svg/app/PlayerThread.java | 129 ++ .../com/kitfox/svg/app/PlayerThreadListener.java | 37 + src/main/java/com/kitfox/svg/app/SVGPlayer.form | 118 ++ src/main/java/com/kitfox/svg/app/SVGPlayer.java | 426 ++++++ src/main/java/com/kitfox/svg/app/SVGViewer.form | 118 ++ src/main/java/com/kitfox/svg/app/SVGViewer.java | 421 ++++++ .../java/com/kitfox/svg/app/VersionDialog.form | 64 + .../java/com/kitfox/svg/app/VersionDialog.java | 151 +++ .../com/kitfox/svg/app/ant/SVGToImageAntTask.java | 251 ++++ .../svg/app/beans/ProportionalLayoutPanel.form | 29 + .../svg/app/beans/ProportionalLayoutPanel.java | 91 ++ .../java/com/kitfox/svg/app/beans/SVGIcon.java | 385 ++++++ .../java/com/kitfox/svg/app/beans/SVGPanel.form | 13 + .../java/com/kitfox/svg/app/beans/SVGPanel.java | 243 ++++ .../java/com/kitfox/svg/batik/GraphicsUtil.java | 382 ++++++ .../com/kitfox/svg/batik/LinearGradientPaint.java | 354 +++++ .../svg/batik/LinearGradientPaintContext.java | 529 ++++++++ .../kitfox/svg/batik/MultipleGradientPaint.java | 236 ++++ .../svg/batik/MultipleGradientPaintContext.java | 1421 ++++++++++++++++++++ .../com/kitfox/svg/batik/RadialGradientPaint.java | 491 +++++++ .../svg/batik/RadialGradientPaintContext.java | 775 +++++++++++ .../com/kitfox/svg/composite/AdobeComposite.java | 70 + .../svg/composite/AdobeCompositeContext.java | 97 ++ src/main/java/com/kitfox/svg/package-info.java | 9 + src/main/java/com/kitfox/svg/pathcmd/Arc.java | 245 ++++ .../java/com/kitfox/svg/pathcmd/BuildHistory.java | 62 + src/main/java/com/kitfox/svg/pathcmd/Cubic.java | 74 + .../java/com/kitfox/svg/pathcmd/CubicSmooth.java | 78 ++ .../java/com/kitfox/svg/pathcmd/Horizontal.java | 65 + src/main/java/com/kitfox/svg/pathcmd/LineTo.java | 67 + src/main/java/com/kitfox/svg/pathcmd/MoveTo.java | 66 + .../java/com/kitfox/svg/pathcmd/PathCommand.java | 56 + src/main/java/com/kitfox/svg/pathcmd/PathUtil.java | 72 + .../java/com/kitfox/svg/pathcmd/Quadratic.java | 70 + .../com/kitfox/svg/pathcmd/QuadraticSmooth.java | 74 + src/main/java/com/kitfox/svg/pathcmd/Terminal.java | 56 + src/main/java/com/kitfox/svg/pathcmd/Vertical.java | 64 + .../java/com/kitfox/svg/pattern/PatternPaint.java | 60 + .../kitfox/svg/pattern/PatternPaintContext.java | 120 ++ src/main/java/com/kitfox/svg/xml/ColorTable.java | 273 ++++ .../java/com/kitfox/svg/xml/NumberWithUnits.java | 89 ++ .../com/kitfox/svg/xml/ReadableXMLElement.java | 48 + .../java/com/kitfox/svg/xml/StyleAttribute.java | 290 ++++ .../com/kitfox/svg/xml/WritableXMLElement.java | 48 + src/main/java/com/kitfox/svg/xml/XMLParseUtil.java | 806 +++++++++++ .../java/com/kitfox/svg/xml/cpx/CPXConsts.java | 40 + .../com/kitfox/svg/xml/cpx/CPXInputStream.java | 293 ++++ .../com/kitfox/svg/xml/cpx/CPXOutputStream.java | 174 +++ src/main/java/com/kitfox/svg/xml/cpx/CPXTest.java | 100 ++ src/main/java/xml/formControls.js | 27 + src/main/java/xml/postXform.html | 13 + src/main/java/xml/postXform.svg | 142 ++ src/main/java/xml/sampleForm.xml | 41 + src/main/res/example/duke.svg | 8 + src/main/res/res/help/about/about.html | 19 + src/main/res/res/icons/SVGUniverseIcon_16c.png | Bin 0 -> 440 bytes src/main/res/res/icons/SVGUniverseIcon_32c.png | Bin 0 -> 703 bytes src/main/res/res/icons/cursor.svg | 9 + src/main/res/xml/about.xml | 3 + src/main/res/xml/about.xsl | 50 + 139 files changed, 24352 insertions(+) create mode 100644 src/main/java/com/kitfox/svg/Circle.java create mode 100644 src/main/java/com/kitfox/svg/ClipPath.java create mode 100644 src/main/java/com/kitfox/svg/Defs.java create mode 100644 src/main/java/com/kitfox/svg/Desc.java create mode 100644 src/main/java/com/kitfox/svg/Ellipse.java create mode 100644 src/main/java/com/kitfox/svg/FeDistantLight.java create mode 100644 src/main/java/com/kitfox/svg/FeLight.java create mode 100644 src/main/java/com/kitfox/svg/FePointLight.java create mode 100644 src/main/java/com/kitfox/svg/FeSpotLight.java create mode 100644 src/main/java/com/kitfox/svg/FillElement.java create mode 100644 src/main/java/com/kitfox/svg/Filter.java create mode 100644 src/main/java/com/kitfox/svg/FilterEffects.java create mode 100644 src/main/java/com/kitfox/svg/Font.java create mode 100644 src/main/java/com/kitfox/svg/FontFace.java create mode 100644 src/main/java/com/kitfox/svg/Glyph.java create mode 100644 src/main/java/com/kitfox/svg/Gradient.java create mode 100644 src/main/java/com/kitfox/svg/Group.java create mode 100644 src/main/java/com/kitfox/svg/ImageSVG.java create mode 100644 src/main/java/com/kitfox/svg/Line.java create mode 100644 src/main/java/com/kitfox/svg/LinearGradient.java create mode 100644 src/main/java/com/kitfox/svg/Metadata.java create mode 100644 src/main/java/com/kitfox/svg/MissingGlyph.java create mode 100644 src/main/java/com/kitfox/svg/Path.java create mode 100644 src/main/java/com/kitfox/svg/PatternSVG.java create mode 100644 src/main/java/com/kitfox/svg/Polygon.java create mode 100644 src/main/java/com/kitfox/svg/Polyline.java create mode 100644 src/main/java/com/kitfox/svg/RadialGradient.java create mode 100644 src/main/java/com/kitfox/svg/Rect.java create mode 100644 src/main/java/com/kitfox/svg/RenderableElement.java create mode 100644 src/main/java/com/kitfox/svg/SVGCache.java create mode 100644 src/main/java/com/kitfox/svg/SVGDiagram.java create mode 100644 src/main/java/com/kitfox/svg/SVGDisplayPanel.form create mode 100644 src/main/java/com/kitfox/svg/SVGDisplayPanel.java create mode 100644 src/main/java/com/kitfox/svg/SVGElement.java create mode 100644 src/main/java/com/kitfox/svg/SVGElementException.java create mode 100644 src/main/java/com/kitfox/svg/SVGException.java create mode 100644 src/main/java/com/kitfox/svg/SVGLoader.java create mode 100644 src/main/java/com/kitfox/svg/SVGLoaderHelper.java create mode 100644 src/main/java/com/kitfox/svg/SVGParseException.java create mode 100644 src/main/java/com/kitfox/svg/SVGRoot.java create mode 100644 src/main/java/com/kitfox/svg/SVGUniverse.java create mode 100644 src/main/java/com/kitfox/svg/ShapeElement.java create mode 100644 src/main/java/com/kitfox/svg/Stop.java create mode 100644 src/main/java/com/kitfox/svg/Style.java create mode 100644 src/main/java/com/kitfox/svg/Symbol.java create mode 100644 src/main/java/com/kitfox/svg/Text.java create mode 100644 src/main/java/com/kitfox/svg/Title.java create mode 100644 src/main/java/com/kitfox/svg/TransformableElement.java create mode 100644 src/main/java/com/kitfox/svg/Tspan.java create mode 100644 src/main/java/com/kitfox/svg/Use.java create mode 100644 src/main/java/com/kitfox/svg/animation/AnimTimeParser.jjt create mode 100644 src/main/java/com/kitfox/svg/animation/Animate.java create mode 100644 src/main/java/com/kitfox/svg/animation/AnimateBase.java create mode 100644 src/main/java/com/kitfox/svg/animation/AnimateColor.java create mode 100644 src/main/java/com/kitfox/svg/animation/AnimateColorIface.java create mode 100644 src/main/java/com/kitfox/svg/animation/AnimateMotion.java create mode 100644 src/main/java/com/kitfox/svg/animation/AnimateTransform.java create mode 100644 src/main/java/com/kitfox/svg/animation/AnimateXform.java create mode 100644 src/main/java/com/kitfox/svg/animation/AnimationElement.java create mode 100644 src/main/java/com/kitfox/svg/animation/AnimationTimeEval.java create mode 100644 src/main/java/com/kitfox/svg/animation/Bezier.java create mode 100644 src/main/java/com/kitfox/svg/animation/SetSmil.java create mode 100644 src/main/java/com/kitfox/svg/animation/TimeBase.java create mode 100644 src/main/java/com/kitfox/svg/animation/TimeCompound.java create mode 100644 src/main/java/com/kitfox/svg/animation/TimeDiscrete.java create mode 100644 src/main/java/com/kitfox/svg/animation/TimeIndefinite.java create mode 100644 src/main/java/com/kitfox/svg/animation/TimeLookup.java create mode 100644 src/main/java/com/kitfox/svg/animation/TimeSum.java create mode 100644 src/main/java/com/kitfox/svg/animation/TrackBase.java create mode 100644 src/main/java/com/kitfox/svg/animation/TrackColor.java create mode 100644 src/main/java/com/kitfox/svg/animation/TrackDouble.java create mode 100644 src/main/java/com/kitfox/svg/animation/TrackManager.java create mode 100644 src/main/java/com/kitfox/svg/animation/TrackMotion.java create mode 100644 src/main/java/com/kitfox/svg/animation/TrackPath.java create mode 100644 src/main/java/com/kitfox/svg/animation/TrackTransform.java create mode 100644 src/main/java/com/kitfox/svg/app/MainFrame.form create mode 100644 src/main/java/com/kitfox/svg/app/MainFrame.java create mode 100644 src/main/java/com/kitfox/svg/app/PlayerDialog.form create mode 100644 src/main/java/com/kitfox/svg/app/PlayerDialog.java create mode 100644 src/main/java/com/kitfox/svg/app/PlayerThread.java create mode 100644 src/main/java/com/kitfox/svg/app/PlayerThreadListener.java create mode 100644 src/main/java/com/kitfox/svg/app/SVGPlayer.form create mode 100644 src/main/java/com/kitfox/svg/app/SVGPlayer.java create mode 100644 src/main/java/com/kitfox/svg/app/SVGViewer.form create mode 100644 src/main/java/com/kitfox/svg/app/SVGViewer.java create mode 100644 src/main/java/com/kitfox/svg/app/VersionDialog.form create mode 100644 src/main/java/com/kitfox/svg/app/VersionDialog.java create mode 100644 src/main/java/com/kitfox/svg/app/ant/SVGToImageAntTask.java create mode 100644 src/main/java/com/kitfox/svg/app/beans/ProportionalLayoutPanel.form create mode 100644 src/main/java/com/kitfox/svg/app/beans/ProportionalLayoutPanel.java create mode 100644 src/main/java/com/kitfox/svg/app/beans/SVGIcon.java create mode 100644 src/main/java/com/kitfox/svg/app/beans/SVGPanel.form create mode 100644 src/main/java/com/kitfox/svg/app/beans/SVGPanel.java create mode 100644 src/main/java/com/kitfox/svg/batik/GraphicsUtil.java create mode 100644 src/main/java/com/kitfox/svg/batik/LinearGradientPaint.java create mode 100644 src/main/java/com/kitfox/svg/batik/LinearGradientPaintContext.java create mode 100644 src/main/java/com/kitfox/svg/batik/MultipleGradientPaint.java create mode 100644 src/main/java/com/kitfox/svg/batik/MultipleGradientPaintContext.java create mode 100644 src/main/java/com/kitfox/svg/batik/RadialGradientPaint.java create mode 100644 src/main/java/com/kitfox/svg/batik/RadialGradientPaintContext.java create mode 100644 src/main/java/com/kitfox/svg/composite/AdobeComposite.java create mode 100644 src/main/java/com/kitfox/svg/composite/AdobeCompositeContext.java create mode 100644 src/main/java/com/kitfox/svg/package-info.java create mode 100644 src/main/java/com/kitfox/svg/pathcmd/Arc.java create mode 100644 src/main/java/com/kitfox/svg/pathcmd/BuildHistory.java create mode 100644 src/main/java/com/kitfox/svg/pathcmd/Cubic.java create mode 100644 src/main/java/com/kitfox/svg/pathcmd/CubicSmooth.java create mode 100644 src/main/java/com/kitfox/svg/pathcmd/Horizontal.java create mode 100644 src/main/java/com/kitfox/svg/pathcmd/LineTo.java create mode 100644 src/main/java/com/kitfox/svg/pathcmd/MoveTo.java create mode 100644 src/main/java/com/kitfox/svg/pathcmd/PathCommand.java create mode 100644 src/main/java/com/kitfox/svg/pathcmd/PathUtil.java create mode 100644 src/main/java/com/kitfox/svg/pathcmd/Quadratic.java create mode 100644 src/main/java/com/kitfox/svg/pathcmd/QuadraticSmooth.java create mode 100644 src/main/java/com/kitfox/svg/pathcmd/Terminal.java create mode 100644 src/main/java/com/kitfox/svg/pathcmd/Vertical.java create mode 100644 src/main/java/com/kitfox/svg/pattern/PatternPaint.java create mode 100644 src/main/java/com/kitfox/svg/pattern/PatternPaintContext.java create mode 100644 src/main/java/com/kitfox/svg/xml/ColorTable.java create mode 100644 src/main/java/com/kitfox/svg/xml/NumberWithUnits.java create mode 100644 src/main/java/com/kitfox/svg/xml/ReadableXMLElement.java create mode 100644 src/main/java/com/kitfox/svg/xml/StyleAttribute.java create mode 100644 src/main/java/com/kitfox/svg/xml/WritableXMLElement.java create mode 100644 src/main/java/com/kitfox/svg/xml/XMLParseUtil.java create mode 100644 src/main/java/com/kitfox/svg/xml/cpx/CPXConsts.java create mode 100644 src/main/java/com/kitfox/svg/xml/cpx/CPXInputStream.java create mode 100644 src/main/java/com/kitfox/svg/xml/cpx/CPXOutputStream.java create mode 100644 src/main/java/com/kitfox/svg/xml/cpx/CPXTest.java create mode 100644 src/main/java/xml/formControls.js create mode 100644 src/main/java/xml/postXform.html create mode 100644 src/main/java/xml/postXform.svg create mode 100644 src/main/java/xml/sampleForm.xml create mode 100644 src/main/res/example/duke.svg create mode 100644 src/main/res/res/help/about/about.html create mode 100644 src/main/res/res/icons/SVGUniverseIcon_16c.png create mode 100644 src/main/res/res/icons/SVGUniverseIcon_32c.png create mode 100644 src/main/res/res/icons/cursor.svg create mode 100644 src/main/res/xml/about.xml create mode 100644 src/main/res/xml/about.xsl (limited to 'src/main') diff --git a/src/main/java/com/kitfox/svg/Circle.java b/src/main/java/com/kitfox/svg/Circle.java new file mode 100644 index 0000000..80daf35 --- /dev/null +++ b/src/main/java/com/kitfox/svg/Circle.java @@ -0,0 +1,175 @@ +/* + * Rect.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 26, 2004, 5:25 PM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import com.kitfox.svg.xml.*; +import com.kitfox.svg.animation.*; +import org.xml.sax.*; + +import java.net.*; +import java.io.*; +import java.util.*; +import java.awt.*; +import java.awt.geom.*; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class Circle extends ShapeElement +{ + + float cx = 0f; + float cy = 0f; + float r = 0f; + + + Ellipse2D.Float circle = new Ellipse2D.Float(); + + /** Creates a new instance of Rect */ + public Circle() { + } +/* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + String cx = attrs.getValue("cx"); + String cy = attrs.getValue("cy"); + String r = attrs.getValue("r"); + + this.cx = XMLParseUtil.parseFloat(cx); + this.cy = XMLParseUtil.parseFloat(cy); + this.r = XMLParseUtil.parseFloat(r); + + build(); + + //setBounds(this.cx - this.r, this.cy - this.r, this.r * 2.0, this.r * 2.0); + } +*/ + /* + public void loaderEndElement(SVGLoaderHelper helper) + { +// super.loaderEndElement(helper); + +// build(); + } + */ + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + if (getPres(sty.setName("cx"))) cx = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("cy"))) cy = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("r"))) r = sty.getFloatValueWithUnits(); + + circle.setFrame(cx - r, cy - r, r * 2f, r * 2f); + } + + public void render(Graphics2D g) throws SVGException + { + beginLayer(g); + renderShape(g, circle); + finishLayer(g); + } + + public Shape getShape() + { + return shapeToParent(circle); + } + + public Rectangle2D getBoundingBox() throws SVGException + { + return boundsToParent(includeStrokeInBounds(circle.getBounds2D())); + } + + /** + * 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) throws SVGException + { +// if (trackManager.getNumTracks() == 0) return false; + boolean changeState = super.updateTime(curTime); + + //Get current values for parameters + StyleAttribute sty = new StyleAttribute(); + boolean shapeChange = false; + + if (getPres(sty.setName("cx"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != cx) + { + cx = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("cy"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != cy) + { + cy = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("r"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != r) + { + r = newVal; + shapeChange = true; + } + } + + if (shapeChange) + { + circle.setFrame(cx - r, cy - r, r * 2f, r * 2f); + return true; + } + + return changeState; + } + +} + + + diff --git a/src/main/java/com/kitfox/svg/ClipPath.java b/src/main/java/com/kitfox/svg/ClipPath.java new file mode 100644 index 0000000..4c16f06 --- /dev/null +++ b/src/main/java/com/kitfox/svg/ClipPath.java @@ -0,0 +1,159 @@ +/* + * Stop.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 26, 2004, 1:56 AM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import java.awt.*; +import java.awt.geom.*; +import java.util.*; + +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class ClipPath extends SVGElement +{ + +// final Vector members = new Vector(); + + public static final int CP_USER_SPACE_ON_USE = 0; + public static final int CP_OBJECT_BOUNDING_BOX = 1; + + int clipPathUnits = CP_USER_SPACE_ON_USE; + + /** Creates a new instance of Stop */ + public ClipPath() { + } +/* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + String clipPathUnits = attrs.getValue("clipPathUnits"); + + if (clipPathUnits.equals("objectBoundingBox")) this.clipPathUnits = CP_OBJECT_BOUNDING_BOX; + + } +*/ + /** + * Called after the start element but before the end element to indicate + * each child tag that has been processed + */ + public void loaderAddChild(SVGLoaderHelper helper, SVGElement child) throws SVGElementException + { + super.loaderAddChild(helper, child); + +// if (child instanceof ShapeElement) members.add(child); + } + + /* + public void loaderEndElement(SVGLoaderHelper helper) + { +// super.loaderEndElement(helper); + +// build(); + } + */ + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + clipPathUnits = (getPres(sty.setName("clipPathUnits")) + && sty.getStringValue().equals("objectBoundingBox")) + ? CP_OBJECT_BOUNDING_BOX + : CP_USER_SPACE_ON_USE; + } + + public int getClipPathUnits() + { + return clipPathUnits; + } + + public Shape getClipPathShape() + { + if (children.size() == 0) return null; + if (children.size() == 1) return ((ShapeElement)children.get(0)).getShape(); + + Area clipArea = null; + for (Iterator it = children.iterator(); it.hasNext();) + { + ShapeElement se = (ShapeElement)it.next(); + + if (clipArea == null) + { + Shape shape = se.getShape(); + if (shape != null) clipArea = new Area(se.getShape()); + continue; + } + + Shape shape = se.getShape(); + if (shape != null) clipArea.intersect(new Area(shape)); + } + + return clipArea; + } + + /** + * 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) throws SVGException + { +// if (trackManager.getNumTracks() == 0) return false; + + //Get current values for parameters + StyleAttribute sty = new StyleAttribute(); + boolean shapeChange = false; + + + if (getPres(sty.setName("clipPathUnits"))) + { + String newUnitsStrn = sty.getStringValue(); + int newUnits = newUnitsStrn.equals("objectBoundingBox") + ? CP_OBJECT_BOUNDING_BOX + : CP_USER_SPACE_ON_USE; + + if (newUnits != clipPathUnits) + { + clipPathUnits = newUnits; + shapeChange = true; + } + } + + return shapeChange; + } +} diff --git a/src/main/java/com/kitfox/svg/Defs.java b/src/main/java/com/kitfox/svg/Defs.java new file mode 100644 index 0000000..ed7e615 --- /dev/null +++ b/src/main/java/com/kitfox/svg/Defs.java @@ -0,0 +1,71 @@ +/* + * Stop.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 26, 2004, 1:56 AM + */ + +package com.kitfox.svg; + +import java.awt.*; +import java.awt.geom.*; +import java.util.*; + +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class Defs extends TransformableElement { + +// final Vector members = new Vector(); + + /** Creates a new instance of Stop */ + public Defs() { + } + + /** + * Called after the start element but before the end element to indicate + * each child tag that has been processed + */ + public void loaderAddChild(SVGLoaderHelper helper, SVGElement child) throws SVGElementException + { + super.loaderAddChild(helper, child); + +// members.add(child); + } + + public boolean updateTime(double curTime) throws SVGException + { + boolean stateChange = false; + for (Iterator it = children.iterator(); it.hasNext();) + { + SVGElement ele = (SVGElement)it.next(); + stateChange = stateChange || ele.updateTime(curTime); + } + + return super.updateTime(curTime) || stateChange; + } +} diff --git a/src/main/java/com/kitfox/svg/Desc.java b/src/main/java/com/kitfox/svg/Desc.java new file mode 100644 index 0000000..4eb2c8f --- /dev/null +++ b/src/main/java/com/kitfox/svg/Desc.java @@ -0,0 +1,58 @@ +/* + * Stop.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 19, 2004, 1:56 AM + */ + +package com.kitfox.svg; + +/** + * Holds title textual information within tree + * + * @author Mark McKay + * @author Mark McKay + */ +public class Desc extends SVGElement { + + StringBuffer text = new StringBuffer(); + + /** Creates a new instance of Stop */ + public Desc() { + } + + /** + * Called during load process to add text scanned within a tag + */ + public void loaderAddText(SVGLoaderHelper helper, String text) + { + this.text.append(text); + } + + public String getText() { return text.toString(); } + + public boolean updateTime(double curTime) + { + return false; + } +} diff --git a/src/main/java/com/kitfox/svg/Ellipse.java b/src/main/java/com/kitfox/svg/Ellipse.java new file mode 100644 index 0000000..1047930 --- /dev/null +++ b/src/main/java/com/kitfox/svg/Ellipse.java @@ -0,0 +1,191 @@ +/* + * Rect.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 26, 2004, 5:25 PM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +import java.awt.*; +import java.awt.geom.*; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class Ellipse extends ShapeElement { + + float cx = 0.0f; + float cy = 0.0f; + float rx = 0.0f; + float ry = 0.0f; + + Ellipse2D.Float ellipse = new Ellipse2D.Float(); + + /** Creates a new instance of Rect */ + public Ellipse() { + } +/* + protected void init(String idIn, Style parentStyle, String cx, String cy, String rx, String ry) { + super.init(idIn, parentStyle); + + this.cx = parseDouble(cx); + this.cy = parseDouble(cy); + this.rx = parseDouble(rx); + this.ry = parseDouble(ry); + + setBounds(this.cx - this.rx, this.cy - this.ry, this.rx * 2.0, this.ry * 2.0); + } +*/ + /* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + String cx = attrs.getValue("cx"); + String cy = attrs.getValue("cy"); + String rx = attrs.getValue("rx"); + String ry = attrs.getValue("ry"); + + this.cx = XMLParseUtil.parseDouble(cx); + this.cy = XMLParseUtil.parseDouble(cy); + this.rx = XMLParseUtil.parseDouble(rx); + this.ry = XMLParseUtil.parseDouble(ry); + + build(); + } + */ + + /* + public void loaderEndElement(SVGLoaderHelper helper) + { + super.loaderEndElement(helper); + + build(); + } + */ + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + if (getPres(sty.setName("cx"))) cx = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("cy"))) cy = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("rx"))) rx = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("ry"))) ry = sty.getFloatValueWithUnits(); + + ellipse.setFrame(cx - rx, cy - ry, rx * 2f, ry * 2f); + } + + public void render(Graphics2D g) throws SVGException + { + beginLayer(g); + renderShape(g, ellipse); + finishLayer(g); + } + + public Shape getShape() + { + return shapeToParent(ellipse); + } + + public Rectangle2D getBoundingBox() throws SVGException + { + return boundsToParent(includeStrokeInBounds(ellipse.getBounds2D())); + } + + /** + * 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) throws SVGException + { +// if (trackManager.getNumTracks() == 0) return false; + boolean changeState = super.updateTime(curTime); + + //Get current values for parameters + StyleAttribute sty = new StyleAttribute(); + boolean shapeChange = false; + + if (getPres(sty.setName("cx"))) + { + float newCx = sty.getFloatValueWithUnits(); + if (newCx != cx) + { + cx = newCx; + shapeChange = true; + } + } + + if (getPres(sty.setName("cy"))) + { + float newCy = sty.getFloatValueWithUnits(); + if (newCy != cy) + { + cy = newCy; + shapeChange = true; + } + } + + if (getPres(sty.setName("rx"))) + { + float newRx = sty.getFloatValueWithUnits(); + if (newRx != rx) + { + rx = newRx; + shapeChange = true; + } + } + + if (getPres(sty.setName("ry"))) + { + float newRy = sty.getFloatValueWithUnits(); + if (newRy != ry) + { + ry = newRy; + shapeChange = true; + } + } + + if (shapeChange) + { + ellipse.setFrame(cx - rx, cy - ry, rx * 2f, ry * 2f); + return true; + } + + return changeState; + } +} diff --git a/src/main/java/com/kitfox/svg/FeDistantLight.java b/src/main/java/com/kitfox/svg/FeDistantLight.java new file mode 100644 index 0000000..a3aff4e --- /dev/null +++ b/src/main/java/com/kitfox/svg/FeDistantLight.java @@ -0,0 +1,100 @@ +/* + * FillElement.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 March 18, 2004, 6:52 AM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import java.awt.*; +import java.awt.geom.*; +import java.net.*; +import java.util.*; + +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class FeDistantLight extends FeLight +{ + float azimuth = 0f; + float elevation = 0f; + + + /** Creates a new instance of FillElement */ + public FeDistantLight() { + } + + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + String strn; + + if (getPres(sty.setName("azimuth"))) azimuth = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("elevation"))) elevation = sty.getFloatValueWithUnits(); + } + + public float getAzimuth() { return azimuth; } + public float getElevation() { return elevation; } + + public boolean updateTime(double curTime) throws SVGException + { +// if (trackManager.getNumTracks() == 0) return false; + + //Get current values for parameters + StyleAttribute sty = new StyleAttribute(); + boolean stateChange = false; + + if (getPres(sty.setName("azimuth"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != azimuth) + { + azimuth = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("elevation"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != elevation) + { + elevation = newVal; + stateChange = true; + } + } + + return stateChange; + } +} + diff --git a/src/main/java/com/kitfox/svg/FeLight.java b/src/main/java/com/kitfox/svg/FeLight.java new file mode 100644 index 0000000..8cd239c --- /dev/null +++ b/src/main/java/com/kitfox/svg/FeLight.java @@ -0,0 +1,50 @@ +/* + * FillElement.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 March 18, 2004, 6:52 AM + */ + +package com.kitfox.svg; + +import java.awt.*; +import java.awt.geom.*; +import java.net.*; +import java.util.*; + +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +/** + * @author Mark McKay + * @author Mark McKay + */ +abstract public class FeLight extends FilterEffects +{ + + /** Creates a new instance of FillElement */ + public FeLight() { + } + +} + diff --git a/src/main/java/com/kitfox/svg/FePointLight.java b/src/main/java/com/kitfox/svg/FePointLight.java new file mode 100644 index 0000000..52e116e --- /dev/null +++ b/src/main/java/com/kitfox/svg/FePointLight.java @@ -0,0 +1,114 @@ +/* + * FillElement.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 March 18, 2004, 6:52 AM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import java.awt.*; +import java.awt.geom.*; +import java.net.*; +import java.util.*; + +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class FePointLight extends FeLight +{ + float x = 0f; + float y = 0f; + float z = 0f; + + + /** Creates a new instance of FillElement */ + public FePointLight() { + } + + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + String strn; + + if (getPres(sty.setName("x"))) x = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("y"))) y = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("z"))) z = sty.getFloatValueWithUnits(); + } + + public float getX() { return x; } + public float getY() { return y; } + public float getZ() { return z; } + + public boolean updateTime(double curTime) throws SVGException + { +// if (trackManager.getNumTracks() == 0) return false; + + //Get current values for parameters + StyleAttribute sty = new StyleAttribute(); + boolean stateChange = false; + + if (getPres(sty.setName("x"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != x) + { + x = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("y"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != y) + { + y = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("z"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != z) + { + z = newVal; + stateChange = true; + } + } + + return stateChange; + } +} + diff --git a/src/main/java/com/kitfox/svg/FeSpotLight.java b/src/main/java/com/kitfox/svg/FeSpotLight.java new file mode 100644 index 0000000..f42a817 --- /dev/null +++ b/src/main/java/com/kitfox/svg/FeSpotLight.java @@ -0,0 +1,177 @@ +/* + * FillElement.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 March 18, 2004, 6:52 AM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import java.awt.*; +import java.awt.geom.*; +import java.net.*; +import java.util.*; + +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class FeSpotLight extends FeLight +{ + float x = 0f; + float y = 0f; + float z = 0f; + float pointsAtX = 0f; + float pointsAtY = 0f; + float pointsAtZ = 0f; + float specularComponent = 0f; + float limitingConeAngle = 0f; + + + /** Creates a new instance of FillElement */ + public FeSpotLight() { + } + + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + String strn; + + if (getPres(sty.setName("x"))) x = sty.getFloatValueWithUnits(); + if (getPres(sty.setName("y"))) y = sty.getFloatValueWithUnits(); + if (getPres(sty.setName("z"))) z = sty.getFloatValueWithUnits(); + if (getPres(sty.setName("pointsAtX"))) pointsAtX = sty.getFloatValueWithUnits(); + if (getPres(sty.setName("pointsAtY"))) pointsAtY = sty.getFloatValueWithUnits(); + if (getPres(sty.setName("pointsAtZ"))) pointsAtZ = sty.getFloatValueWithUnits(); + if (getPres(sty.setName("specularComponent"))) specularComponent = sty.getFloatValueWithUnits(); + if (getPres(sty.setName("limitingConeAngle"))) limitingConeAngle = sty.getFloatValueWithUnits(); + } + + public float getX() { return x; } + public float getY() { return y; } + public float getZ() { return z; } + public float getPointsAtX() { return pointsAtX; } + public float getPointsAtY() { return pointsAtY; } + public float getPointsAtZ() { return pointsAtZ; } + public float getSpecularComponent() { return specularComponent; } + public float getLimitingConeAngle() { return limitingConeAngle; } + + public boolean updateTime(double curTime) throws SVGException + { +// if (trackManager.getNumTracks() == 0) return false; + + //Get current values for parameters + StyleAttribute sty = new StyleAttribute(); + boolean stateChange = false; + + if (getPres(sty.setName("x"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != x) + { + x = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("y"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != y) + { + y = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("z"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != z) + { + z = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("pointsAtX"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != pointsAtX) + { + pointsAtX = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("pointsAtY"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != pointsAtY) + { + pointsAtY = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("pointsAtZ"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != pointsAtZ) + { + pointsAtZ = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("specularComponent"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != specularComponent) + { + specularComponent = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("limitingConeAngle"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != limitingConeAngle) + { + limitingConeAngle = newVal; + stateChange = true; + } + } + + return stateChange; + } +} + diff --git a/src/main/java/com/kitfox/svg/FillElement.java b/src/main/java/com/kitfox/svg/FillElement.java new file mode 100644 index 0000000..9fef10b --- /dev/null +++ b/src/main/java/com/kitfox/svg/FillElement.java @@ -0,0 +1,51 @@ +/* + * FillElement.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 March 18, 2004, 6:52 AM + */ + +package com.kitfox.svg; + +import java.awt.*; +import java.awt.geom.*; + +/** + * @author Mark McKay + * @author Mark McKay + */ +abstract public class FillElement extends SVGElement { + + /** Creates a new instance of FillElement */ + public FillElement() { + } + + /** + * Requests the paint defined by this element. Passes in information + * to allow paint to be customized + * @param bounds - bounding box of shape being rendered + * @param xform - The current transformation that the shape is being rendered + * under. + */ + abstract public Paint getPaint(Rectangle2D bounds, AffineTransform xform); +} diff --git a/src/main/java/com/kitfox/svg/Filter.java b/src/main/java/com/kitfox/svg/Filter.java new file mode 100644 index 0000000..0c41f15 --- /dev/null +++ b/src/main/java/com/kitfox/svg/Filter.java @@ -0,0 +1,229 @@ +/* + * FillElement.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 March 18, 2004, 6:52 AM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import java.awt.*; +import java.awt.geom.*; +import java.net.*; +import java.util.*; + +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class Filter extends SVGElement +{ + public static final int FU_OBJECT_BOUNDING_BOX = 0; + public static final int FU_USER_SPACE_ON_USE = 1; + + protected int filterUnits = FU_OBJECT_BOUNDING_BOX; + + public static final int PU_OBJECT_BOUNDING_BOX = 0; + public static final int PU_USER_SPACE_ON_USE = 1; + + protected int primitiveUnits = PU_OBJECT_BOUNDING_BOX; + + float x = 0f; + float y = 0f; + float width = 1f; + float height = 1f; + + Point2D filterRes = new Point2D.Double(); + + URL href = null; + + final Vector filterEffects = new Vector(); + + /** Creates a new instance of FillElement */ + public Filter() { + } + + /** + * Called after the start element but before the end element to indicate + * each child tag that has been processed + */ + public void loaderAddChild(SVGLoaderHelper helper, SVGElement child) throws SVGElementException + { + super.loaderAddChild(helper, child); + + if (child instanceof FilterEffects) + { + filterEffects.add(child); + } + } + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + String strn; + + if (getPres(sty.setName("filterUnits"))) + { + strn = sty.getStringValue().toLowerCase(); + if (strn.equals("userspaceonuse")) filterUnits = FU_USER_SPACE_ON_USE; + else filterUnits = FU_OBJECT_BOUNDING_BOX; + } + + if (getPres(sty.setName("primitiveUnits"))) + { + strn = sty.getStringValue().toLowerCase(); + if (strn.equals("userspaceonuse")) primitiveUnits = PU_USER_SPACE_ON_USE; + else primitiveUnits = PU_OBJECT_BOUNDING_BOX; + } + + if (getPres(sty.setName("x"))) x = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("y"))) y = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("width"))) width = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("height"))) height = sty.getFloatValueWithUnits(); + + try { + if (getPres(sty.setName("xlink:href"))) + { + URI src = sty.getURIValue(getXMLBase()); + href = src.toURL(); + } + } + catch (Exception e) + { + throw new SVGException(e); + } + + } + + public float getX() { return x; } + public float getY() { return y; } + public float getWidth() { return width; } + public float getHeight() { return height; } + + public boolean updateTime(double curTime) throws SVGException + { +// if (trackManager.getNumTracks() == 0) return false; + + //Get current values for parameters + StyleAttribute sty = new StyleAttribute(); + boolean stateChange = false; + + if (getPres(sty.setName("x"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != x) + { + x = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("y"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != y) + { + y = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("width"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != width) + { + width = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("height"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != height) + { + height = newVal; + stateChange = true; + } + } + + try { + if (getPres(sty.setName("xlink:href"))) + { + URI src = sty.getURIValue(getXMLBase()); + URL newVal = src.toURL(); + + if (!newVal.equals(href)) + { + href = newVal; + stateChange = true; + } + } + } + catch (Exception e) + { + throw new SVGException(e); + } + + if (getPres(sty.setName("filterUnits"))) + { + int newVal; + String strn = sty.getStringValue().toLowerCase(); + if (strn.equals("userspaceonuse")) newVal = FU_USER_SPACE_ON_USE; + else newVal = FU_OBJECT_BOUNDING_BOX; + if (newVal != filterUnits) + { + filterUnits = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("primitiveUnits"))) + { + int newVal; + String strn = sty.getStringValue().toLowerCase(); + if (strn.equals("userspaceonuse")) newVal = PU_USER_SPACE_ON_USE; + else newVal = PU_OBJECT_BOUNDING_BOX; + if (newVal != filterUnits) + { + primitiveUnits = newVal; + stateChange = true; + } + } + + + + return stateChange; + } +} + diff --git a/src/main/java/com/kitfox/svg/FilterEffects.java b/src/main/java/com/kitfox/svg/FilterEffects.java new file mode 100644 index 0000000..e5c7a91 --- /dev/null +++ b/src/main/java/com/kitfox/svg/FilterEffects.java @@ -0,0 +1,233 @@ +/* + * FillElement.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 March 18, 2004, 6:52 AM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import java.awt.*; +import java.awt.geom.*; +import java.net.*; +import java.util.*; + +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class FilterEffects extends SVGElement +{ + public static final int FP_SOURCE_GRAPHIC = 0; + public static final int FP_SOURCE_ALPHA = 1; + public static final int FP_BACKGROUND_IMAGE = 2; + public static final int FP_BACKGROUND_ALPHA = 3; + public static final int FP_FILL_PAINT = 4; + public static final int FP_STROKE_PAINT = 5; + public static final int FP_CUSTOM = 5; + + private int filterPrimitiveTypeIn; + private String filterPrimitiveRefIn; + + + float x = 0f; + float y = 0f; + float width = 1f; + float height = 1f; + + String result = "defaultFilterName"; + + + + URL href = null; + + + /** Creates a new instance of FillElement */ + public FilterEffects() { + } + + /** + * Called after the start element but before the end element to indicate + * each child tag that has been processed + */ + public void loaderAddChild(SVGLoaderHelper helper, SVGElement child) throws SVGElementException + { + super.loaderAddChild(helper, child); + + if (child instanceof FilterEffects) + { +// filterEffects.add(child); + } + } + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + String strn; + /* + if (getPres(sty.setName("filterUnits"))) + { + strn = sty.getStringValue().toLowerCase(); + if (strn.equals("userspaceonuse")) filterUnits = FU_USER_SPACE_ON_USE; + else filterUnits = FU_OBJECT_BOUNDING_BOX; + } + + if (getPres(sty.setName("primitiveUnits"))) + { + strn = sty.getStringValue().toLowerCase(); + if (strn.equals("userspaceonuse")) primitiveUnits = PU_USER_SPACE_ON_USE; + else primitiveUnits = PU_OBJECT_BOUNDING_BOX; + } + + if (getPres(sty.setName("x"))) x = sty.getFloatValue(); + + if (getPres(sty.setName("y"))) y = sty.getFloatValue(); + + if (getPres(sty.setName("width"))) width = sty.getFloatValue(); + + if (getPres(sty.setName("height"))) height = sty.getFloatValue(); + + try { + if (getPres(sty.setName("xlink:href"))) + { + URI src = sty.getURIValue(getXMLBase()); + href = src.toURL(); + } + } + catch (Exception e) + { + throw new SVGException(e); + } +*/ + } + + public float getX() { return x; } + public float getY() { return y; } + public float getWidth() { return width; } + public float getHeight() { return height; } + + public boolean updateTime(double curTime) throws SVGException + { +// if (trackManager.getNumTracks() == 0) return false; + + //Get current values for parameters + StyleAttribute sty = new StyleAttribute(); + boolean stateChange = false; + + if (getPres(sty.setName("x"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != x) + { + x = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("y"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != y) + { + y = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("width"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != width) + { + width = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("height"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != height) + { + height = newVal; + stateChange = true; + } + } + + try { + if (getPres(sty.setName("xlink:href"))) + { + URI src = sty.getURIValue(getXMLBase()); + URL newVal = src.toURL(); + + if (!newVal.equals(href)) + { + href = newVal; + stateChange = true; + } + } + } + catch (Exception e) + { + throw new SVGException(e); + } + + /* + if (getPres(sty.setName("filterUnits"))) + { + int newVal; + String strn = sty.getStringValue().toLowerCase(); + if (strn.equals("userspaceonuse")) newVal = FU_USER_SPACE_ON_USE; + else newVal = FU_OBJECT_BOUNDING_BOX; + if (newVal != filterUnits) + { + filterUnits = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("primitiveUnits"))) + { + int newVal; + String strn = sty.getStringValue().toLowerCase(); + if (strn.equals("userspaceonuse")) newVal = PU_USER_SPACE_ON_USE; + else newVal = PU_OBJECT_BOUNDING_BOX; + if (newVal != filterUnits) + { + primitiveUnits = newVal; + stateChange = true; + } + } + + */ + + return stateChange; + } +} + diff --git a/src/main/java/com/kitfox/svg/Font.java b/src/main/java/com/kitfox/svg/Font.java new file mode 100644 index 0000000..0896472 --- /dev/null +++ b/src/main/java/com/kitfox/svg/Font.java @@ -0,0 +1,249 @@ +/* + * Font.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 February 20, 2004, 10:00 PM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +import java.util.*; + +/** + * Implements an embedded font. + * + * SVG specification: http://www.w3.org/TR/SVG/fonts.html + * + * @author Mark McKay + * @author Mark McKay + */ +public class Font extends SVGElement +{ + int horizOriginX = 0; + int horizOriginY = 0; + int horizAdvX = -1; //Must be specified + int vertOriginX = -1; //Defaults to horizAdvX / 2 + int vertOriginY = -1; //Defaults to font's ascent + int vertAdvY = -1; //Defaults to one 'em'. See font-face + + //Vector members = null; + + FontFace fontFace = null; + MissingGlyph missingGlyph = null; + final HashMap glyphs = new HashMap(); + + /** Creates a new instance of Font */ + public Font() + { + } +/* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + String horizOriginX = attrs.getValue("horiz-origin-x"); + String horizOriginY = attrs.getValue("horiz-origin-y"); + String horizAdvX = attrs.getValue("horiz-adv-x"); + String vertOriginX = attrs.getValue("vert-origin-x"); + String vertOriginY = attrs.getValue("vert-origin-y"); + String vertAdvY = attrs.getValue("vert-adv-y"); + + if (horizOriginX != null) this.horizOriginX = XMLParseUtil.parseInt(horizOriginX); + if (horizOriginY != null) this.horizOriginY = XMLParseUtil.parseInt(horizOriginY); + if (horizAdvX != null) this.horizAdvX = XMLParseUtil.parseInt(horizAdvX); + if (vertOriginX != null) this.vertOriginX = XMLParseUtil.parseInt(vertOriginX); + if (vertOriginY != null) this.vertOriginY = XMLParseUtil.parseInt(vertOriginY); + if (vertAdvY != null) this.vertAdvY = XMLParseUtil.parseInt(vertAdvY); + + } +*/ + /** + * Called after the start element but before the end element to indicate + * each child tag that has been processed + */ + public void loaderAddChild(SVGLoaderHelper helper, SVGElement child) throws SVGElementException + { + super.loaderAddChild(helper, child); + + if (child instanceof Glyph) + { + glyphs.put(((Glyph)child).getUnicode(), child); + } + else if (child instanceof MissingGlyph) + { + missingGlyph = (MissingGlyph)child; + } + else if (child instanceof FontFace) + { + fontFace = (FontFace)child; + } + } + + public void loaderEndElement(SVGLoaderHelper helper) throws SVGParseException + { + super.loaderEndElement(helper); + + //build(); + + helper.universe.registerFont(this); + } + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + if (getPres(sty.setName("horiz-origin-x"))) horizOriginX = sty.getIntValue(); + + if (getPres(sty.setName("horiz-origin-y"))) horizOriginY = sty.getIntValue(); + + if (getPres(sty.setName("horiz-adv-x"))) horizAdvX = sty.getIntValue(); + + if (getPres(sty.setName("vert-origin-x"))) vertOriginX = sty.getIntValue(); + + if (getPres(sty.setName("vert-origin-y"))) vertOriginY = sty.getIntValue(); + + if (getPres(sty.setName("vert-adv-y"))) vertAdvY = sty.getIntValue(); + } + + public FontFace getFontFace() { return fontFace; } + + public MissingGlyph getGlyph(String unicode) + { + Glyph retVal = (Glyph)glyphs.get(unicode); + if (retVal == null) return missingGlyph; + return retVal; + } + + public int getHorizOriginX() { return horizOriginX; } + public int getHorizOriginY() { return horizOriginY; } + public int getHorizAdvX() { return horizAdvX; } + + public int getVertOriginX() + { + if (vertOriginX != -1) return vertOriginX; + vertOriginX = getHorizAdvX() / 2; + return vertOriginX; + } + + public int getVertOriginY() + { + if (vertOriginY != -1) return vertOriginY; + vertOriginY = fontFace.getAscent(); + return vertOriginY; + } + + public int getVertAdvY() + { + if (vertAdvY != -1) return vertAdvY; + vertAdvY = fontFace.getUnitsPerEm(); + return vertAdvY; + } + + /** + * 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) throws SVGException + { + //Fonts can't change + return false; + /* + if (trackManager.getNumTracks() == 0) return false; + + //Get current values for parameters + StyleAttribute sty = new StyleAttribute(); + boolean stateChange = false; + + if (getPres(sty.setName("horiz-origin-x"))) + { + int newVal = sty.getIntValue(); + if (newVal != horizOriginX) + { + horizOriginX = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("horiz-origin-y"))) + { + int newVal = sty.getIntValue(); + if (newVal != horizOriginY) + { + horizOriginY = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("horiz-adv-x"))) + { + int newVal = sty.getIntValue(); + if (newVal != horizAdvX) + { + horizAdvX = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("vert-origin-x"))) + { + int newVal = sty.getIntValue(); + if (newVal != vertOriginX) + { + vertOriginX = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("vert-origin-y"))) + { + int newVal = sty.getIntValue(); + if (newVal != vertOriginY) + { + vertOriginY = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("vert-adv-y"))) + { + int newVal = sty.getIntValue(); + if (newVal != vertAdvY) + { + vertAdvY = newVal; + stateChange = true; + } + } + + return shapeChange; + */ + } +} diff --git a/src/main/java/com/kitfox/svg/FontFace.java b/src/main/java/com/kitfox/svg/FontFace.java new file mode 100644 index 0000000..7b6dcfe --- /dev/null +++ b/src/main/java/com/kitfox/svg/FontFace.java @@ -0,0 +1,215 @@ +/* + * Font.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 February 20, 2004, 10:00 PM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +import java.awt.geom.*; +import java.awt.*; + + +/** + * Implements an embedded font. + * + * SVG specification: http://www.w3.org/TR/SVG/fonts.html + * + * @author Mark McKay + * @author Mark McKay + */ +public class FontFace extends SVGElement +{ + String fontFamily; + + /** Em size of coordinate system font is defined in */ + int unitsPerEm = 1000; + + int ascent = -1; + int descent = -1; + int accentHeight = -1; + + int underlinePosition = -1; + int underlineThickness = -1; + int strikethroughPosition = -1; + int strikethroughThickness = -1; + int overlinePosition = -1; + int overlineThickness = -1; + + /** Creates a new instance of Font */ + public FontFace() + { + } +/* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + fontFamily = attrs.getValue("font-family"); + + String unitsPerEm = attrs.getValue("units-per-em"); + String ascent = attrs.getValue("ascent"); + String descent = attrs.getValue("descent"); + String accentHeight = attrs.getValue("accent-height"); + + String underlinePosition = attrs.getValue("underline-position"); + String underlineThickness = attrs.getValue("underline-thickness"); + String strikethroughPosition = attrs.getValue("strikethrough-position"); + String strikethroughThickness = attrs.getValue("strikethrough-thickness"); + String overlinePosition = attrs.getValue("overline-position"); + String overlineThickness = attrs.getValue("overline-thickness"); + + + if (unitsPerEm != null) this.unitsPerEm = XMLParseUtil.parseInt(unitsPerEm); + if (ascent != null) this.ascent = XMLParseUtil.parseInt(ascent); + if (descent != null) this.descent = XMLParseUtil.parseInt(descent); + if (accentHeight != null) this.accentHeight = XMLParseUtil.parseInt(accentHeight); + + if (underlinePosition != null) this.underlinePosition = XMLParseUtil.parseInt(underlinePosition); + if (underlineThickness != null) this.underlineThickness = XMLParseUtil.parseInt(underlineThickness); + if (strikethroughPosition != null) this.strikethroughPosition = XMLParseUtil.parseInt(strikethroughPosition); + if (strikethroughThickness != null) this.strikethroughThickness = XMLParseUtil.parseInt(strikethroughThickness); + if (overlinePosition != null) this.overlinePosition = XMLParseUtil.parseInt(overlinePosition); + if (overlineThickness != null) this.overlineThickness = XMLParseUtil.parseInt(overlineThickness); + +// unitFontXform.setToScale(1.0 / (double)unitsPerEm, 1.0 / (double)unitsPerEm); + } + */ + /* + public void loaderEndElement(SVGLoaderHelper helper) + { + super.loaderEndElement(helper); + + build(); + +// unitFontXform.setToScale(1.0 / (double)unitsPerEm, 1.0 / (double)unitsPerEm); + } + */ + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + if (getPres(sty.setName("font-family"))) fontFamily = sty.getStringValue(); + + if (getPres(sty.setName("units-per-em"))) unitsPerEm = sty.getIntValue(); + if (getPres(sty.setName("ascent"))) ascent = sty.getIntValue(); + if (getPres(sty.setName("descent"))) descent = sty.getIntValue(); + if (getPres(sty.setName("accent-height"))) accentHeight = sty.getIntValue(); + + if (getPres(sty.setName("underline-position"))) underlinePosition = sty.getIntValue(); + if (getPres(sty.setName("underline-thickness"))) underlineThickness = sty.getIntValue(); + if (getPres(sty.setName("strikethrough-position"))) strikethroughPosition = sty.getIntValue(); + if (getPres(sty.setName("strikethrough-thickenss"))) strikethroughThickness = sty.getIntValue(); + if (getPres(sty.setName("overline-position"))) overlinePosition = sty.getIntValue(); + if (getPres(sty.setName("overline-thickness"))) overlineThickness = sty.getIntValue(); + } + + + public String getFontFamily() { return fontFamily; } + + public int getUnitsPerEm() { return unitsPerEm; } + + public int getAscent() + { + if (ascent == -1) + ascent = unitsPerEm - ((Font)parent).getVertOriginY(); + return ascent; + } + + public int getDescent() + { + if (descent == -1) + descent = ((Font)parent).getVertOriginY(); + return descent; + } + + public int getAccentHeight() + { + if (accentHeight == -1) + accentHeight = getAscent(); + return accentHeight; + } + + public int getUnderlinePosition() + { + if (underlinePosition == -1) + underlinePosition = unitsPerEm * 5 / 6; + return underlinePosition; + } + + public int getUnderlineThickness() + { + if (underlineThickness == -1) + underlineThickness = unitsPerEm / 20; + return underlineThickness; + } + + public int getStrikethroughPosition() + { + if (strikethroughPosition == -1) + strikethroughPosition = unitsPerEm * 3 / 6; + return strikethroughPosition; + } + + public int getStrikethroughThickness() + { + if (strikethroughThickness == -1) + strikethroughThickness = unitsPerEm / 20; + return strikethroughThickness; + } + + public int getOverlinePosition() + { + if (overlinePosition == -1) + overlinePosition = unitsPerEm * 5 / 6; + return overlinePosition; + } + + public int getOverlineThickness() + { + if (overlineThickness == -1) + overlineThickness = unitsPerEm / 20; + return overlineThickness; + } + + /** + * 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) + { + //Fonts can't change + return false; + } +} diff --git a/src/main/java/com/kitfox/svg/Glyph.java b/src/main/java/com/kitfox/svg/Glyph.java new file mode 100644 index 0000000..528444a --- /dev/null +++ b/src/main/java/com/kitfox/svg/Glyph.java @@ -0,0 +1,102 @@ +/* + * Font.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 February 20, 2004, 10:00 PM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +import java.awt.*; +import java.awt.geom.*; +import java.util.*; + +import com.kitfox.svg.pathcmd.*; +//import org.apache.batik.ext.awt.geom.ExtendedGeneralPath; + +/** + * Implements an embedded font. + * + * SVG specification: http://www.w3.org/TR/SVG/fonts.html + * + * @author Mark McKay + * @author Mark McKay + */ +public class Glyph extends MissingGlyph +{ + /** + * One or more characters indicating the unicode sequence that denotes + * this glyph. + */ + String unicode; + + /** Creates a new instance of Font */ + public Glyph() + { + } +/* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + //Get unicode sequence that maps to this glyph + unicode = attrs.getValue("unicode"); + } +*/ + /* + public void loaderEndElement(SVGLoaderHelper helper) + { + super.loaderEndElement(helper); + + build(); + } + */ + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + if (getPres(sty.setName("unicode"))) unicode = sty.getStringValue(); + } + + public String getUnicode() { return unicode; } + + /** + * 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) throws SVGException + { + //Fonts can't change + return false; + } +} diff --git a/src/main/java/com/kitfox/svg/Gradient.java b/src/main/java/com/kitfox/svg/Gradient.java new file mode 100644 index 0000000..a1336f1 --- /dev/null +++ b/src/main/java/com/kitfox/svg/Gradient.java @@ -0,0 +1,282 @@ +/* + * Gradient.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 26, 2004, 3:25 AM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import java.net.*; +import java.util.*; +import java.awt.geom.*; +import java.awt.*; + +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +/** + * @author Mark McKay + * @author Mark McKay + */ +abstract public class Gradient extends FillElement +{ + + public static final int SM_PAD = 0; + public static final int SM_REPEAT = 1; + public static final int SM_REFLECT = 2; + + int spreadMethod = SM_PAD; + + public static final int GU_OBJECT_BOUNDING_BOX = 0; + public static final int GU_USER_SPACE_ON_USE = 1; + + protected int gradientUnits = GU_OBJECT_BOUNDING_BOX; + + //Either this gradient contains a list of stops, or it will take it's + // stops from the referenced gradient + Vector stops = new Vector(); + Gradient stopRef = null; + + protected AffineTransform gradientTransform = null; + + //Cache arrays of stop values here + float[] stopFractions; + Color[] stopColors; + + /** Creates a new instance of Gradient */ + public Gradient() { + } + + /** + * Called after the start element but before the end element to indicate + * each child tag that has been processed + */ + public void loaderAddChild(SVGLoaderHelper helper, SVGElement child) throws SVGElementException + { + super.loaderAddChild(helper, child); + + if (!(child instanceof Stop)) return; + appendStop((Stop)child); + } + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + String strn; + + if (getPres(sty.setName("spreadMethod"))) + { + strn = sty.getStringValue().toLowerCase(); + if (strn.equals("repeat")) spreadMethod = SM_REPEAT; + else if (strn.equals("reflect")) spreadMethod = SM_REFLECT; + else spreadMethod = SM_PAD; + } + + if (getPres(sty.setName("gradientUnits"))) + { + strn = sty.getStringValue().toLowerCase(); + if (strn.equals("userspaceonuse")) gradientUnits = GU_USER_SPACE_ON_USE; + else gradientUnits = GU_OBJECT_BOUNDING_BOX; + } + + if (getPres(sty.setName("gradientTransform"))) gradientTransform = parseTransform(sty.getStringValue()); + //If we still don't have one, set it to identity + if (gradientTransform == null) gradientTransform = new AffineTransform(); + + + //Check to see if we're using our own stops or referencing someone else's + if (getPres(sty.setName("xlink:href"))) + { + try { + URI src = sty.getURIValue(getXMLBase()); +//System.err.println("Gradient: " + sty.getStringValue() + ", " + getXMLBase() + ", " + src); +// URI src = getXMLBase().resolve(href); + stopRef = (Gradient)diagram.getUniverse().getElement(src); + } + catch (Exception e) + { + throw new SVGException("Could not resolve relative URL in Gradient: " + sty.getStringValue() + ", " + getXMLBase(), e); + } + } + } + + public float[] getStopFractions() + { + if (stopRef != null) return stopRef.getStopFractions(); + + if (stopFractions != null) return stopFractions; + + stopFractions = new float[stops.size()]; + int idx = 0; + for (Iterator it = stops.iterator(); it.hasNext();) + { + Stop stop = (Stop)it.next(); + float val = stop.offset; + if (idx != 0 && val < stopFractions[idx - 1]) val = stopFractions[idx - 1]; + stopFractions[idx++] = val; + } + + return stopFractions; + } + + public Color[] getStopColors() + { + if (stopRef != null) return stopRef.getStopColors(); + + if (stopColors != null) return stopColors; + + stopColors = new Color[stops.size()]; + int idx = 0; + for (Iterator it = stops.iterator(); it.hasNext();) + { + Stop stop = (Stop)it.next(); + int stopColorVal = stop.color.getRGB(); + Color stopColor = new Color((stopColorVal >> 16) & 0xff, (stopColorVal >> 8) & 0xff, stopColorVal & 0xff, clamp((int)(stop.opacity * 255), 0, 255)); + stopColors[idx++] = stopColor; + } + + return stopColors; + } + + public void setStops(Color[] colors, float[] fractions) + { + if (colors.length != fractions.length) + { + throw new IllegalArgumentException(); + } + + this.stopColors = colors; + this.stopFractions = fractions; + stopRef = null; + } + + private int clamp(int val, int min, int max) + { + if (val < min) return min; + if (val > max) return max; + return val; + } + + public void setStopRef(Gradient grad) + { + stopRef = grad; + } + + public void appendStop(Stop stop) + { + stops.add(stop); + } + + /** + * 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) throws SVGException + { +// if (trackManager.getNumTracks() == 0) return false; + boolean stateChange = false; + + //Get current values for parameters + StyleAttribute sty = new StyleAttribute(); + boolean shapeChange = false; + String strn; + + + if (getPres(sty.setName("spreadMethod"))) + { + int newVal; + strn = sty.getStringValue().toLowerCase(); + if (strn.equals("repeat")) newVal = SM_REPEAT; + else if (strn.equals("reflect")) newVal = SM_REFLECT; + else newVal = SM_PAD; + if (spreadMethod != newVal) + { + spreadMethod = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("gradientUnits"))) + { + int newVal; + strn = sty.getStringValue().toLowerCase(); + if (strn.equals("userspaceonuse")) newVal = GU_USER_SPACE_ON_USE; + else newVal = GU_OBJECT_BOUNDING_BOX; + if (newVal != gradientUnits) + { + gradientUnits = newVal; + stateChange = true; + } + } + + if (getPres(sty.setName("gradientTransform"))) + { + AffineTransform newVal = parseTransform(sty.getStringValue()); + if (newVal != null && newVal.equals(gradientTransform)) + { + gradientTransform = newVal; + stateChange = true; + } + } + + + //Check to see if we're using our own stops or referencing someone else's + if (getPres(sty.setName("xlink:href"))) + { + try { + URI src = sty.getURIValue(getXMLBase()); + Gradient newVal = (Gradient)diagram.getUniverse().getElement(src); + if (newVal != stopRef) + { + stopRef = newVal; + stateChange = true; + } + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + //Check stops, if any + for (Iterator it = stops.iterator(); it.hasNext();) + { + Stop stop = (Stop)it.next(); + if (stop.updateTime(curTime)) + { + stateChange = true; + stopFractions = null; + stopColors = null; + } + } + + return stateChange; + } + +} diff --git a/src/main/java/com/kitfox/svg/Group.java b/src/main/java/com/kitfox/svg/Group.java new file mode 100644 index 0000000..8446049 --- /dev/null +++ b/src/main/java/com/kitfox/svg/Group.java @@ -0,0 +1,275 @@ +/* + * Stop.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 26, 2004, 1:56 AM + */ + +package com.kitfox.svg; + +import java.awt.*; +import java.awt.geom.*; +import java.util.*; + +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class Group extends ShapeElement { + +// final Vector members = new Vector(); + + //Cache bounding box for faster clip testing + Rectangle2D boundingBox; + Shape cachedShape; + + //Cache clip bounds + final Rectangle clipBounds = new Rectangle(); + + /** Creates a new instance of Stop */ + public Group() { + } + + /* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + //String transform = attrs.getValue("transform"); + } + */ + + /** + * Called after the start element but before the end element to indicate + * each child tag that has been processed + */ + public void loaderAddChild(SVGLoaderHelper helper, SVGElement child) throws SVGElementException + { + super.loaderAddChild(helper, child); + +// members.add(child); + } + + protected boolean outsideClip(Graphics2D g) throws SVGException + { + g.getClipBounds(clipBounds); + Rectangle2D rect = getBoundingBox(); + +//if (rect == null) +//{ +// rect = getBoundingBox(); +//} + +// if (rect.intersects(clipBounds)) + if (rect.intersects(clipBounds)) + { + return false; + } + + return true; + } + + void pick(Point2D point, Vector retVec) throws SVGException + { + Point2D xPoint = new Point2D.Double(point.getX(), point.getY()); + if (xform != null) + { + try + { + xform.inverseTransform(point, xPoint); + } + catch (NoninvertibleTransformException ex) + { + throw new SVGException(ex); + } + } + + + for (Iterator it = children.iterator(); it.hasNext();) + { + SVGElement ele = (SVGElement)it.next(); + if (ele instanceof RenderableElement) + { + RenderableElement rendEle = (RenderableElement)ele; + + rendEle.pick(xPoint, retVec); + } + } + } + + public void render(Graphics2D g) throws SVGException + { + //Don't process if not visible + StyleAttribute styleAttrib = new StyleAttribute(); + if (getStyle(styleAttrib.setName("visibility"))) + { + if (!styleAttrib.getStringValue().equals("visible")) return; + } + + //Do not process offscreen groups + boolean ignoreClip = diagram.ignoringClipHeuristic(); + if (!ignoreClip && outsideClip(g)) return; + + beginLayer(g); + + Iterator it = children.iterator(); + + try + { + g.getClipBounds(clipBounds); + } + catch (Exception e) + { + //For some reason, getClipBounds can throw a null pointer exception for + // some types of Graphics2D + ignoreClip = true; + } + + while (it.hasNext()) + { + SVGElement ele = (SVGElement)it.next(); + if (ele instanceof RenderableElement) + { + RenderableElement rendEle = (RenderableElement)ele; + +// if (shapeEle == null) continue; + + if (!(ele instanceof Group)) + { + //Skip if clipping area is outside our bounds + if (!ignoreClip && !rendEle.getBoundingBox().intersects(clipBounds)) + { + continue; + } + } + + rendEle.render(g); + } + } + + finishLayer(g); + } + + + /** + * Retrieves the cached bounding box of this group + */ + public Shape getShape() + { + if (cachedShape == null) calcShape(); + return cachedShape; + } + + public void calcShape() + { + Area retShape = new Area(); + + for (Iterator it = children.iterator(); it.hasNext();) + { + SVGElement ele = (SVGElement)it.next(); + + if (ele instanceof ShapeElement) + { + ShapeElement shpEle = (ShapeElement)ele; + Shape shape = shpEle.getShape(); + if (shape != null) + { + retShape.add(new Area(shape)); + } + } + } + + cachedShape = shapeToParent(retShape); + } + + /** + * Retrieves the cached bounding box of this group + */ + public Rectangle2D getBoundingBox() throws SVGException + { + if (boundingBox == null) calcBoundingBox(); +// calcBoundingBox(); + return boundingBox; + } + + /** + * Recalculates the bounding box by taking the union of the bounding boxes + * of all children. Caches the result. + */ + public void calcBoundingBox() throws SVGException + { +// Rectangle2D retRect = new Rectangle2D.Float(); + Rectangle2D retRect = null; + + for (Iterator it = children.iterator(); it.hasNext();) + { + SVGElement ele = (SVGElement)it.next(); + + if (ele instanceof RenderableElement) + { + RenderableElement rendEle = (RenderableElement)ele; + Rectangle2D bounds = rendEle.getBoundingBox(); + if (bounds != null) + { + if (retRect == null) retRect = bounds; + else retRect = retRect.createUnion(bounds); + } + } + } + +// if (xform != null) +// { +// retRect = xform.createTransformedShape(retRect).getBounds2D(); +// } + + //If no contents, use degenerate rectangle + if (retRect == null) retRect = new Rectangle2D.Float(); + + boundingBox = boundsToParent(retRect); + } + + public boolean updateTime(double curTime) throws SVGException + { + boolean changeState = super.updateTime(curTime); + Iterator it = children.iterator(); + + //Distribute message to all members of this group + while (it.hasNext()) + { + SVGElement ele = (SVGElement)it.next(); + boolean updateVal = ele.updateTime(curTime); + + changeState = changeState || updateVal; + + //Update our shape if shape aware children change + if (ele instanceof ShapeElement) cachedShape = null; + if (ele instanceof RenderableElement) boundingBox = null; + } + + return changeState; + } +} diff --git a/src/main/java/com/kitfox/svg/ImageSVG.java b/src/main/java/com/kitfox/svg/ImageSVG.java new file mode 100644 index 0000000..2d981bc --- /dev/null +++ b/src/main/java/com/kitfox/svg/ImageSVG.java @@ -0,0 +1,299 @@ +/* + * Font.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 February 20, 2004, 10:00 PM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import com.kitfox.svg.xml.*; + +import java.awt.*; +import java.awt.geom.*; +import java.awt.image.*; +import java.util.*; +import java.net.*; + +/** + * Implements an embedded font. + * + * SVG specification: http://www.w3.org/TR/SVG/fonts.html + * + * @author Mark McKay + * @author Mark McKay + */ +public class ImageSVG extends RenderableElement +{ + float x = 0f; + float y = 0f; + float width = 0f; + float height = 0f; + +// BufferedImage href = null; + URL imageSrc = null; + + AffineTransform xform; + Rectangle2D bounds; + + /** Creates a new instance of Font */ + public ImageSVG() + { + } + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + if (getPres(sty.setName("x"))) x = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("y"))) y = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("width"))) width = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("height"))) height = sty.getFloatValueWithUnits(); + + try { + if (getPres(sty.setName("xlink:href"))) + { + URI src = sty.getURIValue(getXMLBase()); + imageSrc = src.toURL(); + } + } + catch (Exception e) + { + throw new SVGException(e); + } + + + diagram.getUniverse().registerImage(imageSrc); + + //Set widths if not set + BufferedImage img = diagram.getUniverse().getImage(imageSrc); + if (img == null) + { + xform = new AffineTransform(); + bounds = new Rectangle2D.Float(); + return; + } + + if (width == 0) width = img.getWidth(); + if (height == 0) height = img.getHeight(); + + //Determine image xform + xform = new AffineTransform(); +// xform.setToScale(this.width / img.getWidth(), this.height / img.getHeight()); +// xform.translate(this.x, this.y); + xform.translate(this.x, this.y); + xform.scale(this.width / img.getWidth(), this.height / img.getHeight()); + + bounds = new Rectangle2D.Float(this.x, this.y, this.width, this.height); + } + + + + public float getX() { return x; } + public float getY() { return y; } + public float getWidth() { return width; } + public float getHeight() { return height; } + + void pick(Point2D point, Vector retVec) throws SVGException + { + /* + Point2D xPoint = new Point2D.Double(); + try + { + xform.inverseTransform(point, xPoint); + } + catch (NoninvertibleTransformException ex) + { + throw new SVGException(ex); + } + */ + +// if (bounds.contains(xPoint)) + if (getBoundingBox().contains(point)) + { + retVec.add(getPath(null)); + } + } + + public void render(Graphics2D g) throws SVGException + { + StyleAttribute styleAttrib = new StyleAttribute(); + if (getStyle(styleAttrib.setName("visibility"))) + { + if (!styleAttrib.getStringValue().equals("visible")) return; + } + + beginLayer(g); + + float opacity = 1f; + if (getStyle(styleAttrib.setName("opacity"))) + { + opacity = styleAttrib.getRatioValue(); + } + + if (opacity <= 0) return; + + Composite oldComp = null; + + if (opacity < 1) + { + oldComp = g.getComposite(); + Composite comp = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity); + g.setComposite(comp); + } + + BufferedImage img = diagram.getUniverse().getImage(imageSrc); + if (img == null) return; + + AffineTransform curXform = g.getTransform(); + g.transform(xform); + + g.drawImage(img, 0, 0, null); + + g.setTransform(curXform); + if (oldComp != null) g.setComposite(oldComp); + + finishLayer(g); + } + + public Rectangle2D getBoundingBox() + { + return boundsToParent(bounds); + } + + /** + * 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) throws SVGException + { +// if (trackManager.getNumTracks() == 0) return false; + boolean changeState = super.updateTime(curTime); + + //Get current values for parameters + StyleAttribute sty = new StyleAttribute(); + boolean shapeChange = false; + + if (getPres(sty.setName("x"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != x) + { + x = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("y"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != y) + { + y = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("width"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != width) + { + width = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("height"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != height) + { + height = newVal; + shapeChange = true; + } + } + + try { + if (getPres(sty.setName("xlink:href"))) + { + URI src = sty.getURIValue(getXMLBase()); + URL newVal = src.toURL(); + + if (!newVal.equals(imageSrc)) + { + imageSrc = newVal; + shapeChange = true; + } + } + } + catch (IllegalArgumentException ie) + { + new Exception("Image provided with illegal value for href: \"" + sty.getStringValue() + '"', ie).printStackTrace(); + } + catch (Exception e) + { + e.printStackTrace(); + } + + + if (shapeChange) + { + diagram.getUniverse().registerImage(imageSrc); + + //Set widths if not set + BufferedImage img = diagram.getUniverse().getImage(imageSrc); + if (img == null) + { + xform = new AffineTransform(); + bounds = new Rectangle2D.Float(); + } + else + { + if (width == 0) width = img.getWidth(); + if (height == 0) height = img.getHeight(); + + //Determine image xform + xform = new AffineTransform(); +// xform.setToScale(this.width / img.getWidth(), this.height / img.getHeight()); +// xform.translate(this.x, this.y); + xform.translate(this.x, this.y); + xform.scale(this.width / img.getWidth(), this.height / img.getHeight()); + + bounds = new Rectangle2D.Float(this.x, this.y, this.width, this.height); + } + + return true; + } + + return changeState; + } +} diff --git a/src/main/java/com/kitfox/svg/Line.java b/src/main/java/com/kitfox/svg/Line.java new file mode 100644 index 0000000..f52a9c8 --- /dev/null +++ b/src/main/java/com/kitfox/svg/Line.java @@ -0,0 +1,172 @@ +/* + * Rect.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 26, 2004, 5:25 PM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +import java.awt.*; +import java.awt.geom.*; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class Line extends ShapeElement { + + float x1 = 0f; + float y1 = 0f; + float x2 = 0f; + float y2 = 0f; + + Line2D.Float line; +// RectangularShape rect; + + /** Creates a new instance of Rect */ + public Line() { + } + + /* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + String x1 = attrs.getValue("x1"); + String y1 = attrs.getValue("y1"); + String x2 = attrs.getValue("x2"); + String y2 = attrs.getValue("y2"); + + this.x1 = XMLParseUtil.parseFloat(x1); + this.y1 = XMLParseUtil.parseFloat(y1); + this.x2 = XMLParseUtil.parseFloat(x2); + this.y2 = XMLParseUtil.parseFloat(y2); + + build(); + } +*/ + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + if (getPres(sty.setName("x1"))) x1 = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("y1"))) y1 = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("x2"))) x2 = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("y2"))) y2 = sty.getFloatValueWithUnits(); + + line = new Line2D.Float(x1, y1, x2, y2); + } + + + public void render(Graphics2D g) throws SVGException + { + beginLayer(g); + renderShape(g, line); + finishLayer(g); + } + + public Shape getShape() + { + return shapeToParent(line); + } + + public Rectangle2D getBoundingBox() throws SVGException + { + return boundsToParent(includeStrokeInBounds(line.getBounds2D())); + } + + /** + * 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) throws SVGException + { +// if (trackManager.getNumTracks() == 0) return false; + boolean changeState = super.updateTime(curTime); + + //Get current values for parameters + StyleAttribute sty = new StyleAttribute(); + boolean shapeChange = false; + + if (getPres(sty.setName("x1"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != x1) + { + x1 = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("y1"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != y1) + { + y1 = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("x2"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != x2) + { + x2 = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("y2"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != y2) + { + y2 = newVal; + shapeChange = true; + } + } + + if (shapeChange) + { + line = new Line2D.Float(x1, y1, x2, y2); + return true; + } + + return changeState; + } +} diff --git a/src/main/java/com/kitfox/svg/LinearGradient.java b/src/main/java/com/kitfox/svg/LinearGradient.java new file mode 100644 index 0000000..06a1503 --- /dev/null +++ b/src/main/java/com/kitfox/svg/LinearGradient.java @@ -0,0 +1,210 @@ +/* + * LinearGradient.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 26, 2004, 1:54 AM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import java.awt.geom.*; +import java.awt.*; + +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +//import org.apache.batik.ext.awt.*; +import com.kitfox.svg.batik.*; + + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class LinearGradient extends Gradient { + + float x1 = 0f; + float y1 = 0f; + float x2 = 1f; + float y2 = 0f; + + /** Creates a new instance of LinearGradient */ + public LinearGradient() { + } +/* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + String x1 = attrs.getValue("x1"); + String x2 = attrs.getValue("x2"); + String y1 = attrs.getValue("y1"); + String y2 = attrs.getValue("y2"); + + if (x1 != null) this.x1 = (float)XMLParseUtil.parseRatio(x1); + if (y1 != null) this.y1 = (float)XMLParseUtil.parseRatio(y1); + if (x2 != null) this.x2 = (float)XMLParseUtil.parseRatio(x2); + if (y2 != null) this.y2 = (float)XMLParseUtil.parseRatio(y2); + } +*/ + /* + public void loaderEndElement(SVGLoaderHelper helper) + { + super.loaderEndElement(helper); + + build(); + } + */ + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + if (getPres(sty.setName("x1"))) x1 = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("y1"))) y1 = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("x2"))) x2 = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("y2"))) y2 = sty.getFloatValueWithUnits(); + } + + public Paint getPaint(Rectangle2D bounds, AffineTransform xform) + { + com.kitfox.svg.batik.MultipleGradientPaint.CycleMethodEnum method; + switch (spreadMethod) + { + default: + case SM_PAD: + method = com.kitfox.svg.batik.MultipleGradientPaint.NO_CYCLE; + break; + case SM_REPEAT: + method = com.kitfox.svg.batik.MultipleGradientPaint.REPEAT; + break; + case SM_REFLECT: + method = com.kitfox.svg.batik.MultipleGradientPaint.REFLECT; + break; + } + + com.kitfox.svg.batik.LinearGradientPaint paint; + if (gradientUnits == GU_USER_SPACE_ON_USE) + { +// paint = new LinearGradientPaint(x1, y1, x2, y2, getStopFractions(), getStopColors(), method); + paint = new com.kitfox.svg.batik.LinearGradientPaint( + new Point2D.Float(x1, y1), + new Point2D.Float(x2, y2), + getStopFractions(), + getStopColors(), + method, + com.kitfox.svg.batik.MultipleGradientPaint.SRGB, + gradientTransform); + } + else + { + AffineTransform viewXform = new AffineTransform(); + viewXform.translate(bounds.getX(), bounds.getY()); + + //This is a hack to get around shapes that have a width or height of 0. Should be close enough to the true answer. + double width = bounds.getWidth(); + double height = bounds.getHeight(); + if (width == 0) width = 1; + if (height == 0) height = 1; + viewXform.scale(width, height); + + viewXform.concatenate(gradientTransform); + + paint = new com.kitfox.svg.batik.LinearGradientPaint( + new Point2D.Float(x1, y1), + new Point2D.Float(x2, y2), + getStopFractions(), + getStopColors(), + method, + com.kitfox.svg.batik.MultipleGradientPaint.SRGB, + viewXform); + } + + return paint; + } + + /** + * 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) throws SVGException + { +// if (trackManager.getNumTracks() == 0) return stopChange; + boolean changeState = super.updateTime(curTime); + + //Get current values for parameters + StyleAttribute sty = new StyleAttribute(); + boolean shapeChange = false; + + if (getPres(sty.setName("x1"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != x1) + { + x1 = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("y1"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != y1) + { + y1 = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("x2"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != x2) + { + x2 = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("y2"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != y2) + { + y2 = newVal; + shapeChange = true; + } + } + + return changeState || shapeChange; + } +} diff --git a/src/main/java/com/kitfox/svg/Metadata.java b/src/main/java/com/kitfox/svg/Metadata.java new file mode 100644 index 0000000..554c301 --- /dev/null +++ b/src/main/java/com/kitfox/svg/Metadata.java @@ -0,0 +1,46 @@ +/* + * Stop.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 19, 2004, 1:56 AM + */ + +package com.kitfox.svg; + +/** + * Does not hold any information. Included to allow metadata tag to be parsed. + * + * @author Mark McKay + * @author Mark McKay + */ +public class Metadata extends SVGElement +{ + /** Creates a new instance of Stop */ + public Metadata() { + } + + public boolean updateTime(double curTime) + { + return false; + } +} diff --git a/src/main/java/com/kitfox/svg/MissingGlyph.java b/src/main/java/com/kitfox/svg/MissingGlyph.java new file mode 100644 index 0000000..bc0cd2c --- /dev/null +++ b/src/main/java/com/kitfox/svg/MissingGlyph.java @@ -0,0 +1,252 @@ +/* + * Font.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 February 20, 2004, 10:00 PM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +import java.awt.*; +import java.awt.geom.*; +import java.util.*; + +import com.kitfox.svg.pathcmd.*; +//import org.apache.batik.ext.awt.geom.ExtendedGeneralPath; + +/** + * Implements an embedded font. + * + * SVG specification: http://www.w3.org/TR/SVG/fonts.html + * + * @author Mark McKay + * @author Mark McKay + */ +public class MissingGlyph extends ShapeElement +{ + //We may define a path +// ExtendedGeneralPath path = null; + Shape path = null; + + //Alternately, we may have child graphical elements +// Vector members = null; + + int horizAdvX = -1; //Inherits font's value if not set + int vertOriginX = -1; //Inherits font's value if not set + int vertOriginY = -1; //Inherits font's value if not set + int vertAdvY = -1; //Inherits font's value if not set + + /** Creates a new instance of Font */ + public MissingGlyph() + { + } +/* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + //If glyph path was specified, calculate it + String commandList = attrs.getValue("d"); + if (commandList != null) + { + StyleAttribute atyleAttrib = getStyle("fill-rule"); + String fillRule = (atyleAttrib == null) ? "nonzero" : atyleAttrib.getStringValue(); + + PathCommand[] commands = parsePathList(commandList); + +// ExtendedGeneralPath buildPath = new ExtendedGeneralPath( + GeneralPath buildPath = new GeneralPath( + fillRule.equals("evenodd") ? GeneralPath.WIND_EVEN_ODD : GeneralPath.WIND_NON_ZERO, + commands.length); + + BuildHistory hist = new BuildHistory(); + + for (int i = 0; i < commands.length; i++) + { + PathCommand cmd = commands[i]; + cmd.appendPath(buildPath, hist); + } + + //Reflect glyph path to put it in user coordinate system + AffineTransform at = new AffineTransform(); + at.scale(1, -1); + path = at.createTransformedShape(buildPath); + } + + + //Read glyph spacing info + String horizAdvX = attrs.getValue("horiz-adv-x"); + String vertOriginX = attrs.getValue("vert-origin-x"); + String vertOriginY = attrs.getValue("vert-origin-y"); + String vertAdvY = attrs.getValue("vert-adv-y"); + + if (horizAdvX != null) this.horizAdvX = XMLParseUtil.parseInt(horizAdvX); + if (vertOriginX != null) this.vertOriginX = XMLParseUtil.parseInt(vertOriginX); + if (vertOriginY != null) this.vertOriginY = XMLParseUtil.parseInt(vertOriginY); + if (vertAdvY != null) this.vertAdvY = XMLParseUtil.parseInt(vertAdvY); + + } +*/ + /** + * Called after the start element but before the end element to indicate + * each child tag that has been processed + */ + public void loaderAddChild(SVGLoaderHelper helper, SVGElement child) throws SVGElementException + { + super.loaderAddChild(helper, child); + +// if (members == null) members = new Vector(); +// members.add(child); + } + + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + String commandList = ""; + if (getPres(sty.setName("d"))) commandList = sty.getStringValue(); + + + //If glyph path was specified, calculate it + if (commandList != null) + { +// StyleAttribute atyleAttrib = getStyle("fill-rule"); + String fillRule = getStyle(sty.setName("fill-rule")) ? sty.getStringValue() : "nonzero"; + + PathCommand[] commands = parsePathList(commandList); + +// ExtendedGeneralPath buildPath = new ExtendedGeneralPath( + GeneralPath buildPath = new GeneralPath( + fillRule.equals("evenodd") ? GeneralPath.WIND_EVEN_ODD : GeneralPath.WIND_NON_ZERO, + commands.length); + + BuildHistory hist = new BuildHistory(); + + for (int i = 0; i < commands.length; i++) + { + PathCommand cmd = commands[i]; + cmd.appendPath(buildPath, hist); + } + + //Reflect glyph path to put it in user coordinate system + AffineTransform at = new AffineTransform(); + at.scale(1, -1); + path = at.createTransformedShape(buildPath); + } + + + //Read glyph spacing info + if (getPres(sty.setName("horiz-adv-x"))) horizAdvX = sty.getIntValue(); + + if (getPres(sty.setName("vert-origin-x"))) vertOriginX = sty.getIntValue(); + + if (getPres(sty.setName("vert-origin-y"))) vertOriginY = sty.getIntValue(); + + if (getPres(sty.setName("vert-adv-y"))) vertAdvY = sty.getIntValue(); + } + + public Shape getPath() + { + return path; + } + + public void render(Graphics2D g) throws SVGException + { + //Do not push or pop stack + + if (path != null) renderShape(g, path); + + Iterator it = children.iterator(); + while (it.hasNext()) + { + SVGElement ele = (SVGElement)it.next(); + if (ele instanceof RenderableElement) + { + ((RenderableElement)ele).render(g); + } + } + + //Do not push or pop stack + } + + public int getHorizAdvX() + { + if (horizAdvX == -1) + horizAdvX = ((Font)parent).getHorizAdvX(); + return horizAdvX; + } + + public int getVertOriginX() + { + if (vertOriginX == -1) + vertOriginX = getHorizAdvX() / 2; + return vertOriginX; + } + + public int getVertOriginY() + { + if (vertOriginY == -1) + vertOriginY = ((Font)parent).getFontFace().getAscent(); + return vertOriginY; + } + + public int getVertAdvY() + { + if (vertAdvY == -1) + vertAdvY = ((Font)parent).getFontFace().getUnitsPerEm(); + return vertAdvY; + + } + + public Shape getShape() + { + if (path != null) return shapeToParent(path); + return null; + } + + public Rectangle2D getBoundingBox() throws SVGException + { + if (path != null) return boundsToParent(includeStrokeInBounds(path.getBounds2D())); + return 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) throws SVGException + { + //Fonts can't change + return false; + } +} diff --git a/src/main/java/com/kitfox/svg/Path.java b/src/main/java/com/kitfox/svg/Path.java new file mode 100644 index 0000000..85feff3 --- /dev/null +++ b/src/main/java/com/kitfox/svg/Path.java @@ -0,0 +1,153 @@ +/* + * Rect.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 26, 2004, 5:25 PM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import java.awt.*; +import java.awt.geom.*; + +import com.kitfox.svg.pathcmd.*; +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +//import org.apache.batik.ext.awt.geom.ExtendedGeneralPath; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class Path extends ShapeElement { + +// PathCommand[] commands = null; + + int fillRule = GeneralPath.WIND_NON_ZERO; + String d = ""; +// ExtendedGeneralPath path; + GeneralPath path; + + /** Creates a new instance of Rect */ + public Path() { + } + + /* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + StyleAttribute styleAttrib = getStyle("fill-rule"); + String fillRule = (styleAttrib == null) ? "nonzero" : styleAttrib.getStringValue(); + + String d = attrs.getValue("d"); + path = buildPath(d, fillRule.equals("evenodd") ? GeneralPath.WIND_EVEN_ODD : GeneralPath.WIND_NON_ZERO); + } + */ + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + + String fillRuleStrn = (getStyle(sty.setName("fill-rule"))) ? sty.getStringValue() : "nonzero"; + fillRule = fillRuleStrn.equals("evenodd") ? GeneralPath.WIND_EVEN_ODD : GeneralPath.WIND_NON_ZERO; + +// String d = ""; + if (getPres(sty.setName("d"))) d = sty.getStringValue(); + +//System.err.println(d); + + path = buildPath(d, fillRule); + +//System.err.println(d); + } + + public void render(Graphics2D g) throws SVGException + { + beginLayer(g); + renderShape(g, path); + finishLayer(g); + } + + public Shape getShape() + { + return shapeToParent(path); + } + + public Rectangle2D getBoundingBox() throws SVGException + { + return boundsToParent(includeStrokeInBounds(path.getBounds2D())); + } + + /** + * 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) throws SVGException + { +// if (trackManager.getNumTracks() == 0) return false; + boolean changeState = super.updateTime(curTime); + + //Get current values for parameters + StyleAttribute sty = new StyleAttribute(); + boolean shapeChange = false; + + if (getStyle(sty.setName("fill-rule"))) + { + int newVal = sty.getStringValue().equals("evenodd") + ? GeneralPath.WIND_EVEN_ODD + : GeneralPath.WIND_NON_ZERO; + if (newVal != fillRule) + { + fillRule = newVal; + changeState = true; + } + } + + if (getPres(sty.setName("d"))) + { + String newVal = sty.getStringValue(); + if (!newVal.equals(d)) + { + d = newVal; + shapeChange = true; + } + } + + if (shapeChange) + { + path = buildPath(d, fillRule); + return true; + } + + return changeState; + } +} diff --git a/src/main/java/com/kitfox/svg/PatternSVG.java b/src/main/java/com/kitfox/svg/PatternSVG.java new file mode 100644 index 0000000..c1a32e9 --- /dev/null +++ b/src/main/java/com/kitfox/svg/PatternSVG.java @@ -0,0 +1,303 @@ +/* + * Gradient.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 26, 2004, 3:25 AM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import java.net.*; +import java.util.*; +import java.awt.geom.*; +import java.awt.*; +import java.awt.image.*; + +import com.kitfox.svg.pattern.*; +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class PatternSVG extends FillElement { + + public static final int GU_OBJECT_BOUNDING_BOX = 0; + public static final int GU_USER_SPACE_ON_USE = 1; + + int gradientUnits = GU_OBJECT_BOUNDING_BOX; + + float x; + float y; + float width; + float height; + + AffineTransform patternXform = new AffineTransform(); + Rectangle2D.Float viewBox; + +// final Vector members = new Vector(); + + Paint texPaint; + + /** Creates a new instance of Gradient */ + public PatternSVG() { + } +/* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + String href = attrs.getValue("xlink:href"); + //If we have a link to another pattern, initialize ourselves with it's values + if (href != null) + { +//System.err.println("Gradient.loaderStartElement() href '" + href + "'"); + try { + URI src = getXMLBase().resolve(href); +// URL url = srcUrl.toURL(); +// URL url = new URL(helper.docRoot, href); + PatternSVG patSrc = (PatternSVG)helper.universe.getElement(src); + + gradientUnits = patSrc.gradientUnits; + x = patSrc.x; + y = patSrc.y; + width = patSrc.width; + height = patSrc.height; + viewBox = patSrc.viewBox; + patternXform.setTransform(patSrc.patternXform); + members.addAll(patSrc.members); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + + String gradientUnits = attrs.getValue("gradientUnits"); + + if (gradientUnits != null) + { + if (gradientUnits.toLowerCase().equals("userspaceonuse")) this.gradientUnits = GU_USER_SPACE_ON_USE; + else this.gradientUnits = GU_OBJECT_BOUNDING_BOX; + } + + String patternTransform = attrs.getValue("patternTransform"); + if (patternTransform != null) + { + patternXform = parseTransform(patternTransform); + } + + String x = attrs.getValue("x"); + String y = attrs.getValue("y"); + String width = attrs.getValue("width"); + String height = attrs.getValue("height"); + + if (x != null) this.x = XMLParseUtil.parseFloat(x); + if (y != null) this.y = XMLParseUtil.parseFloat(y); + if (width != null) this.width = XMLParseUtil.parseFloat(width); + if (height != null) this.height = XMLParseUtil.parseFloat(height); + + String viewBoxStrn = attrs.getValue("viewBox"); + if (viewBoxStrn != null) + { + float[] dim = XMLParseUtil.parseFloatList(viewBoxStrn); + viewBox = new Rectangle2D.Float(dim[0], dim[1], dim[2], dim[3]); + } + } + */ + /** + * Called after the start element but before the end element to indicate + * each child tag that has been processed + */ + public void loaderAddChild(SVGLoaderHelper helper, SVGElement child) throws SVGElementException + { + super.loaderAddChild(helper, child); + +// members.add(child); + } + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + //Load style string + String href = null; + if (getPres(sty.setName("xlink:href"))) href = sty.getStringValue(); + //String href = attrs.getValue("xlink:href"); + //If we have a link to another pattern, initialize ourselves with it's values + if (href != null) + { +//System.err.println("Gradient.loaderStartElement() href '" + href + "'"); + try { + URI src = getXMLBase().resolve(href); + PatternSVG patSrc = (PatternSVG)diagram.getUniverse().getElement(src); + + gradientUnits = patSrc.gradientUnits; + x = patSrc.x; + y = patSrc.y; + width = patSrc.width; + height = patSrc.height; + viewBox = patSrc.viewBox; + patternXform.setTransform(patSrc.patternXform); + children.addAll(patSrc.children); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + String gradientUnits = ""; + if (getPres(sty.setName("gradientUnits"))) gradientUnits = sty.getStringValue().toLowerCase(); + if (gradientUnits.equals("userspaceonuse")) this.gradientUnits = GU_USER_SPACE_ON_USE; + else this.gradientUnits = GU_OBJECT_BOUNDING_BOX; + + String patternTransform = ""; + if (getPres(sty.setName("patternTransform"))) patternTransform = sty.getStringValue(); + patternXform = parseTransform(patternTransform); + + + if (getPres(sty.setName("x"))) x = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("y"))) y = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("width"))) width = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("height"))) height = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("viewBox"))) + { + float[] dim = sty.getFloatList(); + viewBox = new Rectangle2D.Float(dim[0], dim[1], dim[2], dim[3]); + } + + preparePattern(); + } + +/* + public void loaderEndElement(SVGLoaderHelper helper) + { + build(); + } + */ + + protected void preparePattern() throws SVGException + { + //For now, treat all fills as UserSpaceOnUse. Otherwise, we'll need + // a different paint for every object. + int tileWidth = (int)width; + int tileHeight = (int)height; + + float stretchX = 1f, stretchY = 1f; + if (!patternXform.isIdentity()) + { + //Scale our source tile so that we can have nice sampling from it. + float xlateX = (float)patternXform.getTranslateX(); + float xlateY = (float)patternXform.getTranslateY(); + + Point2D.Float pt = new Point2D.Float(), pt2 = new Point2D.Float(); + + pt.setLocation(width, 0); + patternXform.transform(pt, pt2); + pt2.x -= xlateX; + pt2.y -= xlateY; + stretchX = (float)Math.sqrt(pt2.x * pt2.x + pt2.y * pt2.y) * 1.5f / width; + + pt.setLocation(height, 0); + patternXform.transform(pt, pt2); + pt2.x -= xlateX; + pt2.y -= xlateY; + stretchY = (float)Math.sqrt(pt2.x * pt2.x + pt2.y * pt2.y) * 1.5f / height; + + tileWidth *= stretchX; + tileHeight *= stretchY; + } + + + BufferedImage buf = new BufferedImage(tileWidth, tileHeight, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = buf.createGraphics(); + g.setClip(0, 0, tileWidth, tileHeight); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + for (Iterator it = children.iterator(); it.hasNext();) + { + SVGElement ele = (SVGElement)it.next(); + if (ele instanceof RenderableElement) + { + AffineTransform xform = new AffineTransform(); + + if (viewBox == null) + { + xform.translate(-x, -y); + } + else + { + xform.scale(tileWidth / viewBox.width, tileHeight / viewBox.height); + xform.translate(-viewBox.x, -viewBox.y); + } + + g.setTransform(xform); + ((RenderableElement)ele).render(g); + } + } + + g.dispose(); + +//try { +//javax.imageio.ImageIO.write(buf, "png", new java.io.File("c:\\tmp\\texPaint.png")); +//} catch (Exception e ) {} + + if (patternXform.isIdentity()) + { + texPaint = new TexturePaint(buf, new Rectangle2D.Float(x, y, width, height)); + } + else + { + patternXform.scale(1 / stretchX, 1 / stretchY); + texPaint = new PatternPaint(buf, patternXform); + } + } + + public Paint getPaint(Rectangle2D bounds, AffineTransform xform) + { + return texPaint; + } + + /** + * 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) throws SVGException + { + //Patterns don't change state + return false; + } +} diff --git a/src/main/java/com/kitfox/svg/Polygon.java b/src/main/java/com/kitfox/svg/Polygon.java new file mode 100644 index 0000000..56f2997 --- /dev/null +++ b/src/main/java/com/kitfox/svg/Polygon.java @@ -0,0 +1,177 @@ +/* + * Rect.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 26, 2004, 5:25 PM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import com.kitfox.svg.xml.XMLParseUtil; +import java.awt.geom.*; +import java.awt.*; + +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class Polygon extends ShapeElement { + + int fillRule = GeneralPath.WIND_NON_ZERO; + String pointsStrn = ""; +// float[] points = null; + GeneralPath path; + + /** Creates a new instance of Rect */ + public Polygon() { + } +/* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + + points = XMLParseUtil.parseFloatList(attrs.getValue("points")); + + build(); + } +*/ +/* + public void build() + { + StyleAttribute styleAttrib = getStyle("fill-rule"); + String fillRule = (styleAttrib == null) ? "nonzero" : styleAttrib.getStringValue(); + + path = new GeneralPath( + fillRule.equals("evenodd") ? GeneralPath.WIND_EVEN_ODD : GeneralPath.WIND_NON_ZERO, + points.length / 2); + + path.moveTo(points[0], points[1]); + for (int i = 2; i < points.length; i += 2) + { + path.lineTo(points[i], points[i + 1]); + } + path.closePath(); + } +*/ + +//static int yyyyy = 0; + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + if (getPres(sty.setName("points"))) pointsStrn = sty.getStringValue(); + + String fillRuleStrn = getStyle(sty.setName("fill-rule")) ? sty.getStringValue() : "nonzero"; + fillRule = fillRuleStrn.equals("evenodd") ? GeneralPath.WIND_EVEN_ODD : GeneralPath.WIND_NON_ZERO; + + buildPath(); + } + + protected void buildPath() + { + float[] points = XMLParseUtil.parseFloatList(pointsStrn); + path = new GeneralPath(fillRule, points.length / 2); + + path.moveTo(points[0], points[1]); + for (int i = 2; i < points.length; i += 2) + { + path.lineTo(points[i], points[i + 1]); + } + path.closePath(); + } + + public void render(Graphics2D g) throws SVGException + { + beginLayer(g); + renderShape(g, path); + finishLayer(g); + } + + + public Shape getShape() + { + return shapeToParent(path); + } + + public Rectangle2D getBoundingBox() throws SVGException + { + return boundsToParent(includeStrokeInBounds(path.getBounds2D())); + } + + + /** + * 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) throws SVGException + { +// if (trackManager.getNumTracks() == 0) return false; + boolean changeState = super.updateTime(curTime); + + //Get current values for parameters + StyleAttribute sty = new StyleAttribute(); + boolean shapeChange = false; + + if (getStyle(sty.setName("fill-rule"))) + { + int newVal = sty.getStringValue().equals("evenodd") + ? GeneralPath.WIND_EVEN_ODD + : GeneralPath.WIND_NON_ZERO; + if (newVal != fillRule) + { + fillRule = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("points"))) + { + String newVal = sty.getStringValue(); + if (!newVal.equals(pointsStrn)) + { + pointsStrn = newVal; + shapeChange = true; + } + } + + + if (shapeChange) + { + buildPath(); + return true; + } + + return changeState; + } +} diff --git a/src/main/java/com/kitfox/svg/Polyline.java b/src/main/java/com/kitfox/svg/Polyline.java new file mode 100644 index 0000000..335dd87 --- /dev/null +++ b/src/main/java/com/kitfox/svg/Polyline.java @@ -0,0 +1,155 @@ +/* + * Rect.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 26, 2004, 5:25 PM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import com.kitfox.svg.xml.XMLParseUtil; +import java.awt.geom.*; +import java.awt.*; + +import com.kitfox.svg.xml.*; +import java.util.Vector; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class Polyline extends ShapeElement { + + int fillRule = GeneralPath.WIND_NON_ZERO; + String pointsStrn = ""; +// float[] points = null; + GeneralPath path; + + /** Creates a new instance of Rect */ + public Polyline() { + } + + /* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + + points = XMLParseUtil.parseFloatList(attrs.getValue("points")); + + build(); + } + */ + + public void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + if (getPres(sty.setName("points"))) pointsStrn = sty.getStringValue(); + + String fillRuleStrn = getStyle(sty.setName("fill-rule")) ? sty.getStringValue() : "nonzero"; + fillRule = fillRuleStrn.equals("evenodd") ? GeneralPath.WIND_EVEN_ODD : GeneralPath.WIND_NON_ZERO; + + buildPath(); + } + + protected void buildPath() + { + float[] points = XMLParseUtil.parseFloatList(pointsStrn); + path = new GeneralPath(fillRule, points.length / 2); + + path.moveTo(points[0], points[1]); + for (int i = 2; i < points.length; i += 2) + { + path.lineTo(points[i], points[i + 1]); + } + } + + public void render(Graphics2D g) throws SVGException + { + beginLayer(g); + renderShape(g, path); + finishLayer(g); + } + + public Shape getShape() + { + return shapeToParent(path); + } + + public Rectangle2D getBoundingBox() throws SVGException + { + return boundsToParent(includeStrokeInBounds(path.getBounds2D())); + } + + /** + * 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) throws SVGException + { +// if (trackManager.getNumTracks() == 0) return false; + boolean changeState = super.updateTime(curTime); + + //Get current values for parameters + StyleAttribute sty = new StyleAttribute(); + boolean shapeChange = false; + + if (getStyle(sty.setName("fill-rule"))) + { + int newVal = sty.getStringValue().equals("evenodd") + ? GeneralPath.WIND_EVEN_ODD + : GeneralPath.WIND_NON_ZERO; + if (newVal != fillRule) + { + fillRule = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("points"))) + { + String newVal = sty.getStringValue(); + if (!newVal.equals(pointsStrn)) + { + pointsStrn = newVal; + shapeChange = true; + } + } + + + if (shapeChange) + { + buildPath(); + return true; + } + + return changeState; + } +} diff --git a/src/main/java/com/kitfox/svg/RadialGradient.java b/src/main/java/com/kitfox/svg/RadialGradient.java new file mode 100644 index 0000000..37b9414 --- /dev/null +++ b/src/main/java/com/kitfox/svg/RadialGradient.java @@ -0,0 +1,222 @@ +/* + * RadialGradient.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 26, 2004, 1:55 AM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import java.awt.geom.*; +import java.awt.*; + +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +//import org.apache.batik.ext.awt.*; +import com.kitfox.svg.batik.*; + + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class RadialGradient extends Gradient { + + float cx = 0.5f; + float cy = 0.5f; + float fx = 0.5f; + float fy = 0.5f; + float r = 0.5f; + + /** Creates a new instance of RadialGradient */ + public RadialGradient() { + } +/* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + String cx = attrs.getValue("cx"); + String cy = attrs.getValue("cy"); + String fx = attrs.getValue("fx"); + String fy = attrs.getValue("fy"); + String r = attrs.getValue("r"); + + if (cx != null) this.cx = (float)XMLParseUtil.parseRatio(cx); + if (cy != null) this.cy = (float)XMLParseUtil.parseRatio(cy); + if (fx != null) this.fx = (float)XMLParseUtil.parseRatio(fx); + if (fy != null) this.fy = (float)XMLParseUtil.parseRatio(fy); + if (r != null) this.r = (float)XMLParseUtil.parseRatio(r); + } + */ + + /* + public void loaderEndElement(SVGLoaderHelper helper) + { + super.loaderEndElement(helper); + + build(); + } + */ + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + if (getPres(sty.setName("cx"))) cx = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("cy"))) cy = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("fx"))) fx = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("fy"))) fy = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("r"))) r = sty.getFloatValueWithUnits(); + } + + public Paint getPaint(Rectangle2D bounds, AffineTransform xform) + { + com.kitfox.svg.batik.MultipleGradientPaint.CycleMethodEnum method; + switch (spreadMethod) + { + default: + case SM_PAD: + method = com.kitfox.svg.batik.MultipleGradientPaint.NO_CYCLE; + break; + case SM_REPEAT: + method = com.kitfox.svg.batik.MultipleGradientPaint.REPEAT; + break; + case SM_REFLECT: + method = com.kitfox.svg.batik.MultipleGradientPaint.REFLECT; + break; + } + + com.kitfox.svg.batik.RadialGradientPaint paint; + + if (gradientUnits == GU_USER_SPACE_ON_USE) + { + paint = new com.kitfox.svg.batik.RadialGradientPaint( + new Point2D.Float(cx, cy), + r, + new Point2D.Float(fx, fy), + getStopFractions(), + getStopColors(), + method, + com.kitfox.svg.batik.MultipleGradientPaint.SRGB, + gradientTransform); + } + else + { + AffineTransform viewXform = new AffineTransform(); + viewXform.translate(bounds.getX(), bounds.getY()); + viewXform.scale(bounds.getWidth(), bounds.getHeight()); + + viewXform.concatenate(gradientTransform); + + paint = new com.kitfox.svg.batik.RadialGradientPaint( + new Point2D.Float(cx, cy), + r, + new Point2D.Float(fx, fy), + getStopFractions(), + getStopColors(), + method, + com.kitfox.svg.batik.MultipleGradientPaint.SRGB, + viewXform); + } + + return paint; + } + + /** + * 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) throws SVGException + { +// if (trackManager.getNumTracks() == 0) return false; + boolean changeState = super.updateTime(curTime); + + //Get current values for parameters + StyleAttribute sty = new StyleAttribute(); + boolean shapeChange = false; + + if (getPres(sty.setName("cx"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != cx) + { + cx = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("cy"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != cy) + { + cy = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("fx"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != fx) + { + fx = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("fy"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != fy) + { + fy = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("r"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != r) + { + r = newVal; + shapeChange = true; + } + } + + return changeState; + } +} diff --git a/src/main/java/com/kitfox/svg/Rect.java b/src/main/java/com/kitfox/svg/Rect.java new file mode 100644 index 0000000..fa59372 --- /dev/null +++ b/src/main/java/com/kitfox/svg/Rect.java @@ -0,0 +1,267 @@ +/* + * Rect.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 26, 2004, 5:25 PM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; + +import java.awt.*; +import java.awt.geom.*; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class Rect extends ShapeElement { + + float x = 0f; + float y = 0f; + float width = 0f; + float height = 0f; + float rx = 0f; + float ry = 0f; + + RectangularShape rect; + + /** Creates a new instance of Rect */ + public Rect() { + } + + private void writeObject(ObjectOutputStream out) throws IOException + { + out.writeFloat(x); + out.writeFloat(y); + out.writeFloat(width); + out.writeFloat(height); + out.writeFloat(rx); + out.writeFloat(ry); + } + + private void readObject(ObjectInputStream in) throws IOException + { + x = in.readFloat(); + y = in.readFloat(); + width = in.readFloat(); + height = in.readFloat(); + rx = in.readFloat(); + ry = in.readFloat(); + + if (rx == 0f && ry == 0f) + { + rect = new Rectangle2D.Float(x, y, width, height); + } + else + { + rect = new RoundRectangle2D.Float(x, y, width, height, rx, ry); + } + } + + /* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + String x = attrs.getValue("x"); + String y = attrs.getValue("y"); + String width = attrs.getValue("width"); + String height = attrs.getValue("height"); + String rx = attrs.getValue("rx"); + String ry = attrs.getValue("ry"); + + if (rx == null) rx = ry; + if (ry == null) ry = rx; + + this.x = XMLParseUtil.parseFloat(x); + this.y = XMLParseUtil.parseFloat(y); + this.width = XMLParseUtil.parseFloat(width); + this.height = XMLParseUtil.parseFloat(height); + if (rx != null) + { + this.rx = XMLParseUtil.parseFloat(rx); + this.ry = XMLParseUtil.parseFloat(ry); + } + + build(); +// setBounds(this.x, this.y, this.width, this.height); + } +*/ + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + +// SVGElement parent = this.getParent(); +// if (parent instanceof RenderableElement) +// { +// RenderableElement re = (RenderableElement)parent; +// Rectangle2D bounds = re.getBoundingBox(); +// bounds = null; +// } + + + if (getPres(sty.setName("x"))) x = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("y"))) y = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("width"))) width = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("height"))) height = sty.getFloatValueWithUnits(); + + boolean rxSet = false; + if (getPres(sty.setName("rx"))) { rx = sty.getFloatValueWithUnits(); rxSet = true; } + + boolean rySet = false; + if (getPres(sty.setName("ry"))) { ry = sty.getFloatValueWithUnits(); rySet = true; } + + if (!rxSet) rx = ry; + if (!rySet) ry = rx; + + + if (rx == 0f && ry == 0f) + { + rect = new Rectangle2D.Float(x, y, width, height); + } + else + { + rect = new RoundRectangle2D.Float(x, y, width, height, rx, ry); + } + } + + public void render(Graphics2D g) throws SVGException + { + beginLayer(g); + renderShape(g, rect); + finishLayer(g); + } + + public Shape getShape() + { + return shapeToParent(rect); + } + + public Rectangle2D getBoundingBox() throws SVGException + { + return boundsToParent(includeStrokeInBounds(rect.getBounds2D())); + } + + /** + * 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) throws SVGException + { +// if (trackManager.getNumTracks() == 0) return false; + boolean changeState = super.updateTime(curTime); + + //Get current values for parameters + StyleAttribute sty = new StyleAttribute(); + boolean shapeChange = false; + + if (getPres(sty.setName("x"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != x) + { + x = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("y"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != y) + { + y = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("width"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != width) + { + width = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("height"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != height) + { + height = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("rx"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != rx) + { + rx = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("ry"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != ry) + { + ry = newVal; + shapeChange = true; + } + } + + if (shapeChange) + { + if (rx == 0f && ry == 0f) + { + rect = new Rectangle2D.Float(x, y, width, height); + } + else + { + rect = new RoundRectangle2D.Float(x, y, width, height, rx, ry); + } + return true; + } + + return changeState; + } +} diff --git a/src/main/java/com/kitfox/svg/RenderableElement.java b/src/main/java/com/kitfox/svg/RenderableElement.java new file mode 100644 index 0000000..878a132 --- /dev/null +++ b/src/main/java/com/kitfox/svg/RenderableElement.java @@ -0,0 +1,161 @@ +/* + * BoundedElement.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 26, 2004, 9:00 AM + */ + +package com.kitfox.svg; + + +import com.kitfox.svg.xml.StyleAttribute; +import java.awt.geom.*; +import java.awt.*; +import java.net.*; +import java.util.LinkedList; +import java.util.Vector; + +/** + * Maintains bounding box for this element + * + * @author Mark McKay + * @author Mark McKay + */ +abstract public class RenderableElement extends TransformableElement +{ + + AffineTransform cachedXform = null; + Shape cachedClip = null; + + public static final int VECTOR_EFFECT_NONE = 0; + public static final int VECTOR_EFFECT_NON_SCALING_STROKE = 1; + int vectorEffect; + + /** Creates a new instance of BoundedElement */ + public RenderableElement() { + } + + public RenderableElement(String id, SVGElement parent) + { + super(id, parent); + } + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + if (getPres(sty.setName("vector-effect"))) + { + if ("non-scaling-stroke".equals(sty.getStringValue())) + { + vectorEffect = VECTOR_EFFECT_NON_SCALING_STROKE; + } + else + { + vectorEffect = VECTOR_EFFECT_NONE; + } + } + else + { + vectorEffect = VECTOR_EFFECT_NONE; + } + } + + abstract public void render(Graphics2D g) throws SVGException; + + abstract void pick(Point2D point, Vector retVec) throws SVGException; + + abstract public Rectangle2D getBoundingBox() throws SVGException; +/* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + super.loaderStartElement(helper, attrs, parent); + } +*/ + /** + * Pushes transform stack, transforms to local coordinates and sets up + * clipping mask. + */ + protected void beginLayer(Graphics2D g) throws SVGException + { + if (xform != null) + { + cachedXform = g.getTransform(); + g.transform(xform); + } + + StyleAttribute styleAttrib = new StyleAttribute(); + + //Get clipping path +// StyleAttribute styleAttrib = getStyle("clip-path", false); + Shape clipPath = null; + int clipPathUnits = ClipPath.CP_USER_SPACE_ON_USE; + if (getStyle(styleAttrib.setName("clip-path"))) + { + URI uri = styleAttrib.getURIValue(getXMLBase()); + if (uri != null) + { + ClipPath ele = (ClipPath)diagram.getUniverse().getElement(uri); + clipPath = ele.getClipPathShape(); + clipPathUnits = ele.getClipPathUnits(); + } + } + + //Return if we're out of clipping range + if (clipPath != null) + { + if (clipPathUnits == ClipPath.CP_OBJECT_BOUNDING_BOX && (this instanceof ShapeElement)) + { + Rectangle2D rect = ((ShapeElement)this).getBoundingBox(); + AffineTransform at = new AffineTransform(); + at.scale(rect.getWidth(), rect.getHeight()); + clipPath = at.createTransformedShape(clipPath); + } + + cachedClip = g.getClip(); + Area newClip = new Area(cachedClip); + newClip.intersect(new Area(clipPath)); + g.setClip(newClip); + } + } + + /** + * Restores transform and clipping values to the way they were before + * this layer was drawn. + */ + protected void finishLayer(Graphics2D g) + { + if (cachedClip != null) + { + g.setClip(cachedClip); + } + + if (cachedXform != null) + { + g.setTransform(cachedXform); + } + } + +} diff --git a/src/main/java/com/kitfox/svg/SVGCache.java b/src/main/java/com/kitfox/svg/SVGCache.java new file mode 100644 index 0000000..13fa1ec --- /dev/null +++ b/src/main/java/com/kitfox/svg/SVGCache.java @@ -0,0 +1,28 @@ +/* + * SVGUniverseSingleton.java + * + * Created on April 2, 2005, 1:54 AM + */ + +package com.kitfox.svg; + +/** + * A convienience singleton for allowing all classes to access a common SVG universe. + * + * @author kitfox + */ +public class SVGCache +{ + private static final SVGUniverse svgUniverse = new SVGUniverse(); + + /** Creates a new instance of SVGUniverseSingleton */ + private SVGCache() + { + } + + public static SVGUniverse getSVGUniverse() + { + return svgUniverse; + } + +} diff --git a/src/main/java/com/kitfox/svg/SVGDiagram.java b/src/main/java/com/kitfox/svg/SVGDiagram.java new file mode 100644 index 0000000..aaab400 --- /dev/null +++ b/src/main/java/com/kitfox/svg/SVGDiagram.java @@ -0,0 +1,217 @@ +/* + * SVGDiagram.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 February 18, 2004, 5:04 PM + */ + +package com.kitfox.svg; + +import java.util.*; +import java.net.*; +import java.awt.*; +import java.awt.geom.*; +import java.io.Serializable; + +/** + * 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 */ + public SVGDiagram(URI xmlBase, SVGUniverse universe) + { + this.universe = universe; +// this.docRoot = docRoot; + this.xmlBase = xmlBase; + } + + /** + * Draws this diagram to the passed graphics context + */ + public void render(Graphics2D g) throws SVGException + { + root.render(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 Vector containing the + * path to the node is added to the return vector. That is, the result of + * SVGElement.getPath() is added for each entry. + * + * @return the passed in vector + */ + public Vector pick(Point2D point, Vector retVec) throws SVGException + { + if (retVec == null) + { + retVec = new Vector(); + } + + root.pick(point, 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. + */ + 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; + } + + 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. + */ + 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. + */ + public void setDeviceViewport(Rectangle deviceViewport) + { + this.deviceViewport.setBounds(deviceViewport); + if (root != null) + { + try + { + root.build(); + } catch (SVGException ex) + { + ex.printStackTrace(); + } + } + } +} diff --git a/src/main/java/com/kitfox/svg/SVGDisplayPanel.form b/src/main/java/com/kitfox/svg/SVGDisplayPanel.form new file mode 100644 index 0000000..9392b28 --- /dev/null +++ b/src/main/java/com/kitfox/svg/SVGDisplayPanel.form @@ -0,0 +1,16 @@ + + +
diff --git a/src/main/java/com/kitfox/svg/SVGDisplayPanel.java b/src/main/java/com/kitfox/svg/SVGDisplayPanel.java new file mode 100644 index 0000000..beedace --- /dev/null +++ b/src/main/java/com/kitfox/svg/SVGDisplayPanel.java @@ -0,0 +1,194 @@ +/* + * SVGDisplayPanel.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 February 20, 2004, 12:29 PM + */ + +package com.kitfox.svg; + +import javax.swing.*; +import java.awt.*; +import java.awt.geom.*; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class SVGDisplayPanel extends javax.swing.JPanel implements Scrollable +{ + public static final long serialVersionUID = 1; + + SVGDiagram diagram = null; + float scale = 1f; + Color bgColor = null; + + /** Creates new form SVGDisplayPanel */ + public SVGDisplayPanel() + { + initComponents(); + } + + public SVGDiagram getDiagram() + { + return diagram; + } + + public void setDiagram(SVGDiagram diagram) + { + this.diagram = diagram; + diagram.setDeviceViewport(getBounds()); + + setDimension(); + } + + public void setScale(float scale) + { + this.scale = scale; + setDimension(); + } + + public void setBgColor(Color col) + { + bgColor = col; + } + + private void setDimension() + { + if (diagram == null) + { + setPreferredSize(new Dimension(1, 1)); + revalidate(); + return; + } + + final Rectangle2D.Float rect = new Rectangle2D.Float(); + diagram.getViewRect(rect); + + int w = (int)(rect.width * scale); + int h = (int)(rect.height * scale); + + setPreferredSize(new Dimension(w, h)); + revalidate(); + } + + /** + * Update this image to reflect the passed time + */ + public void updateTime(double curTime) throws SVGException + { + if (diagram == null) return; + + diagram.updateTime(curTime); + } + + public void paintComponent(Graphics gg) + { + Graphics2D g = (Graphics2D)gg; + + if (bgColor != null) + { + Dimension dim = getSize(); + g.setColor(bgColor); + g.fillRect(0, 0, dim.width, dim.height); + } + + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + if (diagram != null) + { + try + { + diagram.render(g); + } + catch (SVGException 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. + */ + //SVGException
without detail message.
+ */
+ public SVGElementException(SVGElement element)
+ {
+ this(element, null, null);
+ }
+
+
+ /**
+ * Constructs an instance of SVGException
with the specified detail message.
+ * @param msg the detail message.
+ */
+ public SVGElementException(SVGElement element, String msg)
+ {
+ this(element, msg, null);
+ }
+
+ public SVGElementException(SVGElement element, String msg, Throwable cause)
+ {
+ super(msg, cause);
+ this.element = element;
+ }
+
+ public SVGElementException(SVGElement element, Throwable cause)
+ {
+ this(element, null, cause);
+ }
+
+ public SVGElement getElement()
+ {
+ return element;
+ }
+}
diff --git a/src/main/java/com/kitfox/svg/SVGException.java b/src/main/java/com/kitfox/svg/SVGException.java
new file mode 100644
index 0000000..c985ea1
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/SVGException.java
@@ -0,0 +1,47 @@
+/*
+ * SVGException.java
+ *
+ * Created on May 12, 2005, 11:32 PM
+ *
+ * To change this template, choose Tools | Options and locate the template under
+ * the Source Creation and Management node. Right-click the template and choose
+ * Open. You can then make changes to the template in the Source Editor.
+ */
+
+package com.kitfox.svg;
+
+/**
+ *
+ * @author kitfox
+ */
+public class SVGException extends java.lang.Exception
+{
+ public static final long serialVersionUID = 0;
+
+ /**
+ * Creates a new instance of SVGException
without detail message.
+ */
+ public SVGException()
+ {
+ }
+
+
+ /**
+ * Constructs an instance of SVGException
with the specified detail message.
+ * @param msg the detail message.
+ */
+ public SVGException(String msg)
+ {
+ super(msg);
+ }
+
+ public SVGException(String msg, Throwable cause)
+ {
+ super(msg, cause);
+ }
+
+ public SVGException(Throwable cause)
+ {
+ super(cause);
+ }
+}
diff --git a/src/main/java/com/kitfox/svg/SVGLoader.java b/src/main/java/com/kitfox/svg/SVGLoader.java
new file mode 100644
index 0000000..7f81c18
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/SVGLoader.java
@@ -0,0 +1,267 @@
+/*
+ * SVGLoader.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 February 18, 2004, 5:09 PM
+ */
+
+package com.kitfox.svg;
+
+
+import java.util.*;
+import java.io.*;
+import java.net.*;
+import org.xml.sax.*;
+import org.xml.sax.helpers.DefaultHandler;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+
+import com.kitfox.svg.animation.*;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class SVGLoader extends DefaultHandler
+{
+ final HashMap nodeClasses = new HashMap();
+ //final HashMap attribClasses = new HashMap();
+ final LinkedList buildStack = new LinkedList();
+
+ final HashSet ignoreClasses = new HashSet();
+
+ final SVGLoaderHelper helper;
+
+ /**
+ * The diagram that represents the base of this SVG document we're loading.
+ * Will be augmented to include node indexing info and other useful stuff.
+ */
+ final SVGDiagram diagram;
+
+// SVGElement loadRoot;
+
+ //Used to keep track of document elements that are not part of the SVG namespace
+ int skipNonSVGTagDepth = 0;
+ int indent = 0;
+
+ final boolean verbose;
+
+ /** Creates a new instance of SVGLoader */
+ public SVGLoader(URI xmlBase, SVGUniverse universe)
+ {
+ this(xmlBase, universe, false);
+ }
+
+ public SVGLoader(URI xmlBase, SVGUniverse universe, boolean verbose)
+ {
+ this.verbose = verbose;
+
+ diagram = new SVGDiagram(xmlBase, universe);
+
+ //Compile a list of important builder classes
+ 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);
+ nodeClasses.put("desc", Desc.class);
+ nodeClasses.put("ellipse", Ellipse.class);
+ nodeClasses.put("filter", Filter.class);
+ nodeClasses.put("font", Font.class);
+ nodeClasses.put("font-face", FontFace.class);
+ nodeClasses.put("g", Group.class);
+ nodeClasses.put("glyph", Glyph.class);
+ nodeClasses.put("image", ImageSVG.class);
+ nodeClasses.put("line", Line.class);
+ nodeClasses.put("lineargradient", LinearGradient.class);
+ nodeClasses.put("metadata", Metadata.class);
+ nodeClasses.put("missing-glyph", MissingGlyph.class);
+ nodeClasses.put("path", Path.class);
+ nodeClasses.put("pattern", PatternSVG.class);
+ nodeClasses.put("polygon", Polygon.class);
+ 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);
+ nodeClasses.put("svg", SVGRoot.class);
+ nodeClasses.put("symbol", Symbol.class);
+ nodeClasses.put("text", Text.class);
+ nodeClasses.put("title", Title.class);
+ nodeClasses.put("tspan", Tspan.class);
+ nodeClasses.put("use", Use.class);
+
+ ignoreClasses.add("midpointstop");
+
+ //attribClasses.put("clip-path", StyleUrl.class);
+ //attribClasses.put("color", StyleColor.class);
+
+ helper = new SVGLoaderHelper(xmlBase, universe, diagram);
+ }
+
+ private String printIndent(int indent, String indentStrn)
+ {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < indent; i++)
+ {
+ sb.append(indentStrn);
+ }
+ return sb.toString();
+ }
+
+ public void startDocument() throws SAXException
+ {
+// System.err.println("Start doc");
+
+// buildStack.clear();
+ }
+
+ public void endDocument() throws SAXException
+ {
+// System.err.println("End doc");
+ }
+
+ public void startElement(String namespaceURI, String sName, String qName, Attributes attrs) throws SAXException
+ {
+ if (verbose)
+ {
+ System.err.println(printIndent(indent, " ") + "Starting parse of tag " + sName+ ": " + namespaceURI);
+ }
+ indent++;
+
+ if (skipNonSVGTagDepth != 0 || (!namespaceURI.equals("") && !namespaceURI.equals(SVGElement.SVG_NS)))
+ {
+ skipNonSVGTagDepth++;
+ return;
+ }
+
+ sName = sName.toLowerCase();
+
+//javax.swing.JOptionPane.showMessageDialog(null, sName);
+
+ Object obj = nodeClasses.get(sName);
+ if (obj == null)
+ {
+ if (!ignoreClasses.contains(sName))
+ {
+ System.err.println("SVGLoader: Could not identify tag '" + sName + "'");
+ }
+ return;
+ }
+
+//Debug info tag depth
+//for (int i = 0; i < buildStack.size(); i++) System.err.print(" ");
+//System.err.println("+" + sName);
+
+ try {
+ Class cls = (Class)obj;
+ SVGElement svgEle = (SVGElement)cls.newInstance();
+
+ SVGElement parent = null;
+ if (buildStack.size() != 0) parent = (SVGElement)buildStack.getLast();
+ svgEle.loaderStartElement(helper, attrs, parent);
+
+ buildStack.addLast(svgEle);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ throw new SAXException(e);
+ }
+
+ }
+
+ public void endElement(String namespaceURI, String sName, String qName)
+ throws SAXException
+ {
+ indent--;
+ if (verbose)
+ {
+ System.err.println(printIndent(indent, " ") + "Ending parse of tag " + sName+ ": " + namespaceURI);
+ }
+
+ if (skipNonSVGTagDepth != 0)
+ {
+ skipNonSVGTagDepth--;
+ return;
+ }
+
+ sName = sName.toLowerCase();
+
+ Object obj = nodeClasses.get(sName);
+ if (obj == null) return;
+
+//Debug info tag depth
+//for (int i = 0; i < buildStack.size(); i++) System.err.print(" ");
+//System.err.println("-" + sName);
+
+ try {
+ SVGElement svgEle = (SVGElement)buildStack.removeLast();
+
+ svgEle.loaderEndElement(helper);
+
+ SVGElement parent = null;
+ if (buildStack.size() != 0) parent = (SVGElement)buildStack.getLast();
+ //else loadRoot = (SVGElement)svgEle;
+
+ if (parent != null) parent.loaderAddChild(helper, svgEle);
+ else diagram.setRoot((SVGRoot)svgEle);
+
+ }
+ catch (Exception e)
+ {
+e.printStackTrace();
+ throw new SAXException(e);
+ }
+ }
+
+ public void characters(char buf[], int offset, int len)
+ throws SAXException
+ {
+ if (skipNonSVGTagDepth != 0)
+ {
+ return;
+ }
+
+ if (buildStack.size() != 0)
+ {
+ SVGElement parent = (SVGElement)buildStack.getLast();
+ String s = new String(buf, offset, len);
+ parent.loaderAddText(helper, s);
+ }
+ }
+
+ public void processingInstruction(String target, String data)
+ throws SAXException
+ {
+ //Check for external style sheet
+ }
+
+// public SVGElement getLoadRoot() { return loadRoot; }
+ public SVGDiagram getLoadedDiagram() { return diagram; }
+}
diff --git a/src/main/java/com/kitfox/svg/SVGLoaderHelper.java b/src/main/java/com/kitfox/svg/SVGLoaderHelper.java
new file mode 100644
index 0000000..35e45bb
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/SVGLoaderHelper.java
@@ -0,0 +1,79 @@
+/*
+ * SVGLoaderHelper.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 February 18, 2004, 5:37 PM
+ */
+
+package com.kitfox.svg;
+
+import java.net.*;
+import java.io.*;
+
+import com.kitfox.svg.animation.parser.*;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class SVGLoaderHelper
+{
+ /** This is the URL that this document is being loaded from */
+// public final URL docRoot;
+// public final URI docRoot;
+
+ /** This is the universe of all currently loaded SVG documents */
+ public final SVGUniverse universe;
+
+ /** This is the diagram which the load process is currently loading */
+ public final SVGDiagram diagram;
+
+ 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)
+ {
+ /*
+ URI docURI = null;
+ try
+ {
+ docURI = new URI(docRoot.toString());
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ */
+
+ this.xmlBase = xmlBase;
+// this.docRoot = docURI;
+ this.universe = universe;
+ this.diagram = diagram;
+ }
+
+}
diff --git a/src/main/java/com/kitfox/svg/SVGParseException.java b/src/main/java/com/kitfox/svg/SVGParseException.java
new file mode 100644
index 0000000..478ecd2
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/SVGParseException.java
@@ -0,0 +1,47 @@
+/*
+ * SVGException.java
+ *
+ * Created on May 12, 2005, 11:32 PM
+ *
+ * To change this template, choose Tools | Options and locate the template under
+ * the Source Creation and Management node. Right-click the template and choose
+ * Open. You can then make changes to the template in the Source Editor.
+ */
+
+package com.kitfox.svg;
+
+/**
+ *
+ * @author kitfox
+ */
+public class SVGParseException extends java.lang.Exception
+{
+ public static final long serialVersionUID = 0;
+
+ /**
+ * Creates a new instance of SVGException
without detail message.
+ */
+ public SVGParseException()
+ {
+ }
+
+
+ /**
+ * Constructs an instance of SVGException
with the specified detail message.
+ * @param msg the detail message.
+ */
+ public SVGParseException(String msg)
+ {
+ super(msg);
+ }
+
+ public SVGParseException(String msg, Throwable cause)
+ {
+ super(msg, cause);
+ }
+
+ public SVGParseException(Throwable cause)
+ {
+ super(cause);
+ }
+}
diff --git a/src/main/java/com/kitfox/svg/SVGRoot.java b/src/main/java/com/kitfox/svg/SVGRoot.java
new file mode 100644
index 0000000..7fea53a
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/SVGRoot.java
@@ -0,0 +1,390 @@
+/*
+ * SVGRoot.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 February 18, 2004, 5:33 PM
+ */
+
+package com.kitfox.svg;
+
+import com.kitfox.svg.xml.NumberWithUnits;
+import com.kitfox.svg.xml.StyleAttribute;
+import java.awt.geom.*;
+import java.awt.*;
+
+/**
+ * The root element of an SVG tree.
+ *
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class SVGRoot extends Group
+{
+ NumberWithUnits x;
+ NumberWithUnits y;
+ NumberWithUnits width;
+ NumberWithUnits height;
+
+
+// final Rectangle2D.Float viewBox = new Rectangle2D.Float();
+ Rectangle2D.Float viewBox = null;
+
+ public static final int PA_X_NONE = 0;
+ public static final int PA_X_MIN = 1;
+ public static final int PA_X_MID = 2;
+ public static final int PA_X_MAX = 3;
+
+ public static final int PA_Y_NONE = 0;
+ public static final int PA_Y_MIN = 1;
+ public static final int PA_Y_MID = 2;
+ public static final int PA_Y_MAX = 3;
+
+ public static final int PS_MEET = 0;
+ public static final int PS_SLICE = 1;
+
+ int parSpecifier = PS_MEET;
+ int parAlignX = PA_X_MID;
+ int parAlignY = PA_Y_MID;
+
+ final AffineTransform viewXform = new AffineTransform();
+ final Rectangle2D.Float clipRect = new Rectangle2D.Float();
+
+ /** Creates a new instance of SVGRoot */
+ public SVGRoot()
+ {
+ }
+/*
+ public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent)
+ {
+ //Load style string
+ super.loaderStartElement(helper, attrs, parent);
+
+ x = XMLParseUtil.parseNumberWithUnits(attrs.getValue("x"));
+ y = XMLParseUtil.parseNumberWithUnits(attrs.getValue("y"));
+ width = XMLParseUtil.parseNumberWithUnits(attrs.getValue("width"));
+ height = XMLParseUtil.parseNumberWithUnits(attrs.getValue("height"));
+
+ String viewBox = attrs.getValue("viewBox");
+ float[] coords = XMLParseUtil.parseFloatList(viewBox);
+
+ if (coords == null)
+ {
+ //this.viewBox.setRect(0, 0, width.getValue(), height.getValue());
+ this.viewBox = null;
+ }
+ else
+ {
+ this.viewBox = new Rectangle2D.Float(coords[0], coords[1], coords[2], coords[3]);
+ }
+
+ String par = attrs.getValue("preserveAspectRatio");
+ if (par != null)
+ {
+ String[] parList = XMLParseUtil.parseStringList(par);
+
+ if (parList[0].equals("none")) { parAlignX = PA_X_NONE; parAlignY = PA_Y_NONE; }
+ else if (parList[0].equals("xMinYMin")) { parAlignX = PA_X_MIN; parAlignY = PA_Y_MIN; }
+ else if (parList[0].equals("xMidYMin")) { parAlignX = PA_X_MID; parAlignY = PA_Y_MIN; }
+ else if (parList[0].equals("xMaxYMin")) { parAlignX = PA_X_MAX; parAlignY = PA_Y_MIN; }
+ else if (parList[0].equals("xMinYMid")) { parAlignX = PA_X_MIN; parAlignY = PA_Y_MID; }
+ else if (parList[0].equals("xMidYMid")) { parAlignX = PA_X_MID; parAlignY = PA_Y_MID; }
+ else if (parList[0].equals("xMaxYMid")) { parAlignX = PA_X_MAX; parAlignY = PA_Y_MID; }
+ else if (parList[0].equals("xMinYMax")) { parAlignX = PA_X_MIN; parAlignY = PA_Y_MAX; }
+ else if (parList[0].equals("xMidYMax")) { parAlignX = PA_X_MID; parAlignY = PA_Y_MAX; }
+ else if (parList[0].equals("xMaxYMax")) { parAlignX = PA_X_MAX; parAlignY = PA_Y_MAX; }
+
+ if (parList[1].equals("meet")) parSpecifier = PS_MEET;
+ else if (parList[1].equals("slice")) parSpecifier = PS_SLICE;
+ }
+
+ build();
+ }
+*/
+
+ public void build() throws SVGException
+ {
+ super.build();
+
+ StyleAttribute sty = new StyleAttribute();
+
+ if (getPres(sty.setName("x"))) x = sty.getNumberWithUnits();
+
+ if (getPres(sty.setName("y"))) y = sty.getNumberWithUnits();
+
+ if (getPres(sty.setName("width"))) width = sty.getNumberWithUnits();
+
+ if (getPres(sty.setName("height"))) height = sty.getNumberWithUnits();
+
+ if (getPres(sty.setName("viewBox")))
+ {
+ float[] coords = sty.getFloatList();
+ viewBox = new Rectangle2D.Float(coords[0], coords[1], coords[2], coords[3]);
+ }
+
+ if (getPres(sty.setName("preserveAspectRatio")))
+ {
+ String preserve = sty.getStringValue();
+
+ if (contains(preserve, "none")) { parAlignX = PA_X_NONE; parAlignY = PA_Y_NONE; }
+ else if (contains(preserve, "xMinYMin")) { parAlignX = PA_X_MIN; parAlignY = PA_Y_MIN; }
+ else if (contains(preserve, "xMidYMin")) { parAlignX = PA_X_MID; parAlignY = PA_Y_MIN; }
+ else if (contains(preserve, "xMaxYMin")) { parAlignX = PA_X_MAX; parAlignY = PA_Y_MIN; }
+ else if (contains(preserve, "xMinYMid")) { parAlignX = PA_X_MIN; parAlignY = PA_Y_MID; }
+ else if (contains(preserve, "xMidYMid")) { parAlignX = PA_X_MID; parAlignY = PA_Y_MID; }
+ else if (contains(preserve, "xMaxYMid")) { parAlignX = PA_X_MAX; parAlignY = PA_Y_MID; }
+ else if (contains(preserve, "xMinYMax")) { parAlignX = PA_X_MIN; parAlignY = PA_Y_MAX; }
+ else if (contains(preserve, "xMidYMax")) { parAlignX = PA_X_MID; parAlignY = PA_Y_MAX; }
+ else if (contains(preserve, "xMaxYMax")) { parAlignX = PA_X_MAX; parAlignY = PA_Y_MAX; }
+
+ if (contains(preserve, "meet")) parSpecifier = PS_MEET;
+ else if (contains(preserve, "slice")) parSpecifier = PS_SLICE;
+
+ /*
+ String[] parList = sty.getStringList();
+
+ if (parList[0].equals("none")) { parAlignX = PA_X_NONE; parAlignY = PA_Y_NONE; }
+ else if (parList[0].equals("xMinYMin")) { parAlignX = PA_X_MIN; parAlignY = PA_Y_MIN; }
+ else if (parList[0].equals("xMidYMin")) { parAlignX = PA_X_MID; parAlignY = PA_Y_MIN; }
+ else if (parList[0].equals("xMaxYMin")) { parAlignX = PA_X_MAX; parAlignY = PA_Y_MIN; }
+ else if (parList[0].equals("xMinYMid")) { parAlignX = PA_X_MIN; parAlignY = PA_Y_MID; }
+ else if (parList[0].equals("xMidYMid")) { parAlignX = PA_X_MID; parAlignY = PA_Y_MID; }
+ else if (parList[0].equals("xMaxYMid")) { parAlignX = PA_X_MAX; parAlignY = PA_Y_MID; }
+ else if (parList[0].equals("xMinYMax")) { parAlignX = PA_X_MIN; parAlignY = PA_Y_MAX; }
+ else if (parList[0].equals("xMidYMax")) { parAlignX = PA_X_MID; parAlignY = PA_Y_MAX; }
+ else if (parList[0].equals("xMaxYMax")) { parAlignX = PA_X_MAX; parAlignY = PA_Y_MAX; }
+
+ if (parList[1].equals("meet")) parSpecifier = PS_MEET;
+ else if (parList[1].equals("slice")) parSpecifier = PS_SLICE;
+ */
+ }
+
+ prepareViewport();
+ }
+
+ private boolean contains(String text, String find)
+ {
+ return (text.indexOf(find) != -1);
+ }
+
+ protected void prepareViewport()
+ {
+ Rectangle deviceViewport = diagram.getDeviceViewport();
+
+ Rectangle2D defaultBounds;
+ try
+ {
+ defaultBounds = getBoundingBox();
+ } catch (SVGException ex)
+ {
+ defaultBounds= new Rectangle2D.Float();
+ }
+
+ //Determine destination rectangle
+ float xx, yy, ww, hh;
+ if (width != null)
+ {
+ xx = (x == null) ? 0 : StyleAttribute.convertUnitsToPixels(x.getUnits(), x.getValue());
+ if (width.getUnits() == NumberWithUnits.UT_PERCENT)
+ {
+ ww = width.getValue() * deviceViewport.width;
+ }
+ else
+ {
+ ww = StyleAttribute.convertUnitsToPixels(width.getUnits(), width.getValue());
+ }
+// setAttribute("x", AnimationElement.AT_XML, "" + xx);
+// setAttribute("width", AnimationElement.AT_XML, "" + ww);
+ }
+ else if (viewBox != null)
+ {
+ xx = (float)viewBox.x;
+ ww = (float)viewBox.width;
+ width = new NumberWithUnits(ww, NumberWithUnits.UT_PX);
+ x = new NumberWithUnits(xx, NumberWithUnits.UT_PX);
+ }
+ else
+ {
+ //Estimate size from scene bounding box
+ xx = (float)defaultBounds.getX();
+ ww = (float)defaultBounds.getWidth();
+ width = new NumberWithUnits(ww, NumberWithUnits.UT_PX);
+ x = new NumberWithUnits(xx, NumberWithUnits.UT_PX);
+ }
+
+ if (height != null)
+ {
+ yy = (y == null) ? 0 : StyleAttribute.convertUnitsToPixels(y.getUnits(), y.getValue());
+ if (height.getUnits() == NumberWithUnits.UT_PERCENT)
+ {
+ hh = height.getValue() * deviceViewport.height;
+ }
+ else
+ {
+ hh = StyleAttribute.convertUnitsToPixels(height.getUnits(), height.getValue());
+ }
+ }
+ else if (viewBox != null)
+ {
+ yy = (float)viewBox.y;
+ hh = (float)viewBox.height;
+ height = new NumberWithUnits(hh, NumberWithUnits.UT_PX);
+ y = new NumberWithUnits(yy, NumberWithUnits.UT_PX);
+ }
+ else
+ {
+ //Estimate size from scene bounding box
+ yy = (float)defaultBounds.getY();
+ hh = (float)defaultBounds.getHeight();
+ height = new NumberWithUnits(hh, NumberWithUnits.UT_PX);
+ y = new NumberWithUnits(yy, NumberWithUnits.UT_PX);
+ }
+
+ clipRect.setRect(xx, yy, ww, hh);
+
+ if (viewBox == null)
+ {
+ viewXform.setToIdentity();
+ }
+ else
+ {
+ viewXform.setToTranslation(clipRect.x, clipRect.y);
+ viewXform.scale(clipRect.width, clipRect.height);
+ viewXform.scale(1 / viewBox.width, 1 / viewBox.height);
+ viewXform.translate(-viewBox.x, -viewBox.y);
+ }
+
+
+ //For now, treat all preserveAspectRatio as 'none'
+// viewXform.setToTranslation(viewBox.x, viewBox.y);
+// viewXform.scale(clipRect.width / viewBox.width, clipRect.height / viewBox.height);
+// viewXform.translate(-clipRect.x, -clipRect.y);
+ }
+
+ public void render(Graphics2D g) throws SVGException
+ {
+ prepareViewport();
+
+ AffineTransform cachedXform = g.getTransform();
+ g.transform(viewXform);
+
+ super.render(g);
+
+ g.setTransform(cachedXform);
+ }
+
+ public Shape getShape()
+ {
+ Shape shape = super.getShape();
+ return viewXform.createTransformedShape(shape);
+ }
+
+ public Rectangle2D getBoundingBox() throws SVGException
+ {
+ Rectangle2D bbox = super.getBoundingBox();
+ return viewXform.createTransformedShape(bbox).getBounds2D();
+ }
+
+ public float getDeviceWidth()
+ {
+ return clipRect.width;
+ }
+
+ public float getDeviceHeight()
+ {
+ return clipRect.height;
+ }
+
+ public Rectangle2D getDeviceRect(Rectangle2D rect)
+ {
+ rect.setRect(clipRect);
+ return rect;
+ }
+
+ /**
+ * 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) throws SVGException
+ {
+ boolean changeState = super.updateTime(curTime);
+
+ StyleAttribute sty = new StyleAttribute();
+ boolean shapeChange = false;
+
+ if (getPres(sty.setName("x")))
+ {
+ NumberWithUnits newVal = sty.getNumberWithUnits();
+ if (newVal != x)
+ {
+ x = newVal;
+ shapeChange = true;
+ }
+ }
+
+ if (getPres(sty.setName("y")))
+ {
+ NumberWithUnits newVal = sty.getNumberWithUnits();
+ if (newVal != y)
+ {
+ y = newVal;
+ shapeChange = true;
+ }
+ }
+
+ if (getPres(sty.setName("width")))
+ {
+ NumberWithUnits newVal = sty.getNumberWithUnits();
+ if (newVal != width)
+ {
+ width = newVal;
+ shapeChange = true;
+ }
+ }
+
+ if (getPres(sty.setName("height")))
+ {
+ NumberWithUnits newVal = sty.getNumberWithUnits();
+ if (newVal != height)
+ {
+ height = newVal;
+ shapeChange = true;
+ }
+ }
+
+ if (getPres(sty.setName("viewBox")))
+ {
+ float[] coords = sty.getFloatList();
+ Rectangle2D.Float newViewBox = new Rectangle2D.Float(coords[0], coords[1], coords[2], coords[3]);
+ if (!newViewBox.equals(viewBox))
+ {
+ viewBox = newViewBox;
+ shapeChange = true;
+ }
+ }
+
+ return changeState;
+ }
+
+}
diff --git a/src/main/java/com/kitfox/svg/SVGUniverse.java b/src/main/java/com/kitfox/svg/SVGUniverse.java
new file mode 100644
index 0000000..48d0f3a
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/SVGUniverse.java
@@ -0,0 +1,533 @@
+/*
+ * SVGUniverse.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 February 18, 2004, 11:43 PM
+ */
+
+package com.kitfox.svg;
+
+import com.kitfox.svg.app.beans.SVGIcon;
+import java.awt.Graphics2D;
+import java.net.*;
+import java.awt.image.*;
+import javax.imageio.*;
+import java.beans.*;
+import java.lang.ref.*;
+//import java.util.*;
+//import java.util.regex.*;
+
+import java.util.*;
+import java.io.*;
+import org.xml.sax.*;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.parsers.SAXParser;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+/**
+ * Many SVG files can be loaded at one time. These files will quite likely
+ * need to reference one another. The SVG universe provides a container for
+ * all these files and the means for them to relate to each other.
+ *
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class SVGUniverse implements Serializable
+{
+ public static final long serialVersionUID = 0;
+
+ transient private PropertyChangeSupport changes = new PropertyChangeSupport(this);
+
+ /**
+ * Maps document URIs to their loaded SVG diagrams. Note that URIs for
+ * documents loaded from URLs will reflect their URLs and URIs for documents
+ * initiated from streams will have the scheme svgSalamander.
+ */
+ final HashMap loadedDocs = new HashMap();
+
+ final HashMap loadedFonts = new HashMap();
+
+ final HashMap loadedImages = new HashMap();
+
+ public static final String INPUTSTREAM_SCHEME = "svgSalamander";
+
+ /**
+ * Current time in this universe. Used for resolving attributes that
+ * are influenced by track information. Time is in milliseconds. Time
+ * 0 coresponds to the time of 0 in each member diagram.
+ */
+ protected double curTime = 0.0;
+
+ private boolean verbose = false;
+
+ /** Creates a new instance of SVGUniverse */
+ public SVGUniverse()
+ {
+ }
+
+ public void addPropertyChangeListener(PropertyChangeListener l)
+ {
+ changes.addPropertyChangeListener(l);
+ }
+
+ public void removePropertyChangeListener(PropertyChangeListener l)
+ {
+ changes.removePropertyChangeListener(l);
+ }
+
+ /**
+ * Release all loaded SVG document from memory
+ */
+ public void clear()
+ {
+ loadedDocs.clear();
+ loadedFonts.clear();
+ loadedImages.clear();
+ }
+
+ /**
+ * Returns the current animation time in milliseconds.
+ */
+ public double getCurTime()
+ {
+ return curTime;
+ }
+
+ public void setCurTime(double curTime)
+ {
+ double oldTime = this.curTime;
+ this.curTime = curTime;
+ changes.firePropertyChange("curTime", new Double(oldTime), new Double(curTime));
+ }
+
+ /**
+ * Updates all time influenced style and presentation attributes in all SVG
+ * documents in this universe.
+ */
+ public void updateTime() throws SVGException
+ {
+ for (Iterator it = loadedDocs.values().iterator(); it.hasNext();)
+ {
+ SVGDiagram dia = (SVGDiagram)it.next();
+ dia.updateTime(curTime);
+ }
+ }
+
+ /**
+ * Called by the Font element to let the universe know that a font has been
+ * loaded and is available.
+ */
+ void registerFont(Font font)
+ {
+ loadedFonts.put(font.getFontFace().getFontFamily(), font);
+ }
+
+ public Font getDefaultFont()
+ {
+ for (Iterator it = loadedFonts.values().iterator(); it.hasNext();)
+ {
+ return (Font)it.next();
+ }
+ return null;
+ }
+
+ public Font getFont(String fontName)
+ {
+ return (Font)loadedFonts.get(fontName);
+ }
+
+ void registerImage(URL imageURL)
+ {
+ if (loadedImages.containsKey(imageURL)) return;
+
+ SoftReference ref;
+ try
+ {
+ String fileName = imageURL.getFile();
+ if (".svg".equals(fileName.substring(fileName.length() - 4).toLowerCase()))
+ {
+ SVGIcon icon = new SVGIcon();
+ icon.setSvgURI(imageURL.toURI());
+
+ BufferedImage img = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g = img.createGraphics();
+ icon.paintIcon(null, g, 0, 0);
+ g.dispose();
+ ref = new SoftReference(img);
+ }
+ else
+ {
+ BufferedImage img = ImageIO.read(imageURL);
+ ref = new SoftReference(img);
+ }
+ loadedImages.put(imageURL, ref);
+ }
+ catch (Exception e)
+ {
+ System.err.println("Could not load image: " + imageURL);
+ e.printStackTrace();
+ }
+ }
+
+ BufferedImage getImage(URL imageURL)
+ {
+ SoftReference ref = (SoftReference)loadedImages.get(imageURL);
+ if (ref == null) return null;
+
+ BufferedImage img = (BufferedImage)ref.get();
+ //If image was cleared from memory, reload it
+ if (img == null)
+ {
+ try
+ {
+ img = ImageIO.read(imageURL);
+ }
+ catch (Exception e)
+ { e.printStackTrace(); }
+ ref = new SoftReference(img);
+ loadedImages.put(imageURL, ref);
+ }
+
+ return img;
+ }
+
+ /**
+ * Returns the element of the document at the given URI. If the document
+ * is not already loaded, it will be.
+ */
+ public SVGElement getElement(URI path)
+ {
+ return getElement(path, true);
+ }
+
+ public SVGElement getElement(URL path)
+ {
+ try
+ {
+ URI uri = new URI(path.toString());
+ return getElement(uri, true);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * Looks up a href within our universe. If the href refers to a document that
+ * is not loaded, it will be loaded. The URL #target will then be checked
+ * against the SVG diagram's index and the coresponding element returned.
+ * If there is no coresponding index, null is returned.
+ */
+ public SVGElement getElement(URI path, boolean loadIfAbsent)
+ {
+ try
+ {
+ //Strip fragment from URI
+ URI xmlBase = new URI(path.getScheme(), path.getSchemeSpecificPart(), null);
+
+ SVGDiagram dia = (SVGDiagram)loadedDocs.get(xmlBase);
+ if (dia == null && loadIfAbsent)
+ {
+//System.err.println("SVGUnivserse: " + xmlBase.toString());
+//javax.swing.JOptionPane.showMessageDialog(null, xmlBase.toString());
+ URL url = xmlBase.toURL();
+
+ loadSVG(url, false);
+ dia = (SVGDiagram)loadedDocs.get(xmlBase);
+ if (dia == null) return null;
+ }
+
+ String fragment = path.getFragment();
+ return fragment == null ? dia.getRoot() : dia.getElement(fragment);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public SVGDiagram getDiagram(URI xmlBase)
+ {
+ return getDiagram(xmlBase, true);
+ }
+
+ /**
+ * Returns the diagram that has been loaded from this root. If diagram is
+ * not already loaded, returns null.
+ */
+ public SVGDiagram getDiagram(URI xmlBase, boolean loadIfAbsent)
+ {
+ if (xmlBase == null) return null;
+
+ SVGDiagram dia = (SVGDiagram)loadedDocs.get(xmlBase);
+ if (dia != null || !loadIfAbsent) return dia;
+
+ //Load missing diagram
+ try
+ {
+ URL url = xmlBase.toURL();
+
+ loadSVG(url, false);
+ dia = (SVGDiagram)loadedDocs.get(xmlBase);
+ return dia;
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ public URI loadSVG(URL docRoot)
+ {
+ return loadSVG(docRoot, false);
+ }
+
+ /**
+ * Loads an SVG file and all the files it references from the URL provided.
+ * If a referenced file already exists in the SVG universe, it is not
+ * reloaded.
+ * @param docRoot - URL to the location where this SVG file can be found.
+ * @param forceLoad - if true, ignore cached diagram and reload
+ * @return - The URI that refers to the loaded document
+ */
+ public URI loadSVG(URL docRoot, boolean forceLoad)
+ {
+ try
+ {
+ URI uri = new URI(docRoot.toString());
+ if (loadedDocs.containsKey(uri) && !forceLoad) return uri;
+
+ InputStream is = docRoot.openStream();
+ return loadSVG(uri, new InputStreamReader(is));
+ }
+ catch (Throwable t)
+ {
+ t.printStackTrace();
+ }
+
+ return null;
+ }
+
+
+ public URI loadSVG(InputStream is, String name)
+ {
+ return loadSVG(is, name, false);
+ }
+
+ public URI loadSVG(InputStream is, String name, boolean forceLoad)
+ {
+ return loadSVG(new InputStreamReader(is), name, forceLoad);
+ }
+
+ public URI loadSVG(Reader reader, String name)
+ {
+ return loadSVG(reader, name, false);
+ }
+
+ /**
+ * This routine allows you to create SVG documents from data streams that
+ * may not necessarily have a URL to load from. Since every SVG document
+ * must be identified by a unique URL, Salamander provides a method to
+ * fake this for streams by defining it's own protocol - svgSalamander -
+ * for SVG documents without a formal URL.
+ *
+ * @param reader - A stream containing a valid SVG document
+ * @param name - A unique name for this document. It will be used to + * construct a unique URI to refer to this document and perform resolution + * with relative URIs within this document.
+ *For example, a name of "/myScene" will produce the URI + * svgSalamander:/myScene. "/maps/canada/toronto" will produce + * svgSalamander:/maps/canada/toronto. If this second document then + * contained the href "../uk/london", it would resolve by default to + * svgSalamander:/maps/uk/london. That is, SVG Salamander defines the + * URI scheme svgSalamander for it's own internal use and uses it + * for uniquely identfying documents loaded by stream.
+ *If you need to link to documents outside of this scheme, you can + * either supply full hrefs (eg, href="url(http://www.kitfox.com/index.html)") + * or put the xml:base attribute in a tag to change the defaultbase + * URIs are resolved against
+ *If a name does not start with the character '/', it will be automatically + * prefixed to it.
+ * @param forceLoad - if true, ignore cached diagram and reload + * + * @return - The URI that refers to the loaded document + */ + public URI loadSVG(Reader reader, String name, boolean forceLoad) + { +//System.err.println(url.toString()); + //Synthesize URI for this stream + URI uri = getStreamBuiltURI(name); + if (uri == null) return null; + if (loadedDocs.containsKey(uri) && !forceLoad) return uri; + + return loadSVG(uri, reader); + } + + /** + * Synthesize a URI for an SVGDiagram constructed from a stream. + * @param name - Name given the document constructed from a stream. + */ + public URI getStreamBuiltURI(String name) + { + if (name == null || name.length() == 0) return null; + + if (name.charAt(0) != '/') name = '/' + name; + + try + { + //Dummy URL for SVG documents built from image streams + return new URI(INPUTSTREAM_SCHEME, name, null); + } + catch (Exception e) + { + e.printStackTrace(); + return null; + } + } + + + protected URI loadSVG(URI xmlBase, Reader is) + { + // Use an instance of ourselves as the SAX event handler + SVGLoader handler = new SVGLoader(xmlBase, this, verbose); + + //Place this docment in the universe before it is completely loaded + // so that the load process can refer to references within it's current + // document +//System.err.println("SVGUniverse: loading dia " + xmlBase); + loadedDocs.put(xmlBase, handler.getLoadedDiagram()); + + // Use the default (non-validating) parser + SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setValidating(false); + factory.setNamespaceAware(true); + + try + { + // Parse the input + XMLReader reader = XMLReaderFactory.createXMLReader(); + reader.setEntityResolver( + new EntityResolver() + { + public InputSource resolveEntity(String publicId, String systemId) + { + //Ignore all DTDs + return new InputSource(new ByteArrayInputStream(new byte[0])); + } + } + ); + reader.setContentHandler(handler); + reader.parse(new InputSource(new BufferedReader(is))); + +// SAXParser saxParser = factory.newSAXParser(); +// saxParser.parse(new InputSource(new BufferedReader(is)), handler); + return xmlBase; + } + catch (SAXParseException sex) + { + System.err.println("Error processing " + xmlBase); + System.err.println(sex.getMessage()); + + loadedDocs.remove(xmlBase); + return null; + } + catch (Throwable t) + { + t.printStackTrace(); + } + + return null; + } + + public static void main(String argv[]) + { + try + { + URL url = new URL("svgSalamander", "localhost", -1, "abc.svg", + new URLStreamHandler() + { + protected URLConnection openConnection(URL u) + { + return null; + } + } + ); +// URL url2 = new URL("svgSalamander", "localhost", -1, "abc.svg"); + + //Investigate URI resolution + URI uriA, uriB, uriC, uriD, uriE; + + uriA = new URI("svgSalamander", "/names/mySpecialName", null); +// uriA = new URI("http://www.kitfox.com/salamander"); +// uriA = new URI("svgSalamander://mySpecialName/grape"); + System.err.println(uriA.toString()); + System.err.println(uriA.getScheme()); + + uriB = uriA.resolve("#begin"); + System.err.println(uriB.toString()); + + uriC = uriA.resolve("tree#boing"); + System.err.println(uriC.toString()); + + uriC = uriA.resolve("../tree#boing"); + System.err.println(uriC.toString()); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + public boolean isVerbose() + { + return verbose; + } + + public void setVerbose(boolean verbose) + { + this.verbose = verbose; + } + + /** + * Uses serialization to duplicate this universe. + */ + public SVGUniverse duplicate() throws IOException, ClassNotFoundException + { + ByteArrayOutputStream bs = new ByteArrayOutputStream(); + ObjectOutputStream os = new ObjectOutputStream(bs); + os.writeObject(this); + os.close(); + + ByteArrayInputStream bin = new ByteArrayInputStream(bs.toByteArray()); + ObjectInputStream is = new ObjectInputStream(bin); + SVGUniverse universe = (SVGUniverse)is.readObject(); + is.close(); + + return universe; + } +} diff --git a/src/main/java/com/kitfox/svg/ShapeElement.java b/src/main/java/com/kitfox/svg/ShapeElement.java new file mode 100644 index 0000000..c4e583e --- /dev/null +++ b/src/main/java/com/kitfox/svg/ShapeElement.java @@ -0,0 +1,299 @@ +/* + * ShapeElement.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 26, 2004, 5:21 PM + */ + +package com.kitfox.svg; + + +import com.kitfox.svg.xml.StyleAttribute; +import java.net.*; +import java.awt.*; +import java.awt.geom.*; +import com.kitfox.svg.xml.*; +import java.util.Vector; + +/** + * Parent of shape objects + * + * @author Mark McKay + * @author Mark McKay + */ +abstract public class ShapeElement extends RenderableElement +{ + + /** + * This is necessary to get text elements to render the stroke the correct + * width. It is an alternative to producing new font glyph sets at different + * sizes. + */ + protected float strokeWidthScalar = 1f; + + /** Creates a new instance of ShapeElement */ + public ShapeElement() { + } + + abstract public void render(java.awt.Graphics2D g) throws SVGException; + + /* + protected void setStrokeWidthScalar(float strokeWidthScalar) + { + this.strokeWidthScalar = strokeWidthScalar; + } + */ + + void pick(Point2D point, Vector retVec) throws SVGException + { + /* + Point2D xPoint = new Point2D.Double(); + try + { + xform.inverseTransform(point, xPoint); + } + catch (NoninvertibleTransformException ex) + { + throw new SVGException(ex); + } + */ + + StyleAttribute styleAttrib = new StyleAttribute(); + if (getStyle(styleAttrib.setName("fill")) && getShape().contains(point)) + { + retVec.add(getPath(null)); + } + } + + protected void renderShape(Graphics2D g, Shape shape) throws SVGException + { +//g.setColor(Color.green); + + StyleAttribute styleAttrib = new StyleAttribute(); + + //Don't process if not visible + if (getStyle(styleAttrib.setName("visibility"))) + { + if (!styleAttrib.getStringValue().equals("visible")) return; + } + + if (getStyle(styleAttrib.setName("display"))) + { + if (styleAttrib.getStringValue().equals("none")) return; + } + + //None, solid color, gradient, pattern + Paint fillPaint = Color.black; //Default to black. Must be explicitly set to none for no fill. + if (getStyle(styleAttrib.setName("fill"))) + { + if (styleAttrib.getStringValue().equals("none")) fillPaint = null; + else + { + fillPaint = styleAttrib.getColorValue(); + if (fillPaint == null) + { + URI uri = styleAttrib.getURIValue(getXMLBase()); + if (uri != null) + { + Rectangle2D bounds = shape.getBounds2D(); + AffineTransform xform = g.getTransform(); + + SVGElement ele = diagram.getUniverse().getElement(uri); + fillPaint = ((FillElement)ele).getPaint(bounds, xform); + } + } + } + } + + //Default opacity + float opacity = 1f; + if (getStyle(styleAttrib.setName("opacity"))) + { + opacity = styleAttrib.getRatioValue(); + } + + float fillOpacity = opacity; + if (getStyle(styleAttrib.setName("fill-opacity"))) + { + fillOpacity *= styleAttrib.getRatioValue(); + } + + + Paint strokePaint = null; //Default is to stroke with none + if (getStyle(styleAttrib.setName("stroke"))) + { + if (styleAttrib.getStringValue().equals("none")) strokePaint = null; + else + { + strokePaint = styleAttrib.getColorValue(); + if (strokePaint == null) + { + URI uri = styleAttrib.getURIValue(getXMLBase()); + if (uri != null) + { + Rectangle2D bounds = shape.getBounds2D(); + AffineTransform xform = g.getTransform(); + + SVGElement ele = diagram.getUniverse().getElement(uri); + strokePaint = ((FillElement)ele).getPaint(bounds, xform); + } + } + } + } + + float[] strokeDashArray = null; + if (getStyle(styleAttrib.setName("stroke-dasharray"))) + { + strokeDashArray = styleAttrib.getFloatList(); + if (strokeDashArray.length == 0) strokeDashArray = null; + } + + float strokeDashOffset = 0f; + if (getStyle(styleAttrib.setName("stroke-dashoffset"))) + { + strokeDashOffset = styleAttrib.getFloatValueWithUnits(); + } + + int strokeLinecap = BasicStroke.CAP_BUTT; + if (getStyle(styleAttrib.setName("stroke-linecap"))) + { + String val = styleAttrib.getStringValue(); + if (val.equals("round")) strokeLinecap = BasicStroke.CAP_ROUND; + else if (val.equals("square")) strokeLinecap = BasicStroke.CAP_SQUARE; + } + + int strokeLinejoin = BasicStroke.JOIN_MITER; + if (getStyle(styleAttrib.setName("stroke-linejoin"))) + { + String val = styleAttrib.getStringValue(); + if (val.equals("round")) strokeLinecap = BasicStroke.JOIN_ROUND; + else if (val.equals("bevel")) strokeLinecap = BasicStroke.JOIN_BEVEL; + } + + float strokeMiterLimit = 4f; + if (getStyle(styleAttrib.setName("stroke-miterlimit"))) + { + strokeMiterLimit = Math.max(styleAttrib.getFloatValueWithUnits(), 1); + } + + float strokeOpacity = opacity; + if (getStyle(styleAttrib.setName("stroke-opacity"))) + { + strokeOpacity *= styleAttrib.getRatioValue(); + } + + float strokeWidth = 1f; + if (getStyle(styleAttrib.setName("stroke-width"))) + { + strokeWidth = styleAttrib.getFloatValueWithUnits(); + } +// if (strokeWidthScalar != 1f) +// { + strokeWidth *= strokeWidthScalar; +// } + + + + //Draw the shape + if (fillPaint != null && fillOpacity != 0f) + { + if (fillOpacity <= 0) + { + //Do nothing + } + else if (fillOpacity < 1f) + { + Composite cachedComposite = g.getComposite(); + g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, fillOpacity)); + + g.setPaint(fillPaint); + g.fill(shape); + + g.setComposite(cachedComposite); + } + else + { + g.setPaint(fillPaint); + g.fill(shape); + } + } + + + if (strokePaint != null && strokeOpacity != 0f) + { + BasicStroke stroke; + if (strokeDashArray == null) + { + stroke = new BasicStroke(strokeWidth, strokeLinecap, strokeLinejoin, strokeMiterLimit); + } + else + { + stroke = new BasicStroke(strokeWidth, strokeLinecap, strokeLinejoin, strokeMiterLimit, strokeDashArray, strokeDashOffset); + } + + Shape strokeShape = stroke.createStrokedShape(shape); + + if (strokeOpacity <= 0) + { + //Do nothing + } + else if (strokeOpacity < 1f) + { + Composite cachedComposite = g.getComposite(); + g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, strokeOpacity)); + + g.setPaint(strokePaint); + g.fill(strokeShape); + + g.setComposite(cachedComposite); + } + else + { + g.setPaint(strokePaint); + g.fill(strokeShape); + } + + } + + } + + abstract public Shape getShape(); + + protected Rectangle2D includeStrokeInBounds(Rectangle2D rect) throws SVGException + { + StyleAttribute styleAttrib = new StyleAttribute(); + if (!getStyle(styleAttrib.setName("stroke"))) return rect; + + double strokeWidth = 1; + if (getStyle(styleAttrib.setName("stroke-width"))) strokeWidth = styleAttrib.getDoubleValue(); + + rect.setRect( + rect.getX() - strokeWidth / 2, + rect.getY() - strokeWidth / 2, + rect.getWidth() + strokeWidth, + rect.getHeight() + strokeWidth); + + return rect; + } + +} diff --git a/src/main/java/com/kitfox/svg/Stop.java b/src/main/java/com/kitfox/svg/Stop.java new file mode 100644 index 0000000..b3990c6 --- /dev/null +++ b/src/main/java/com/kitfox/svg/Stop.java @@ -0,0 +1,129 @@ +/* + * Stop.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 26, 2004, 1:56 AM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import java.awt.*; +import java.util.*; + +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class Stop extends SVGElement { + + float offset = 0f; + float opacity = 1f; + Color color = Color.black; + + /** Creates a new instance of Stop */ + public Stop() { + } +/* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + String offset = attrs.getValue("offset"); + this.offset = (float)XMLParseUtil.parseRatio(offset); + + buildStop(); + } + */ + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + if (getPres(sty.setName("offset"))) + { + offset = sty.getFloatValue(); + String units = sty.getUnits(); + if (units != null && units.equals("%")) offset /= 100f; + if (offset > 1) offset = 1; + if (offset < 0) offset = 0; + } + + if (getStyle(sty.setName("stop-color"))) color = sty.getColorValue(); + + if (getStyle(sty.setName("stop-opacity"))) opacity = sty.getRatioValue(); + } + + /** + * 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) throws SVGException + { +// if (trackManager.getNumTracks() == 0) return false; + + //Get current values for parameters + StyleAttribute sty = new StyleAttribute(); + boolean shapeChange = false; + + if (getPres(sty.setName("offset"))) + { + float newVal = sty.getFloatValue(); + if (newVal != offset) + { + offset = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("stop-color"))) + { + Color newVal = sty.getColorValue(); + if (newVal != color) + { + color = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("stop-opacity"))) + { + float newVal = sty.getFloatValue(); + if (newVal != opacity) + { + opacity = newVal; + shapeChange = true; + } + } + + return shapeChange; + } +} diff --git a/src/main/java/com/kitfox/svg/Style.java b/src/main/java/com/kitfox/svg/Style.java new file mode 100644 index 0000000..58bbf26 --- /dev/null +++ b/src/main/java/com/kitfox/svg/Style.java @@ -0,0 +1,83 @@ +/* + * Stop.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 19, 2004, 1:56 AM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + + +/** + * Holds title textual information within tree + * + * @author Mark McKay + * @author Mark McKay + */ +public class Style extends SVGElement { + + //Should be set to "text/css" + String type; + StringBuffer text = new StringBuffer(); + + /** Creates a new instance of Stop */ + public Style() { + } +/* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + this.type = attrs.getValue("type"); + } +*/ + /** + * Called during load process to add text scanned within a tag + */ + public void loaderAddText(SVGLoaderHelper helper, String text) + { + this.text.append(text); + } + + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + if (getPres(sty.setName("type"))) type = sty.getStringValue(); + } + + public boolean updateTime(double curTime) throws SVGException + { + //Style sheet doesn't change + return false; + } + +} diff --git a/src/main/java/com/kitfox/svg/Symbol.java b/src/main/java/com/kitfox/svg/Symbol.java new file mode 100644 index 0000000..1a9f0b3 --- /dev/null +++ b/src/main/java/com/kitfox/svg/Symbol.java @@ -0,0 +1,158 @@ +/* + * Stop.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 26, 2004, 1:56 AM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import java.awt.*; +import java.awt.geom.*; +import java.util.*; + +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class Symbol extends Group +{ + AffineTransform viewXform; + Rectangle2D viewBox; + + /** Creates a new instance of Stop */ + public Symbol() { + } +/* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + String viewBoxStrn = attrs.getValue("viewBox"); + if (viewBoxStrn != null) + { + float[] dim = XMLParseUtil.parseFloatList(viewBoxStrn); + viewBox = new Rectangle2D.Float(dim[0], dim[1], dim[2], dim[3]); + } + } +*/ + /* + public void loaderEndElement(SVGLoaderHelper helper) + { + if (viewBox == null) + { + viewBox = super.getBoundingBox(); + } + + //Transform pattern onto unit square + viewXform = new AffineTransform(); + viewXform.scale(1.0 / viewBox.getWidth(), 1.0 / viewBox.getHeight()); + viewXform.translate(-viewBox.getX(), -viewBox.getY()); + } +*/ + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + +// sty = getPres("unicode"); +// if (sty != null) unicode = sty.getStringValue(); + + + if (getPres(sty.setName("viewBox"))) + { + float[] dim = sty.getFloatList(); + viewBox = new Rectangle2D.Float(dim[0], dim[1], dim[2], dim[3]); + } + + if (viewBox == null) + { +// viewBox = super.getBoundingBox(); + viewBox = new Rectangle(0, 0, 1, 1); + } + + //Transform pattern onto unit square + viewXform = new AffineTransform(); + viewXform.scale(1.0 / viewBox.getWidth(), 1.0 / viewBox.getHeight()); + viewXform.translate(-viewBox.getX(), -viewBox.getY()); + } + + protected boolean outsideClip(Graphics2D g) throws SVGException + { + g.getClipBounds(clipBounds); + Rectangle2D rect = super.getBoundingBox(); + if (rect.intersects(clipBounds)) + { + return false; + } + + return true; + + } + + public void render(Graphics2D g) throws SVGException + { + AffineTransform oldXform = g.getTransform(); + g.transform(viewXform); + + super.render(g); + + g.setTransform(oldXform); + } + + public Shape getShape() + { + Shape shape = super.getShape(); + return viewXform.createTransformedShape(shape); + } + + public Rectangle2D getBoundingBox() throws SVGException + { + Rectangle2D rect = super.getBoundingBox(); + return viewXform.createTransformedShape(rect).getBounds2D(); + } + + /** + * 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) throws SVGException + { +// if (trackManager.getNumTracks() == 0) return false; + boolean changeState = super.updateTime(curTime); + + //View box properties do not change + + return changeState; + } + +} diff --git a/src/main/java/com/kitfox/svg/Text.java b/src/main/java/com/kitfox/svg/Text.java new file mode 100644 index 0000000..1256419 --- /dev/null +++ b/src/main/java/com/kitfox/svg/Text.java @@ -0,0 +1,539 @@ +/* + * Stop.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 26, 2004, 1:56 AM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import java.awt.*; +import java.awt.font.*; +import java.awt.geom.*; +import java.util.*; +import java.util.regex.*; + +//import org.apache.batik.ext.awt.geom.ExtendedGeneralPath; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class Text extends ShapeElement +{ + + float x = 0; + float y = 0; + AffineTransform transform = null; + + String fontFamily; + float fontSize; + + //List of strings and tspans containing the content of this node + LinkedList content = new LinkedList(); + + Shape textShape; + + public static final int TXAN_START = 0; + public static final int TXAN_MIDDLE = 1; + public static final int TXAN_END = 2; + int textAnchor = TXAN_START; + + public static final int TXST_NORMAL = 0; + public static final int TXST_ITALIC = 1; + public static final int TXST_OBLIQUE = 2; + int fontStyle; + + public static final int TXWE_NORMAL = 0; + public static final int TXWE_BOLD = 1; + public static final int TXWE_BOLDER = 2; + public static final int TXWE_LIGHTER = 3; + public static final int TXWE_100 = 4; + public static final int TXWE_200 = 5; + public static final int TXWE_300 = 6; + public static final int TXWE_400 = 7; + public static final int TXWE_500 = 8; + public static final int TXWE_600 = 9; + public static final int TXWE_700 = 10; + public static final int TXWE_800 = 11; + public static final int TXWE_900 = 12; + int fontWeight; + + /** Creates a new instance of Stop */ + public Text() + { + } + + public void appendText(String text) + { + content.addLast(text); + } + + public void appendTspan(Tspan tspan) throws SVGElementException + { + super.loaderAddChild(null, tspan); + content.addLast(tspan); +// tspan.setParent(this); + } + + /** + * Discard cached information + */ + public void rebuild() throws SVGException + { + build(); + } + + public java.util.List getContent() + { + return content; + } +/* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + String x = attrs.getValue("x"); + String y = attrs.getValue("y"); + //String transform = attrs.getValue("transform"); + + this.x = XMLParseUtil.parseFloat(x); + this.y = XMLParseUtil.parseFloat(y); + } + */ + /** + * Called after the start element but before the end element to indicate + * each child tag that has been processed + */ + public void loaderAddChild(SVGLoaderHelper helper, SVGElement child) throws SVGElementException + { + super.loaderAddChild(helper, child); + + content.addLast(child); + } + + /** + * Called during load process to add text scanned within a tag + */ + public void loaderAddText(SVGLoaderHelper helper, String text) + { + Matcher matchWs = Pattern.compile("\\s*").matcher(text); + if (!matchWs.matches()) content.addLast(text); + } + + public void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + if (getPres(sty.setName("x"))) x = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("y"))) y = sty.getFloatValueWithUnits(); + + if (getStyle(sty.setName("font-family"))) fontFamily = sty.getStringValue(); + else fontFamily = "Sans Serif"; + + if (getStyle(sty.setName("font-size"))) fontSize = sty.getFloatValueWithUnits(); + else fontSize = 12f; + + if (getStyle(sty.setName("font-style"))) + { + String s = sty.getStringValue(); + if ("normal".equals(s)) + { + fontStyle = TXST_NORMAL; + } + else if ("italic".equals(s)) + { + fontStyle = TXST_ITALIC; + } + else if ("oblique".equals(s)) + { + fontStyle = TXST_OBLIQUE; + } + } + else fontStyle = TXST_NORMAL; + + if (getStyle(sty.setName("font-weight"))) + { + String s = sty.getStringValue(); + if ("normal".equals(s)) + { + fontWeight = TXWE_NORMAL; + } + else if ("bold".equals(s)) + { + fontWeight = TXWE_BOLD; + } + } + else fontWeight = TXWE_BOLD; + + if (getStyle(sty.setName("text-anchor"))) + { + String s = sty.getStringValue(); + if (s.equals("middle")) textAnchor = TXAN_MIDDLE; + else if (s.equals("end")) textAnchor = TXAN_END; + else textAnchor = TXAN_START; + } + else textAnchor = TXAN_START; + + //text anchor + //text-decoration + //text-rendering + + buildFont(); + } + + protected void buildFont() throws SVGException + { + int style; + switch (fontStyle) + { + case TXST_ITALIC: + style = java.awt.Font.ITALIC; + break; + default: + style = java.awt.Font.PLAIN; + break; + } + + int weight; + switch (fontWeight) + { + case TXWE_BOLD: + case TXWE_BOLDER: + weight = java.awt.Font.BOLD; + break; + default: + weight = java.awt.Font.PLAIN; + break; + } + + //Get font + Font font = diagram.getUniverse().getFont(fontFamily); + if (font == null) + { +// System.err.println("Could not load font"); + + java.awt.Font sysFont = new java.awt.Font(fontFamily, style | weight, (int)fontSize); + buildSysFont(sysFont); + return; + } + +// font = new java.awt.Font(font.getFamily(), style | weight, font.getSize()); + +// Area textArea = new Area(); + GeneralPath textPath = new GeneralPath(); + textShape = textPath; + + float cursorX = x, cursorY = y; + + FontFace fontFace = font.getFontFace(); + //int unitsPerEm = fontFace.getUnitsPerEm(); + int ascent = fontFace.getAscent(); + float fontScale = fontSize / (float)ascent; + +// AffineTransform oldXform = g.getTransform(); + AffineTransform xform = new AffineTransform(); + + for (Iterator it = content.iterator(); it.hasNext();) + { + Object obj = it.next(); + + if (obj instanceof String) + { + String text = (String)obj; + + strokeWidthScalar = 1f / fontScale; + + for (int i = 0; i < text.length(); i++) + { + xform.setToIdentity(); + xform.setToTranslation(cursorX, cursorY); + xform.scale(fontScale, fontScale); +// g.transform(xform); + + String unicode = text.substring(i, i + 1); + MissingGlyph glyph = font.getGlyph(unicode); + + Shape path = glyph.getPath(); + if (path != null) + { + path = xform.createTransformedShape(path); + textPath.append(path, false); + } +// else glyph.render(g); + + cursorX += fontScale * glyph.getHorizAdvX(); + +// g.setTransform(oldXform); + } + + strokeWidthScalar = 1f; + } + else if (obj instanceof Tspan) + { + Tspan tspan = (Tspan)obj; + + xform.setToIdentity(); + xform.setToTranslation(cursorX, cursorY); + xform.scale(fontScale, fontScale); +// tspan.setCursorX(cursorX); +// tspan.setCursorY(cursorY); + + Shape tspanShape = tspan.getShape(); + tspanShape = xform.createTransformedShape(tspanShape); + textPath.append(tspanShape, false); +// tspan.render(g); +// cursorX = tspan.getCursorX(); +// cursorY = tspan.getCursorY(); + } + + } + + switch (textAnchor) + { + case TXAN_MIDDLE: + { + AffineTransform at = new AffineTransform(); + at.translate(-textPath.getBounds2D().getWidth() / 2, 0); + textPath.transform(at); + break; + } + case TXAN_END: + { + AffineTransform at = new AffineTransform(); + at.translate(-textPath.getBounds2D().getWidth(), 0); + textPath.transform(at); + break; + } + } + } + + private void buildSysFont(java.awt.Font font) throws SVGException + { + GeneralPath textPath = new GeneralPath(); + textShape = textPath; + + float cursorX = x, cursorY = y; + +// FontMetrics fm = g.getFontMetrics(font); + FontRenderContext frc = new FontRenderContext(null, true, true); + +// FontFace fontFace = font.getFontFace(); + //int unitsPerEm = fontFace.getUnitsPerEm(); +// int ascent = fm.getAscent(); +// float fontScale = fontSize / (float)ascent; + +// AffineTransform oldXform = g.getTransform(); + AffineTransform xform = new AffineTransform(); + + for (Iterator it = content.iterator(); it.hasNext();) + { + Object obj = it.next(); + + if (obj instanceof String) + { + String text = (String)obj; + + Shape textShape = font.createGlyphVector(frc, text).getOutline(cursorX, cursorY); + textPath.append(textShape, false); +// renderShape(g, textShape); +// g.drawString(text, cursorX, cursorY); + + Rectangle2D rect = font.getStringBounds(text, frc); + cursorX += (float)rect.getWidth(); + } + else if (obj instanceof Tspan) + { + /* + Tspan tspan = (Tspan)obj; + + xform.setToIdentity(); + xform.setToTranslation(cursorX, cursorY); + + Shape tspanShape = tspan.getShape(); + tspanShape = xform.createTransformedShape(tspanShape); + textArea.add(new Area(tspanShape)); + + cursorX += tspanShape.getBounds2D().getWidth(); + */ + + + Tspan tspan = (Tspan)obj; + tspan.setCursorX(cursorX); + tspan.setCursorY(cursorY); + tspan.addShape(textPath); + cursorX = tspan.getCursorX(); + cursorY = tspan.getCursorY(); + + } + } + + switch (textAnchor) + { + case TXAN_MIDDLE: + { + AffineTransform at = new AffineTransform(); + at.translate(-textPath.getBounds2D().getWidth() / 2, 0); + textPath.transform(at); + break; + } + case TXAN_END: + { + AffineTransform at = new AffineTransform(); + at.translate(-textPath.getBounds2D().getWidth(), 0); + textPath.transform(at); + break; + } + } + } + + + public void render(Graphics2D g) throws SVGException + { + beginLayer(g); + renderShape(g, textShape); + finishLayer(g); + } + + public Shape getShape() + { + return shapeToParent(textShape); + } + + public Rectangle2D getBoundingBox() throws SVGException + { + return boundsToParent(includeStrokeInBounds(textShape.getBounds2D())); + } + + /** + * 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) throws SVGException + { +// if (trackManager.getNumTracks() == 0) return false; + boolean changeState = super.updateTime(curTime); + + //Get current values for parameters + StyleAttribute sty = new StyleAttribute(); + boolean shapeChange = false; + + if (getPres(sty.setName("x"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != x) + { + x = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("y"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != y) + { + y = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("font-family"))) + { + String newVal = sty.getStringValue(); + if (!newVal.equals(fontFamily)) + { + fontFamily = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("font-size"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != fontSize) + { + fontSize = newVal; + shapeChange = true; + } + } + + + if (getStyle(sty.setName("font-style"))) + { + String s = sty.getStringValue(); + int newVal = fontStyle; + if ("normal".equals(s)) + { + newVal = TXST_NORMAL; + } + else if ("italic".equals(s)) + { + newVal = TXST_ITALIC; + } + else if ("oblique".equals(s)) + { + newVal = TXST_OBLIQUE; + } + if (newVal != fontStyle) + { + fontStyle = newVal; + shapeChange = true; + } + } + + if (getStyle(sty.setName("font-weight"))) + { + String s = sty.getStringValue(); + int newVal = fontWeight; + if ("normal".equals(s)) + { + newVal = TXWE_NORMAL; + } + else if ("bold".equals(s)) + { + newVal = TXWE_BOLD; + } + if (newVal != fontWeight) + { + fontWeight = newVal; + shapeChange = true; + } + } + + if (shapeChange) + { + buildFont(); + return true; + } + + return changeState; + } +} diff --git a/src/main/java/com/kitfox/svg/Title.java b/src/main/java/com/kitfox/svg/Title.java new file mode 100644 index 0000000..c056297 --- /dev/null +++ b/src/main/java/com/kitfox/svg/Title.java @@ -0,0 +1,65 @@ +/* + * Stop.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 19, 2004, 1:56 AM + */ + +package com.kitfox.svg; + +/** + * Holds title textual information within tree + * + * @author Mark McKay + * @author Mark McKay + */ +public class Title extends SVGElement { + + StringBuffer text = new StringBuffer(); + + /** Creates a new instance of Stop */ + public Title() { + } + + /** + * Called during load process to add text scanned within a tag + */ + public void loaderAddText(SVGLoaderHelper helper, String text) + { + this.text.append(text); + } + + public String getText() { return text.toString(); } + + /** + * 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) throws SVGException + { + //Title does not change + return false; + } +} diff --git a/src/main/java/com/kitfox/svg/TransformableElement.java b/src/main/java/com/kitfox/svg/TransformableElement.java new file mode 100644 index 0000000..cde6340 --- /dev/null +++ b/src/main/java/com/kitfox/svg/TransformableElement.java @@ -0,0 +1,117 @@ +/* + * BoundedElement.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 26, 2004, 9:00 AM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +import java.awt.geom.*; +import java.awt.*; + +/** + * Maintains bounding box for this element + * + * @author Mark McKay + * @author Mark McKay + */ +public class TransformableElement extends SVGElement +{ + + AffineTransform xform = null; +// AffineTransform invXform = null; + + /** Creates a new instance of BoundedElement */ + public TransformableElement() { + } + + public TransformableElement(String id, SVGElement parent) + { + super(id, parent); + } +/* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + String transform = attrs.getValue("transform"); + if (transform != null) + { + xform = parseTransform(transform); + } + } +*/ + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + if (getPres(sty.setName("transform"))) + { + xform = parseTransform(sty.getStringValue()); + } + } + + protected Shape shapeToParent(Shape shape) + { + if (xform == null) return shape; + return xform.createTransformedShape(shape); + } + + protected Rectangle2D boundsToParent(Rectangle2D rect) + { + if (xform == null) return rect; + return xform.createTransformedShape(rect).getBounds2D(); + } + + /** + * 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) throws SVGException + { + StyleAttribute sty = new StyleAttribute(); + + if (getPres(sty.setName("transform"))) + { + AffineTransform newXform = parseTransform(sty.getStringValue()); + if (!newXform.equals(xform)) + { + xform = newXform; + return true; + } + } + + return false; + } +} diff --git a/src/main/java/com/kitfox/svg/Tspan.java b/src/main/java/com/kitfox/svg/Tspan.java new file mode 100644 index 0000000..bffa6b8 --- /dev/null +++ b/src/main/java/com/kitfox/svg/Tspan.java @@ -0,0 +1,380 @@ +/* + * Stop.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 26, 2004, 1:56 AM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import java.awt.*; +import java.awt.font.*; +import java.awt.geom.*; +import java.util.*; + +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +//import org.apache.batik.ext.awt.geom.ExtendedGeneralPath; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class Tspan extends ShapeElement { + + float[] x = null; + float[] y = null; + float[] dx = null; + float[] dy = null; + float[] rotate = null; + + private String text = ""; + + float cursorX; + float cursorY; + +// Shape tspanShape; + + /** Creates a new instance of Stop */ + public Tspan() { + } + + public float getCursorX() { return cursorX; } + public float getCursorY() { return cursorY; } + + public void setCursorX(float cursorX) + { + this.cursorX = cursorX; + } + + public void setCursorY(float cursorY) + { + this.cursorY = cursorY; + } +/* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + String x = attrs.getValue("x"); + String y = attrs.getValue("y"); + String dx = attrs.getValue("dx"); + String dy = attrs.getValue("dy"); + String rotate = attrs.getValue("rotate"); + + if (x != null) this.x = XMLParseUtil.parseFloatList(x); + if (y != null) this.y = XMLParseUtil.parseFloatList(y); + if (dx != null) this.dx = XMLParseUtil.parseFloatList(dx); + if (dy != null) this.dy = XMLParseUtil.parseFloatList(dy); + if (rotate != null) + { + this.rotate = XMLParseUtil.parseFloatList(rotate); + for (int i = 0; i < this.rotate.length; i++) + this.rotate[i] = (float)Math.toRadians(this.rotate[i]); + } + } + */ + + /** + * Called during load process to add text scanned within a tag + */ + public void loaderAddText(SVGLoaderHelper helper, String text) + { + this.text += text; + } + + + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + if (getPres(sty.setName("x"))) x = sty.getFloatList(); + + if (getPres(sty.setName("y"))) y = sty.getFloatList(); + + if (getPres(sty.setName("dx"))) dx = sty.getFloatList(); + + if (getPres(sty.setName("dy"))) dy = sty.getFloatList(); + + if (getPres(sty.setName("rotate"))) + { + rotate = sty.getFloatList(); + for (int i = 0; i < this.rotate.length; i++) + { + rotate[i] = (float)Math.toRadians(this.rotate[i]); + } + + } + } + + public void addShape(GeneralPath addShape) throws SVGException + { + if (x != null) + { + cursorX = x[0]; + cursorY = y[0]; + } + else if (dx != null) + { + cursorX += dx[0]; + cursorY += dy[0]; + } + + StyleAttribute sty = new StyleAttribute(); + + String fontFamily = null; + if (getStyle(sty.setName("font-family"))) + { + fontFamily = sty.getStringValue(); + } + + + float fontSize = 12f; + if (getStyle(sty.setName("font-size"))) + { + fontSize = sty.getFloatValueWithUnits(); + } + + //Get font + Font font = diagram.getUniverse().getFont(fontFamily); + if (font == null) + { + addShapeSysFont(addShape, font, fontFamily, fontSize); + return; + } + + FontFace fontFace = font.getFontFace(); + int ascent = fontFace.getAscent(); + float fontScale = fontSize / (float)ascent; + + AffineTransform xform = new AffineTransform(); + + strokeWidthScalar = 1f / fontScale; + + int posPtr = 1; + + for (int i = 0; i < text.length(); i++) + { + xform.setToIdentity(); + xform.setToTranslation(cursorX, cursorY); + xform.scale(fontScale, fontScale); + if (rotate != null) xform.rotate(rotate[posPtr]); + + String unicode = text.substring(i, i + 1); + MissingGlyph glyph = font.getGlyph(unicode); + + Shape path = glyph.getPath(); + if (path != null) + { + path = xform.createTransformedShape(path); + addShape.append(path, false); + } + + if (x != null && posPtr < x.length) + { + cursorX = x[posPtr]; + cursorY = y[posPtr++]; + } + else if (dx != null && posPtr < dx.length) + { + cursorX += dx[posPtr]; + cursorY += dy[posPtr++]; + } + + cursorX += fontScale * glyph.getHorizAdvX(); + } + + strokeWidthScalar = 1f; + } + + private void addShapeSysFont(GeneralPath addShape, Font font, String fontFamily, float fontSize) + { + java.awt.Font sysFont = new java.awt.Font(fontFamily, java.awt.Font.PLAIN, (int)fontSize); + + FontRenderContext frc = new FontRenderContext(null, true, true); + GlyphVector textVector = sysFont.createGlyphVector(frc, text); + + AffineTransform xform = new AffineTransform(); + + int posPtr = 1; + for (int i = 0; i < text.length(); i++) + { + xform.setToIdentity(); + xform.setToTranslation(cursorX, cursorY); + if (rotate != null) xform.rotate(rotate[Math.min(i, rotate.length - 1)]); + + String unicode = text.substring(i, i + 1); + Shape glyphOutline = textVector.getGlyphOutline(i); + GlyphMetrics glyphMetrics = textVector.getGlyphMetrics(i); + + glyphOutline = xform.createTransformedShape(glyphOutline); + addShape.append(glyphOutline, false); + + if (x != null && posPtr < x.length) + { + cursorX = x[posPtr]; + cursorY = y[posPtr++]; + } + else if (dx != null && posPtr < dx.length) + { + cursorX += dx[posPtr]; + cursorY += dy[posPtr++]; + } + } + } + + public void render(Graphics2D g) throws SVGException + { + if (x != null) + { + cursorX = x[0]; + cursorY = y[0]; + } + else if (dx != null) + { + cursorX += dx[0]; + cursorY += dy[0]; + } + + StyleAttribute sty = new StyleAttribute(); + + String fontFamily = null; + if (getPres(sty.setName("font-family"))) + { + fontFamily = sty.getStringValue(); + } + + + float fontSize = 12f; + if (getPres(sty.setName("font-size"))) + { + fontSize = sty.getFloatValueWithUnits(); + } + + //Get font + Font font = diagram.getUniverse().getFont(fontFamily); + if (font == null) + { + System.err.println("Could not load font"); + java.awt.Font sysFont = new java.awt.Font(fontFamily, java.awt.Font.PLAIN, (int)fontSize); + renderSysFont(g, sysFont); + return; + } + + + FontFace fontFace = font.getFontFace(); + int ascent = fontFace.getAscent(); + float fontScale = fontSize / (float)ascent; + + AffineTransform oldXform = g.getTransform(); + AffineTransform xform = new AffineTransform(); + + strokeWidthScalar = 1f / fontScale; + + int posPtr = 1; + + for (int i = 0; i < text.length(); i++) + { + xform.setToTranslation(cursorX, cursorY); + xform.scale(fontScale, fontScale); + g.transform(xform); + + String unicode = text.substring(i, i + 1); + MissingGlyph glyph = font.getGlyph(unicode); + + Shape path = glyph.getPath(); + if (path != null) + { + renderShape(g, path); + } + else glyph.render(g); + + if (x != null && posPtr < x.length) + { + cursorX = x[posPtr]; + cursorY = y[posPtr++]; + } + else if (dx != null && posPtr < dx.length) + { + cursorX += dx[posPtr]; + cursorY += dy[posPtr++]; + } + + cursorX += fontScale * glyph.getHorizAdvX(); + + g.setTransform(oldXform); + } + + strokeWidthScalar = 1f; + } + + protected void renderSysFont(Graphics2D g, java.awt.Font font) throws SVGException + { + int posPtr = 1; + FontRenderContext frc = g.getFontRenderContext(); + + Shape textShape = font.createGlyphVector(frc, text).getOutline(cursorX, cursorY); + renderShape(g, textShape); + Rectangle2D rect = font.getStringBounds(text, frc); + cursorX += (float)rect.getWidth(); + } + + public Shape getShape() + { + return null; + //return shapeToParent(tspanShape); + } + + public Rectangle2D getBoundingBox() + { + return null; + //return boundsToParent(tspanShape.getBounds2D()); + } + + /** + * 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) throws SVGException + { + //Tspan does not change + return false; + } + + public String getText() + { + return text; + } + + public void setText(String text) + { + this.text = text; + } +} diff --git a/src/main/java/com/kitfox/svg/Use.java b/src/main/java/com/kitfox/svg/Use.java new file mode 100644 index 0000000..00d05ff --- /dev/null +++ b/src/main/java/com/kitfox/svg/Use.java @@ -0,0 +1,255 @@ +/* + * LinearGradient.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 26, 2004, 1:54 AM + */ + +package com.kitfox.svg; + +import com.kitfox.svg.xml.StyleAttribute; +import com.kitfox.svg.xml.*; +import org.xml.sax.*; + +import java.net.*; +import java.awt.*; +import java.awt.geom.*; + +/** + * @author Mark McKay + * @author Mark McKay + */ +public class Use extends ShapeElement { + + float x = 0f; + float y = 0f; + float width = 1f; + float height = 1f; + + SVGElement href = null; + + AffineTransform refXform; + + /** Creates a new instance of LinearGradient */ + public Use() { + } +/* + public void loaderStartElement(SVGLoaderHelper helper, Attributes attrs, SVGElement parent) + { + //Load style string + super.loaderStartElement(helper, attrs, parent); + + String x = attrs.getValue("x"); + String y = attrs.getValue("y"); + String width = attrs.getValue("width"); + String height = attrs.getValue("height"); + String href = attrs.getValue("xlink:href"); + + if (x != null) this.x = (float)XMLParseUtil.parseRatio(x); + if (y != null) this.y = (float)XMLParseUtil.parseRatio(y); + if (width != null) this.width = (float)XMLParseUtil.parseRatio(width); + if (height != null) this.height = (float)XMLParseUtil.parseRatio(height); + + + if (href != null) + { + try { + URI src = getXMLBase().resolve(href); + this.href = helper.universe.getElement(src); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + //Determine use offset/scale + refXform = new AffineTransform(); + refXform.translate(this.x, this.y); + refXform.scale(this.width, this.height); + } +*/ + protected void build() throws SVGException + { + super.build(); + + StyleAttribute sty = new StyleAttribute(); + + if (getPres(sty.setName("x"))) x = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("y"))) y = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("width"))) width = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("height"))) height = sty.getFloatValueWithUnits(); + + if (getPres(sty.setName("xlink:href"))) + { + URI src = sty.getURIValue(getXMLBase()); + href = diagram.getUniverse().getElement(src); + } + + //Determine use offset/scale + refXform = new AffineTransform(); + refXform.translate(this.x, this.y); + } + + public void render(Graphics2D g) throws SVGException + { + beginLayer(g); + + //AffineTransform oldXform = g.getTransform(); + AffineTransform oldXform = g.getTransform(); + g.transform(refXform); + + if (href == null || !(href instanceof RenderableElement)) return; + + RenderableElement rendEle = (RenderableElement)href; + rendEle.pushParentContext(this); + rendEle.render(g); + rendEle.popParentContext(); + + g.setTransform(oldXform); + + finishLayer(g); + } + + public Shape getShape() + { + if (href instanceof ShapeElement) + { + Shape shape = ((ShapeElement)href).getShape(); + shape = refXform.createTransformedShape(shape); + shape = shapeToParent(shape); + return shape; + } + + return null; + } + + public Rectangle2D getBoundingBox() throws SVGException + { + if (href instanceof ShapeElement) + { + ShapeElement shapeEle = (ShapeElement)href; + shapeEle.pushParentContext(this); + Rectangle2D bounds = shapeEle.getBoundingBox(); + shapeEle.popParentContext(); + + bounds = refXform.createTransformedShape(bounds).getBounds2D(); + bounds = boundsToParent(bounds); + + return bounds; + } + + return 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) throws SVGException + { +// if (trackManager.getNumTracks() == 0) return false; + boolean changeState = super.updateTime(curTime); + + //Get current values for parameters + StyleAttribute sty = new StyleAttribute(); + boolean shapeChange = false; + + if (getPres(sty.setName("x"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != x) + { + x = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("y"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != y) + { + y = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("width"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != width) + { + width = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("height"))) + { + float newVal = sty.getFloatValueWithUnits(); + if (newVal != height) + { + height = newVal; + shapeChange = true; + } + } + + if (getPres(sty.setName("xlink:href"))) + { + URI src = sty.getURIValue(getXMLBase()); + SVGElement newVal = diagram.getUniverse().getElement(src); + if (newVal != href) + { + href = newVal; + shapeChange = true; + } + } +/* + if (getPres(sty.setName("xlink:href"))) + { + URI src = sty.getURIValue(getXMLBase()); + href = diagram.getUniverse().getElement(src); + } + + //Determine use offset/scale + refXform = new AffineTransform(); + refXform.translate(this.x, this.y); + refXform.scale(this.width, this.height); +*/ + if (shapeChange) + { + //Determine use offset/scale + refXform.setToTranslation(this.x, this.y); + refXform.scale(this.width, this.height); + return true; + } + + return changeState; + } +} diff --git a/src/main/java/com/kitfox/svg/animation/AnimTimeParser.jjt b/src/main/java/com/kitfox/svg/animation/AnimTimeParser.jjt new file mode 100644 index 0000000..3a63b4c --- /dev/null +++ b/src/main/java/com/kitfox/svg/animation/AnimTimeParser.jjt @@ -0,0 +1,316 @@ +/** + */ + + +options { + MULTI=true; + STATIC=false; +} + +PARSER_BEGIN(AnimTimeParser) + +package com.kitfox.svg.animation.parser; + +import java.util.*; +import java.io.*; +import com.kitfox.svg.animation.*; + +public class AnimTimeParser +{ + /** + * Test the parser + */ + public static void main(String args[]) throws ParseException + { +// AnimTimeParser parser = new AnimTimeParser(System.in); + StringReader reader; + + reader = new StringReader("1:30 + 5ms"); + AnimTimeParser parser = new AnimTimeParser(reader); + TimeBase tc; + + tc = parser.Expr(); + System.err.println("AnimTimeParser eval to " + tc.evalTime()); + + reader = new StringReader("19"); + parser.ReInit(reader); + tc = parser.Expr(); + System.err.println("AnimTimeParser eval to " + tc.evalTime()); + } + +} + +PARSER_END(AnimTimeParser) + + + + +/** + * Tokens + */ + +SKIP : /* WHITE SPACE */ +{ + " " +| "\t" +| "\n" +| "\r" +| "\f" +} + +TOKEN : +{ + < #LETTER: [ "a"-"z", "A"-"Z" ] > +| + < #DIGIT: [ "0"-"9"] > +| + < INTEGER: (Translates a group of SVG files into images.
+ * + *Parameters:
+ *LinearGradientPaint
class provides a way to fill
+ * a {@link java.awt.Shape} with a linear color gradient pattern. The user may
+ * specify 2 or more gradient colors, and this paint will provide an
+ * interpolation between each color. The user also specifies start and end
+ * points which define where in user space the color gradient should begin
+ * and end.
+ * + * The user must provide an array of floats specifying how to distribute the + * colors along the gradient. These values should range from 0.0 to 1.0 and + * act like keyframes along the gradient (they mark where the gradient should + * be exactly a particular color). + *
+ * For example:
+ *
+ * Point2D start = new Point2D.Float(0, 0);
+ *
+ *
+ *
+ * Point2D end = new Point2D.Float(100,100);
+ * float[] dist = {0.0, 0.2, 1.0};
+ * Color[] colors = {Color.red, Color.white, Color.blue};
+ * LinearGradientPaint p = new LinearGradientPaint(start, end, dist, colors);
+ *
+ * This code will create a LinearGradientPaint which interpolates between + * red and white for the first 20% of the gradient and between white and blue + * for the remaining 80%. + * + *
In the event that the user does not set the first keyframe value equal
+ * to 0 and the last keyframe value equal to 1, keyframes will be created at
+ * these positions and the first and last colors will be replicated there.
+ * So, if a user specifies the following arrays to construct a gradient:
+ * {Color.blue, Color.red}, {.3, .7}
+ * this will be converted to a gradient with the following keyframes:
+ * {Color.blue, Color.blue, Color.red, Color.red}, {0, .3, .7, 1}
+ *
+ *
+ * The user may also select what action the LinearGradientPaint should take + * when filling color outside the start and end points. If no cycle method is + * specified, NO_CYCLE will be chosen by default, so the endpoint colors + * will be used to fill the remaining area. + * + *
The following image demonstrates the options NO_CYCLE and REFLECT. + * + *
+ * + * + *
The colorSpace parameter allows the user to specify in which colorspace + * the interpolation should be performed, default sRGB or linearized RGB. + * + * + * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans + * @author Vincent Hardy + * @version $Id: LinearGradientPaint.java,v 1.2 2004/09/27 09:27:27 kitfox Exp $ + * @see java.awt.Paint + * @see java.awt.Graphics2D#setPaint + * + */ + +public final class LinearGradientPaint extends MultipleGradientPaint { + + /** Gradient start and end points. */ + private Point2D start, end; + + /**
+ * Constructs an LinearGradientPaint
with the default
+ * NO_CYCLE repeating method and SRGB colorspace.
+ *
+ * @param startX the x coordinate of the gradient axis start point
+ * in user space
+ *
+ * @param startY the y coordinate of the gradient axis start point
+ * in user space
+ *
+ * @param endX the x coordinate of the gradient axis end point
+ * in user space
+ *
+ * @param endY the y coordinate of the gradient axis end point
+ * in user space
+ *
+ * @param fractions numbers ranging from 0.0 to 1.0 specifying the
+ * distribution of colors along the gradient
+ *
+ * @param colors array of colors corresponding to each fractional value
+ *
+ *
+ * @throws IllegalArgumentException if start and end points are the
+ * same points, or if fractions.length != colors.length, or if colors
+ * is less than 2 in size.
+ *
+ */
+ public LinearGradientPaint(float startX, float startY,
+ float endX, float endY,
+ float[] fractions, Color[] colors) {
+
+ this(new Point2D.Float(startX, startY),
+ new Point2D.Float(endX, endY),
+ fractions,
+ colors,
+ NO_CYCLE,
+ SRGB);
+ }
+
+ /**
+ * Constructs an LinearGradientPaint
with default SRGB
+ * colorspace.
+ *
+ * @param startX the x coordinate of the gradient axis start point
+ * in user space
+ *
+ * @param startY the y coordinate of the gradient axis start point
+ * in user space
+ *
+ * @param endX the x coordinate of the gradient axis end point
+ * in user space
+ *
+ * @param endY the y coordinate of the gradient axis end point
+ * in user space
+ *
+ * @param fractions numbers ranging from 0.0 to 1.0 specifying the
+ * distribution of colors along the gradient
+ *
+ * @param colors array of colors corresponding to each fractional value
+ *
+ * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
+ *
+ * @throws IllegalArgumentException if start and end points are the
+ * same points, or if fractions.length != colors.length, or if colors
+ * is less than 2 in size.
+ *
+ */
+ public LinearGradientPaint(float startX, float startY,
+ float endX, float endY,
+ float[] fractions, Color[] colors,
+ CycleMethodEnum cycleMethod) {
+ this(new Point2D.Float(startX, startY),
+ new Point2D.Float(endX, endY),
+ fractions,
+ colors,
+ cycleMethod,
+ SRGB);
+ }
+
+ /**
+ * Constructs a LinearGradientPaint
with the default
+ * NO_CYCLE repeating method and SRGB colorspace.
+ *
+ * @param start the gradient axis start Point
in user space
+ *
+ * @param end the gradient axis end Point
in user space
+ *
+ * @param fractions numbers ranging from 0.0 to 1.0 specifying the
+ * distribution of colors along the gradient
+ *
+ * @param colors array of colors corresponding to each fractional value
+ *
+ * @throws NullPointerException if one of the points is null
+ *
+ * @throws IllegalArgumentException if start and end points are the
+ * same points, or if fractions.length != colors.length, or if colors
+ * is less than 2 in size.
+ *
+ */
+ public LinearGradientPaint(Point2D start, Point2D end, float[] fractions,
+ Color[] colors) {
+
+ this(start, end, fractions, colors, NO_CYCLE, SRGB);
+ }
+
+ /**
+ * Constructs a LinearGradientPaint
.
+ *
+ * @param start the gradient axis start Point
in user space
+ *
+ * @param end the gradient axis end Point
in user space
+ *
+ * @param fractions numbers ranging from 0.0 to 1.0 specifying the
+ * distribution of colors along the gradient
+ *
+ * @param colors array of colors corresponding to each fractional value
+ *
+ * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
+ *
+ * @param colorSpace which colorspace to use for interpolation,
+ * either SRGB or LINEAR_RGB
+ *
+ * @throws NullPointerException if one of the points is null
+ *
+ * @throws IllegalArgumentException if start and end points are the
+ * same points, or if fractions.length != colors.length, or if colors
+ * is less than 2 in size.
+ *
+ */
+ public LinearGradientPaint(Point2D start, Point2D end, float[] fractions,
+ Color[] colors,
+ CycleMethodEnum cycleMethod,
+ ColorSpaceEnum colorSpace) {
+
+ this(start, end, fractions, colors, cycleMethod, colorSpace,
+ new AffineTransform());
+
+ }
+
+ /**
+ * Constructs a
+ * This class provides a way to fill a shape with a circular radial color
+ * gradient pattern. The user may specify 2 or more gradient colors, and this
+ * paint will provide an interpolation between each color.
+ *
+ *
+ * The user must provide an array of floats specifying how to distribute the
+ * colors along the gradient. These values should range from 0.0 to 1.0 and
+ * act like keyframes along the gradient (they mark where the gradient should
+ * be exactly a particular color).
+ *
+ *
+ * This paint will map the first color of the gradient to a focus point within
+ * the circle, and the last color to the perimeter of the circle, interpolating
+ * smoothly for any inbetween colors specified by the user. Any line drawn
+ * from the focus point to the circumference will span the all the gradient
+ * colors. By default the focus is set to be the center of the circle.
+ *
+ *
+ * Specifying a focus point outside of the circle's radius will result in the
+ * focus being set to the intersection point of the focus-center line and the
+ * perimenter of the circle.
+ *
+ *
+ * Specifying a cycle method allows the user to control the painting behavior
+ * outside of the bounds of the circle's radius. See LinearGradientPaint for
+ * more details.
+ *
+ *
+ * The following code demonstrates typical usage of RadialGradientPaint:
+ *
+ * In the event that the user does not set the first keyframe value equal
+ * to 0 and the last keyframe value equal to 1, keyframes will be created at
+ * these positions and the first and last colors will be replicated there.
+ * So, if a user specifies the following arrays to construct a gradient:
+ *
+ *
+ * This image demonstrates a radial gradient with NO_CYCLE and default focus.
+ *
+ *
+ *
+ *
+ * This image demonstrates a radial gradient with NO_CYCLE and non-centered
+ * focus.
+ *
+ *
+ *
+ *
+ * This image demonstrates a radial gradient with REFLECT and non-centered
+ * focus.
+ *
+ * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
+ * @author Vincent Hardy
+ * @version $Id: RadialGradientPaint.java,v 1.1 2004/09/06 19:35:39 kitfox Exp $
+ *
+ */
+
+public final class RadialGradientPaint extends MultipleGradientPaint {
+
+ /** Focus point which defines the 0% gradient stop x coordinate. */
+ private Point2D focus;
+
+ /** Center of the circle defining the 100% gradient stop x coordinate. */
+ private Point2D center;
+
+ /** Radius of the outermost circle defining the 100% gradient stop. */
+ private float radius;
+
+ /**
+ *
+ *
+ * Constructs a
+ *
+ * Constructs a
+ *
+ * Constructs a
+ *
+ * Constructs a
+ *
+ * Constructs a
+ *
+ * Constructs a
+ *
+ * Constructs a
+ * Creates and returns a PaintContext used to generate the color pattern,
+ * for use by the internal rendering engine.
+ *
+ * @param cm {@link ColorModel} that receives
+ * the
+ * This method
+ * simply performs
+ * This method simply performs the call
+ *
+ * This method simply performs
+ * The
+ * Implements the abstract write method of OutputStream.
+ *
+ * @param b the
+ * The
+ * Note that this method does not call the one-argument
+ *
+ * The
+ * Note that this method does not call the
+ * The
+ * The LinearGradientPaint
.
+ *
+ * @param start the gradient axis start Point
in user space
+ *
+ * @param end the gradient axis end Point
in user space
+ *
+ * @param fractions numbers ranging from 0.0 to 1.0 specifying the
+ * distribution of colors along the gradient
+ *
+ * @param colors array of colors corresponding to each fractional value
+ *
+ * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
+ *
+ * @param colorSpace which colorspace to use for interpolation,
+ * either SRGB or LINEAR_RGB
+ *
+ * @param gradientTransform transform to apply to the gradient
+ *
+ * @throws NullPointerException if one of the points is null,
+ * or gradientTransform is null
+ *
+ * @throws IllegalArgumentException if start and end points are the
+ * same points, or if fractions.length != colors.length, or if colors
+ * is less than 2 in size.
+ *
+ */
+ public LinearGradientPaint(Point2D start, Point2D end, float[] fractions,
+ Color[] colors,
+ CycleMethodEnum cycleMethod,
+ ColorSpaceEnum colorSpace,
+ AffineTransform gradientTransform) {
+ super(fractions, colors, cycleMethod, colorSpace, gradientTransform);
+
+ //
+ // Check input parameters
+ //
+ if (start == null || end == null) {
+ throw new NullPointerException("Start and end points must be" +
+ "non-null");
+ }
+
+ if (start.equals(end)) {
+ throw new IllegalArgumentException("Start point cannot equal" +
+ "endpoint");
+ }
+
+ //copy the points...
+ this.start = (Point2D)start.clone();
+
+ this.end = (Point2D)end.clone();
+
+ }
+
+ /**
+ * Creates and returns a PaintContext used to generate the color pattern,
+ * for use by the internal rendering engine.
+ *
+ * @param cm {@link ColorModel} that receives
+ * the Paint
data. This is used only as a hint.
+ *
+ * @param deviceBounds the device space bounding box of the
+ * graphics primitive being rendered
+ *
+ * @param userBounds the user space bounding box of the
+ * graphics primitive being rendered
+ *
+ * @param transform the {@link AffineTransform} from user
+ * space into device space
+ *
+ * @param hints the hints that the context object uses to choose
+ * between rendering alternatives
+ *
+ * @return the {@link PaintContext} that generates color patterns.
+ *
+ * @see PaintContext
+ */
+ public PaintContext createContext(ColorModel cm,
+ Rectangle deviceBounds,
+ Rectangle2D userBounds,
+ AffineTransform transform,
+ RenderingHints hints) {
+
+ // Can't modify the transform passed in...
+ transform = new AffineTransform(transform);
+ //incorporate the gradient transform
+ transform.concatenate(gradientTransform);
+
+ try {
+ return new LinearGradientPaintContext(cm,
+ deviceBounds,
+ userBounds,
+ transform,
+ hints,
+ start,
+ end,
+ fractions,
+ this.getColors(),
+ cycleMethod,
+ colorSpace);
+ }
+
+ catch(NoninvertibleTransformException e) {
+ e.printStackTrace();
+ throw new IllegalArgumentException("transform should be" +
+ "invertible");
+ }
+ }
+
+ /**
+ * Returns a copy of the start point of the gradient axis
+ * @return a {@link Point2D} object that is a copy of the point
+ * that anchors the first color of this
+ * LinearGradientPaint
.
+ */
+ public Point2D getStartPoint() {
+ return new Point2D.Double(start.getX(), start.getY());
+ }
+
+ /** Returns a copy of the end point of the gradient axis
+ * @return a {@link Point2D} object that is a copy of the point
+ * that anchors the last color of this
+ * LinearGradientPaint
.
+ */
+ public Point2D getEndPoint() {
+ return new Point2D.Double(end.getX(), end.getY());
+ }
+
+}
+
+
diff --git a/src/main/java/com/kitfox/svg/batik/LinearGradientPaintContext.java b/src/main/java/com/kitfox/svg/batik/LinearGradientPaintContext.java
new file mode 100644
index 0000000..c06d557
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/batik/LinearGradientPaintContext.java
@@ -0,0 +1,529 @@
+/*****************************************************************************
+ * Copyright (C) The Apache Software Foundation. All rights reserved. *
+ * ------------------------------------------------------------------------- *
+ * This software is published under the terms of the Apache Software License *
+ * version 1.1, a copy of which has been included with this distribution in *
+ * the LICENSE file. *
+ *****************************************************************************/
+
+package com.kitfox.svg.batik;
+
+import java.awt.Color;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.ColorModel;
+
+/**
+ * Provides the actual implementation for the LinearGradientPaint
+ * This is where the pixel processing is done.
+ *
+ * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
+ * @author Vincent Hardy
+ * @version $Id: LinearGradientPaintContext.java,v 1.2 2007/02/04 01:28:05 kitfox Exp $
+ * @see java.awt.PaintContext
+ * @see java.awt.Paint
+ * @see java.awt.GradientPaint
+ */
+final class LinearGradientPaintContext extends MultipleGradientPaintContext {
+
+ /**
+ * The following invariants are used to process the gradient value from
+ * a device space coordinate, (X, Y):
+ * g(X, Y) = dgdX*X + dgdY*Y + gc
+ */
+ private float dgdX, dgdY, gc, pixSz;
+
+ private static final int DEFAULT_IMPL = 1;
+ private static final int ANTI_ALIAS_IMPL = 3;
+
+ private int fillMethod;
+
+ /**
+ * Constructor for LinearGradientPaintContext.
+ *
+ * @param cm {@link ColorModel} that receives
+ * the Paint
data. This is used only as a hint.
+ *
+ * @param deviceBounds the device space bounding box of the
+ * graphics primitive being rendered
+ *
+ * @param userBounds the user space bounding box of the
+ * graphics primitive being rendered
+ *
+ * @param t the {@link AffineTransform} from user
+ * space into device space (gradientTransform should be
+ * concatenated with this)
+ *
+ * @param hints the hints that the context object uses to choose
+ * between rendering alternatives
+ *
+ * @param start gradient start point, in user space
+ *
+ * @param end gradient end point, in user space
+ *
+ * @param fractions the fractions specifying the gradient distribution
+ *
+ * @param colors the gradient colors
+ *
+ * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
+ *
+ * @param colorSpace which colorspace to use for interpolation,
+ * either SRGB or LINEAR_RGB
+ *
+ */
+ public LinearGradientPaintContext(ColorModel cm,
+ Rectangle deviceBounds,
+ Rectangle2D userBounds,
+ AffineTransform t,
+ RenderingHints hints,
+ Point2D dStart,
+ Point2D dEnd,
+ float[] fractions,
+ Color[] colors,
+ MultipleGradientPaint.CycleMethodEnum
+ cycleMethod,
+ MultipleGradientPaint.ColorSpaceEnum
+ colorSpace)
+ throws NoninvertibleTransformException
+ {
+ super(cm, deviceBounds, userBounds, t, hints, fractions,
+ colors, cycleMethod, colorSpace);
+
+ // Use single precision floating points
+ Point2D.Float start = new Point2D.Float((float)dStart.getX(),
+ (float)dStart.getY());
+ Point2D.Float end = new Point2D.Float((float)dEnd.getX(),
+ (float)dEnd.getY());
+
+ // A given point in the raster should take on the same color as its
+ // projection onto the gradient vector.
+ // Thus, we want the projection of the current position vector
+ // onto the gradient vector, then normalized with respect to the
+ // length of the gradient vector, giving a value which can be mapped into
+ // the range 0-1.
+ // projection = currentVector dot gradientVector / length(gradientVector)
+ // normalized = projection / length(gradientVector)
+
+ float dx = end.x - start.x; // change in x from start to end
+ float dy = end.y - start.y; // change in y from start to end
+ float dSq = dx*dx + dy*dy; // total distance squared
+
+ //avoid repeated calculations by doing these divides once.
+ float constX = dx/dSq;
+ float constY = dy/dSq;
+
+ //incremental change along gradient for +x
+ dgdX = a00*constX + a10*constY;
+ //incremental change along gradient for +y
+ dgdY = a01*constX + a11*constY;
+
+ float dgdXAbs = Math.abs(dgdX);
+ float dgdYAbs = Math.abs(dgdY);
+ if (dgdXAbs > dgdYAbs) pixSz = dgdXAbs;
+ else pixSz = dgdYAbs;
+
+ //constant, incorporates the translation components from the matrix
+ gc = (a02-start.x)*constX + (a12-start.y)*constY;
+
+ Object colorRend = hints == null ? RenderingHints.VALUE_COLOR_RENDER_SPEED : hints.get(RenderingHints.KEY_COLOR_RENDERING);
+ Object rend = hints == null ? RenderingHints.VALUE_RENDER_SPEED : hints.get(RenderingHints.KEY_RENDERING);
+
+ fillMethod = DEFAULT_IMPL;
+
+ if ((cycleMethod == MultipleGradientPaint.REPEAT) ||
+ hasDiscontinuity) {
+ if (rend == RenderingHints.VALUE_RENDER_QUALITY)
+ fillMethod = ANTI_ALIAS_IMPL;
+ // ColorRend overrides rend.
+ if (colorRend == RenderingHints.VALUE_COLOR_RENDER_SPEED)
+ fillMethod = DEFAULT_IMPL;
+ else if (colorRend == RenderingHints.VALUE_COLOR_RENDER_QUALITY)
+ fillMethod = ANTI_ALIAS_IMPL;
+ }
+ }
+
+ protected void fillHardNoCycle(int[] pixels, int off, int adjust,
+ int x, int y, int w, int h) {
+
+ //constant which can be pulled out of the inner loop
+ final float initConst = (dgdX*x) + gc;
+
+ for(int i=0; i
+ * Point2D center = new Point2D.Float(0, 0);
+ *
+ *
+ * float radius = 20;
+ * float[] dist = {0.0, 0.2, 1.0};
+ * Color[] colors = {Color.red, Color.white, Color.blue};
+ * RadialGradientPaint p = new RadialGradientPaint(center, radius,
+ * dist, colors);
+ *
+ * {Color.blue, Color.red}, {.3, .7}
+ * this will be converted to a gradient with the following keyframes:
+ * {Color.blue, Color.blue, Color.red, Color.red}, {0, .3, .7, 1}
+ *
+ *
+ * RadialGradientPaint
, using the center as the
+ * focus point.
+ *
+ * @param cx the x coordinate in user space of the center point of the
+ * circle defining the gradient. The last color of the gradient is mapped
+ * to the perimeter of this circle
+ *
+ * @param cy the y coordinate in user space of the center point of the
+ * circle defining the gradient. The last color of the gradient is mapped
+ * to the perimeter of this circle
+ *
+ * @param radius the radius of the circle defining the extents of the
+ * color gradient
+ *
+ * @param fractions numbers ranging from 0.0 to 1.0 specifying the
+ * distribution of colors along the gradient
+ *
+ * @param colors array of colors to use in the gradient. The first color
+ * is used at the focus point, the last color around the perimeter of the
+ * circle.
+ *
+ *
+ * @throws IllegalArgumentException
+ * if fractions.length != colors.length, or if colors is less
+ * than 2 in size, or if radius < 0
+ *
+ *
+ */
+ public RadialGradientPaint(float cx, float cy, float radius,
+ float[] fractions, Color[] colors) {
+ this(cx, cy,
+ radius,
+ cx, cy,
+ fractions,
+ colors);
+ }
+
+ /**
+ * RadialGradientPaint
, using the center as the
+ * focus point.
+ *
+ * @param center the center point, in user space, of the circle defining
+ * the gradient
+ *
+ * @param radius the radius of the circle defining the extents of the
+ * color gradient
+ *
+ * @param fractions numbers ranging from 0.0 to 1.0 specifying the
+ * distribution of colors along the gradient
+ *
+ * @param colors array of colors to use in the gradient. The first color
+ * is used at the focus point, the last color around the perimeter of the
+ * circle.
+ *
+ * @throws NullPointerException if center point is null
+ *
+ * @throws IllegalArgumentException
+ * if fractions.length != colors.length, or if colors is less
+ * than 2 in size, or if radius < 0
+ *
+ *
+ */
+ public RadialGradientPaint(Point2D center, float radius,
+ float[] fractions, Color[] colors) {
+ this(center,
+ radius,
+ center,
+ fractions,
+ colors);
+ }
+
+ /**
+ * RadialGradientPaint
.
+ *
+ * @param cx the x coordinate in user space of the center point of the
+ * circle defining the gradient. The last color of the gradient is mapped
+ * to the perimeter of this circle
+ *
+ * @param cy the y coordinate in user space of the center point of the
+ * circle defining the gradient. The last color of the gradient is mapped
+ * to the perimeter of this circle
+ *
+ * @param radius the radius of the circle defining the extents of the
+ * color gradient
+ *
+ * @param fx the x coordinate of the point in user space to which the
+ * first color is mapped
+ *
+ * @param fy the y coordinate of the point in user space to which the
+ * first color is mapped
+ *
+ * @param fractions numbers ranging from 0.0 to 1.0 specifying the
+ * distribution of colors along the gradient
+ *
+ * @param colors array of colors to use in the gradient. The first color
+ * is used at the focus point, the last color around the perimeter of the
+ * circle.
+ *
+ * @throws IllegalArgumentException
+ * if fractions.length != colors.length, or if colors is less
+ * than 2 in size, or if radius < 0
+ *
+ *
+ */
+ public RadialGradientPaint(float cx, float cy, float radius,
+ float fx, float fy,
+ float[] fractions, Color[] colors) {
+ this(new Point2D.Float(cx, cy),
+ radius,
+ new Point2D.Float(fx, fy),
+ fractions,
+ colors,
+ NO_CYCLE,
+ SRGB);
+ }
+
+ /**
+ * RadialGradientPaint
.
+ *
+ * @param center the center point, in user space, of the circle defining
+ * the gradient. The last color of the gradient is mapped to the perimeter
+ * of this circle
+ *
+ * @param radius the radius of the circle defining the extents of the color
+ * gradient
+ *
+ * @param focus the point, in user space, to which the first color is
+ * mapped
+ *
+ * @param fractions numbers ranging from 0.0 to 1.0 specifying the
+ * distribution of colors along the gradient
+ *
+ * @param colors array of colors to use in the gradient. The first color
+ * is used at the focus point, the last color around the perimeter of the
+ * circle.
+ *
+ * @throws NullPointerException if one of the points is null
+ *
+ * @throws IllegalArgumentException
+ * if fractions.length != colors.length, or if colors is less
+ * than 2 in size, or if radius < 0
+ *
+ */
+ public RadialGradientPaint(Point2D center, float radius,
+ Point2D focus,
+ float[] fractions, Color[] colors) {
+ this(center,
+ radius,
+ focus,
+ fractions,
+ colors,
+ NO_CYCLE,
+ SRGB);
+ }
+
+ /**
+ * RadialGradientPaint
.
+ *
+ * @param center the center point in user space of the circle defining the
+ * gradient. The last color of the gradient is mapped to the perimeter of
+ * this circle
+ *
+ * @param radius the radius of the circle defining the extents of the color
+ * gradient
+ *
+ * @param focus the point in user space to which the first color is mapped
+ *
+ * @param fractions numbers ranging from 0.0 to 1.0 specifying the
+ * distribution of colors along the gradient
+ *
+ * @param colors array of colors to use in the gradient. The first color is
+ * used at the focus point, the last color around the perimeter of the
+ * circle.
+ *
+ * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
+ *
+ * @param colorSpace which colorspace to use for interpolation,
+ * either SRGB or LINEAR_RGB
+ *
+ * @throws NullPointerException if one of the points is null
+ *
+ * @throws IllegalArgumentException
+ * if fractions.length != colors.length, or if colors is less
+ * than 2 in size, or if radius < 0
+ *
+ */
+ public RadialGradientPaint(Point2D center, float radius,
+ Point2D focus,
+ float[] fractions, Color[] colors,
+ CycleMethodEnum cycleMethod,
+ ColorSpaceEnum colorSpace) {
+ this(center,
+ radius,
+ focus,
+ fractions,
+ colors,
+ cycleMethod,
+ colorSpace,
+ new AffineTransform());
+ }
+
+ /**
+ * RadialGradientPaint
.
+ *
+ * @param center the center point in user space of the circle defining the
+ * gradient. The last color of the gradient is mapped to the perimeter of
+ * this circle
+ *
+ * @param radius the radius of the circle defining the extents of the color
+ * gradient.
+ *
+ * @param focus the point in user space to which the first color is mapped
+ *
+ * @param fractions numbers ranging from 0.0 to 1.0 specifying the
+ * distribution of colors along the gradient
+ *
+ * @param colors array of colors to use in the gradient. The first color is
+ * used at the focus point, the last color around the perimeter of the
+ * circle.
+ *
+ * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
+ *
+ * @param colorSpace which colorspace to use for interpolation,
+ * either SRGB or LINEAR_RGB
+ *
+ * @param gradientTransform transform to apply to the gradient
+ *
+ * @throws NullPointerException if one of the points is null,
+ * or gradientTransform is null
+ *
+ * @throws IllegalArgumentException
+ * if fractions.length != colors.length, or if colors is less
+ * than 2 in size, or if radius < 0
+ *
+ */
+ public RadialGradientPaint(Point2D center,
+ float radius,
+ Point2D focus,
+ float[] fractions, Color[] colors,
+ CycleMethodEnum cycleMethod,
+ ColorSpaceEnum colorSpace,
+ AffineTransform gradientTransform){
+ super(fractions, colors, cycleMethod, colorSpace, gradientTransform);
+
+ // Check input arguments
+ if (center == null) {
+ throw new NullPointerException("Center point should not be null.");
+ }
+
+ if (focus == null) {
+ throw new NullPointerException("Focus point should not be null.");
+ }
+
+ if (radius <= 0) {
+ throw new IllegalArgumentException("radius should be greater than zero");
+ }
+
+ //copy parameters
+ this.center = (Point2D)center.clone();
+ this.focus = (Point2D)focus.clone();
+ this.radius = radius;
+ }
+
+ /**
+ * RadialGradientPaint
, the gradient circle is
+ * defined by a bounding box.
+ *
+ * @param gradientBounds the bounding box, in user space, of the circle
+ * defining outermost extent of the gradient.
+ *
+ * @param fractions numbers ranging from 0.0 to 1.0 specifying the
+ * distribution of colors along the gradient
+ *
+ * @param colors array of colors to use in the gradient. The first color
+ * is used at the focus point, the last color around the perimeter of the
+ * circle.
+ *
+ * @throws NullPointerException if the gradientBounds is null
+ *
+ * @throws IllegalArgumentException
+ * if fractions.length != colors.length, or if colors is less
+ * than 2 in size, or if radius < 0
+ *
+ */
+ public RadialGradientPaint(Rectangle2D gradientBounds,
+ float[] fractions, Color[] colors) {
+
+ //calculate center point and radius based on bounding box coordinates.
+ this((float)gradientBounds.getX() +
+ ( (float)gradientBounds.getWidth() / 2),
+
+ (float)gradientBounds.getY() +
+ ( (float)gradientBounds.getWidth() / 2),
+
+ (float)gradientBounds.getWidth() / 2,
+ fractions, colors);
+ }
+
+
+ /** Paint
data. This is used only as a hint.
+ *
+ * @param deviceBounds the device space bounding box of the
+ * graphics primitive being rendered
+ *
+ * @param userBounds the user space bounding box of the
+ * graphics primitive being rendered
+ *
+ * @param transform the {@link AffineTransform} from user
+ * space into device space
+ *
+ * @param hints the hints that the context object uses to choose
+ * between rendering alternatives
+ *
+ * @return the {@link PaintContext} that generates color patterns.
+ *
+ * @throws IllegalArgumentException if the transform is not invertible
+ *
+ * @see PaintContext
+ */
+ public PaintContext createContext(ColorModel cm,
+ Rectangle deviceBounds,
+ Rectangle2D userBounds,
+ AffineTransform transform,
+ RenderingHints hints) {
+ // Can't modify the transform passed in...
+ transform = new AffineTransform(transform);
+ // incorporate the gradient transform
+ transform.concatenate(gradientTransform);
+
+ try{
+ return new RadialGradientPaintContext
+ (cm, deviceBounds, userBounds, transform, hints,
+ (float)center.getX(), (float)center.getY(), radius,
+ (float)focus.getX(), (float)focus.getY(),
+ fractions, colors, cycleMethod, colorSpace);
+ }
+
+ catch(NoninvertibleTransformException e){
+ throw new IllegalArgumentException("transform should be " +
+ "invertible");
+ }
+ }
+
+ /**
+ * Returns a copy of the center point of the radial gradient.
+ * @return a {@link Point2D} object that is a copy of the center point
+ */
+ public Point2D getCenterPoint() {
+ return new Point2D.Double(center.getX(), center.getY());
+ }
+
+ /** Returns a copy of the end point of the gradient axis.
+ * @return a {@link Point2D} object that is a copy of the focus point
+ */
+ public Point2D getFocusPoint() {
+ return new Point2D.Double(focus.getX(), focus.getY());
+ }
+
+ /** Returns the radius of the circle defining the radial gradient.
+ * @return the radius of the circle defining the radial gradient
+ */
+ public float getRadius() {
+ return radius;
+ }
+
+}
+
diff --git a/src/main/java/com/kitfox/svg/batik/RadialGradientPaintContext.java b/src/main/java/com/kitfox/svg/batik/RadialGradientPaintContext.java
new file mode 100644
index 0000000..5b097fd
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/batik/RadialGradientPaintContext.java
@@ -0,0 +1,775 @@
+/*****************************************************************************
+ * Copyright (C) The Apache Software Foundation. All rights reserved. *
+ * ------------------------------------------------------------------------- *
+ * This software is published under the terms of the Apache Software License *
+ * version 1.1, a copy of which has been included with this distribution in *
+ * the LICENSE file. *
+ *****************************************************************************/
+
+package com.kitfox.svg.batik;
+
+import java.awt.Color;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.ColorModel;
+
+/**
+ * Provides the actual implementation for the RadialGradientPaint.
+ * This is where the pixel processing is done. A RadialGradienPaint
+ * only supports circular gradients, but it should be possible to scale
+ * the circle to look approximately elliptical, by means of a
+ * gradient transform passed into the RadialGradientPaint constructor.
+ *
+ * @author Nicholas Talian, Vincent Hardy, Jim Graham, Jerry Evans
+ * @author Vincent Hardy
+ * @version $Id: RadialGradientPaintContext.java,v 1.2 2005/10/12 20:36:55 kitfox Exp $
+ *
+ */
+final class RadialGradientPaintContext extends MultipleGradientPaintContext {
+
+ /** True when (focus == center) */
+ private boolean isSimpleFocus = false;
+
+ /** True when (cycleMethod == NO_CYCLE) */
+ private boolean isNonCyclic = false;
+
+ /** Radius of the outermost circle defining the 100% gradient stop. */
+ private float radius;
+
+ /** Variables representing center and focus points. */
+ private float centerX, centerY, focusX, focusY;
+
+ /** Radius of the gradient circle squared. */
+ private float radiusSq;
+
+ /** Constant part of X, Y user space coordinates. */
+ private float constA, constB;
+
+ /** This value represents the solution when focusX == X. It is called
+ * trivial because it is easier to calculate than the general case.
+ */
+ private float trivial;
+
+ private static final int FIXED_POINT_IMPL = 1;
+ private static final int DEFAULT_IMPL = 2;
+ private static final int ANTI_ALIAS_IMPL = 3;
+
+ private int fillMethod;
+
+ /** Amount for offset when clamping focus. */
+ private static final float SCALEBACK = .97f;
+
+ /**
+ * Constructor for RadialGradientPaintContext.
+ *
+ * @param cm {@link ColorModel} that receives
+ * the Paint
data. This is used only as a hint.
+ *
+ * @param deviceBounds the device space bounding box of the
+ * graphics primitive being rendered
+ *
+ * @param userBounds the user space bounding box of the
+ * graphics primitive being rendered
+ *
+ * @param t the {@link AffineTransform} from user
+ * space into device space (gradientTransform should be
+ * concatenated with this)
+ *
+ * @param hints the hints that the context object uses to choose
+ * between rendering alternatives
+ *
+ * @param cx the center point in user space of the circle defining
+ * the gradient. The last color of the gradient is mapped to the
+ * perimeter of this circle X coordinate
+ *
+ * @param cy the center point in user space of the circle defining
+ * the gradient. The last color of the gradient is mapped to the
+ * perimeter of this circle Y coordinate
+ *
+ * @param r the radius of the circle defining the extents of the
+ * color gradient
+ *
+ * @param fx the point in user space to which the first color is mapped
+ * X coordinate
+ *
+ * @param fy the point in user space to which the first color is mapped
+ * Y coordinate
+ *
+ * @param fractions the fractions specifying the gradient distribution
+ *
+ * @param colors the gradient colors
+ *
+ * @param cycleMethod either NO_CYCLE, REFLECT, or REPEAT
+ *
+ * @param colorSpace which colorspace to use for interpolation,
+ * either SRGB or LINEAR_RGB
+ *
+ */
+ public RadialGradientPaintContext(ColorModel cm,
+ Rectangle deviceBounds,
+ Rectangle2D userBounds,
+ AffineTransform t,
+ RenderingHints hints,
+ float cx, float cy,
+ float r,
+ float fx, float fy,
+ float[] fractions,
+ Color[] colors,
+ MultipleGradientPaint.CycleMethodEnum
+ cycleMethod,
+ MultipleGradientPaint.ColorSpaceEnum
+ colorSpace)
+ throws NoninvertibleTransformException
+ {
+ super(cm, deviceBounds, userBounds, t, hints, fractions, colors,
+ cycleMethod, colorSpace);
+
+ //copy some parameters.
+ centerX = cx;
+ centerY = cy;
+ focusX = fx;
+ focusY = fy;
+ radius = r;
+
+ this.isSimpleFocus = (focusX == centerX) && (focusY == centerY);
+ this.isNonCyclic = (cycleMethod == RadialGradientPaint.NO_CYCLE);
+
+ //for use in the quadractic equation
+ radiusSq = radius * radius;
+
+ float dX = focusX - centerX;
+ float dY = focusY - centerY;
+
+ double dist = Math.sqrt((dX * dX) + (dY * dY));
+
+ //test if distance from focus to center is greater than the radius
+ if (dist > radius* SCALEBACK) { //clamp focus to radius
+ double angle = Math.atan2(dY, dX);
+
+ //x = r cos theta, y = r sin theta
+ focusX = (float)(SCALEBACK * radius * Math.cos(angle)) + centerX;
+
+ focusY = (float)(SCALEBACK * radius * Math.sin(angle)) + centerY;
+ }
+
+ //calculate the solution to be used in the case where X == focusX
+ //in cyclicCircularGradientFillRaster
+ dX = focusX - centerX;
+ trivial = (float)Math.sqrt(radiusSq - (dX * dX));
+
+ // constant parts of X, Y user space coordinates
+ constA = a02 - centerX;
+ constB = a12 - centerY;
+
+ Object colorRend;
+ Object rend;
+ //hints can be null on Mac OSX
+ if (hints == null)
+ {
+ colorRend = RenderingHints.VALUE_COLOR_RENDER_DEFAULT;
+ rend = RenderingHints.VALUE_RENDER_DEFAULT;
+ }
+ else
+ {
+ colorRend = hints.get(RenderingHints.KEY_COLOR_RENDERING);
+ rend = hints.get(RenderingHints.KEY_RENDERING);
+ }
+
+ fillMethod = 0;
+
+ if ((rend == RenderingHints.VALUE_RENDER_QUALITY) ||
+ (colorRend == RenderingHints.VALUE_COLOR_RENDER_QUALITY)) {
+ // System.out.println("AAHints set: " + rend + ", " + colorRend);
+ fillMethod = ANTI_ALIAS_IMPL;
+ }
+
+ if ((rend == RenderingHints.VALUE_RENDER_SPEED) ||
+ (colorRend == RenderingHints.VALUE_COLOR_RENDER_SPEED)) {
+ // System.out.println("SPHints set: " + rend + ", " + colorRend);
+ fillMethod = DEFAULT_IMPL;
+ }
+
+ // We are in the 'default' case, no hint or hint set to
+ // DEFAULT values...
+ if (fillMethod == 0) {
+ // For now we will always use the 'default' impl if
+ // one is not specified.
+ fillMethod = DEFAULT_IMPL;
+
+ if (false) {
+ // This could be used for a 'smart' choice in
+ // the default case, if the gradient has obvious
+ // discontinuites use AA, otherwise default
+ if (hasDiscontinuity) {
+ fillMethod = ANTI_ALIAS_IMPL;
+ } else {
+ fillMethod = DEFAULT_IMPL;
+ }
+ }
+ }
+
+ if ((fillMethod == DEFAULT_IMPL) &&
+ (isSimpleFocus && isNonCyclic && isSimpleLookup)) {
+ this.calculateFixedPointSqrtLookupTable();
+ fillMethod = FIXED_POINT_IMPL;
+ }
+ }
+
+ /**
+ * Return a Raster containing the colors generated for the graphics
+ * operation.
+ * @param x,y,w,h The area in device space for which colors are
+ * generated.
+ */
+ protected void fillRaster(int pixels[], int off, int adjust,
+ int x, int y, int w, int h) {
+ switch(fillMethod) {
+ case FIXED_POINT_IMPL:
+ // System.out.println("Calling FP");
+ fixedPointSimplestCaseNonCyclicFillRaster(pixels, off, adjust, x,
+ y, w, h);
+ break;
+ case ANTI_ALIAS_IMPL:
+ // System.out.println("Calling AA");
+ antiAliasFillRaster(pixels, off, adjust, x, y, w, h);
+ break;
+ case DEFAULT_IMPL:
+ default:
+ // System.out.println("Calling Default");
+ cyclicCircularGradientFillRaster(pixels, off, adjust, x, y, w, h);
+ }
+ }
+
+ /**
+ * This code works in the simplest of cases, where the focus == center
+ * point, the gradient is noncyclic, and the gradient lookup method is
+ * fast (single array index, no conversion necessary).
+ *
+ */
+ private void fixedPointSimplestCaseNonCyclicFillRaster(int pixels[],
+ int off,
+ int adjust,
+ int x, int y,
+ int w, int h) {
+ float iSq=0; // Square distance index
+ final float indexFactor = fastGradientArraySize / radius;
+
+ //constant part of X and Y coordinates for the entire raster
+ final float constX = (a00*x) + (a01*y) + constA;
+ final float constY = (a10*x) + (a11*y) + constB;
+ final float deltaX = indexFactor * a00; //incremental change in dX
+ final float deltaY = indexFactor * a10; //incremental change in dY
+ float dX, dY; //the current distance from center
+ final int fixedArraySizeSq=
+ (fastGradientArraySize * fastGradientArraySize);
+ float g, gDelta, gDeltaDelta, temp; //gradient square value
+ int gIndex; // integer number used to index gradient array
+ int iSqInt; // Square distance index
+
+ int end, j; //indexing variables
+ int indexer = off;//used to index pixels array
+
+ temp = ((deltaX * deltaX) + (deltaY * deltaY));
+ gDeltaDelta = ((temp * 2));
+
+ if (temp > fixedArraySizeSq) {
+ // This combination of scale and circle radius means
+ // essentially no pixels will be anything but the end
+ // stop color. This also avoids math problems.
+ final int val = gradientOverflow;
+ for(j = 0; j < h; j++){ //for every row
+ //for every column (inner loop begins here)
+ for (end = indexer+w; indexer < end; indexer++)
+ pixels[indexer] = val;
+ indexer += adjust;
+ }
+ return;
+ }
+
+ // For every point in the raster, calculate the color at that point
+ for(j = 0; j < h; j++){ //for every row
+ //x and y (in user space) of the first pixel of this row
+ dX = indexFactor * ((a01*j) + constX);
+ dY = indexFactor * ((a11*j) + constY);
+
+ // these values below here allow for an incremental calculation
+ // of dX^2 + dY^2
+
+ //initialize to be equal to distance squared
+ g = (((dY * dY) + (dX * dX)) );
+ gDelta = (((((deltaY * dY) + (deltaX * dX))* 2) +
+ temp));
+
+ //for every column (inner loop begins here)
+ for (end = indexer+w; indexer < end; indexer++) {
+ //determine the distance to the center
+
+ //since this is a non cyclic fill raster, crop at "1" and 0
+ if (g >= fixedArraySizeSq) {
+ pixels[indexer] = gradientOverflow;
+ }
+
+ // This should not happen as gIndex is a square
+ // quantity. Code commented out on purpose, can't underflow.
+ // else if (g < 0) {
+ // gIndex = 0;
+ // }
+
+ else {
+ iSq = (g * invSqStepFloat);
+
+ iSqInt = (int)iSq; //chop off fractional part
+ iSq -= iSqInt;
+ gIndex = sqrtLutFixed[iSqInt];
+ gIndex += (int)(iSq * (sqrtLutFixed[iSqInt + 1]-gIndex));
+ pixels[indexer] = gradient[gIndex];
+ }
+
+
+ //incremental calculation
+ g += gDelta;
+ gDelta += gDeltaDelta;
+ }
+ indexer += adjust;
+ }
+ }
+
+ /** Length of a square distance intervale in the lookup table */
+ private float invSqStepFloat;
+
+ /** Used to limit the size of the square root lookup table */
+ private int MAX_PRECISION = 256;
+
+ /** Square root lookup table */
+ private int sqrtLutFixed[] = new int[MAX_PRECISION];
+
+ /**
+ * Build square root lookup table
+ */
+ private void calculateFixedPointSqrtLookupTable() {
+ float sqStepFloat;
+ sqStepFloat = ((fastGradientArraySize * fastGradientArraySize)
+ / (MAX_PRECISION - 2));
+
+ // The last two values are the same so that linear square root
+ // interpolation can happen on the maximum reachable element in the
+ // lookup table (precision-2)
+ int i;
+ for (i = 0; i < MAX_PRECISION - 1; i++) {
+ sqrtLutFixed[i] = (int)(Math.sqrt(i*sqStepFloat));
+ }
+ sqrtLutFixed[i] = sqrtLutFixed[i-1];
+ invSqStepFloat = 1/sqStepFloat;
+ }
+
+ /** Fill the raster, cycling the gradient colors when a point falls outside
+ * of the perimeter of the 100% stop circle.
+ *
+ * This calculation first computes the intersection point of the line
+ * from the focus through the current point in the raster, and the
+ * perimeter of the gradient circle.
+ *
+ * Then it determines the percentage distance of the current point along
+ * that line (focus is 0%, perimeter is 100%).
+ *
+ * Equation of a circle centered at (a,b) with radius r:
+ * (x-a)^2 + (y-b)^2 = r^2
+ * Equation of a line with slope m and y-intercept b
+ * y = mx + b
+ * replacing y in the cirlce equation and solving using the quadratic
+ * formula produces the following set of equations. Constant factors have
+ * been extracted out of the inner loop.
+ *
+ */
+ private void cyclicCircularGradientFillRaster(int pixels[], int off,
+ int adjust,
+ int x, int y,
+ int w, int h) {
+ // Constant part of the C factor of the quadratic equation
+ final double constC =
+ -(radiusSq) + (centerX * centerX) + (centerY * centerY);
+ double A; //coefficient of the quadratic equation (Ax^2 + Bx + C = 0)
+ double B; //coefficient of the quadratic equation
+ double C; //coefficient of the quadratic equation
+ double slope; //slope of the focus-perimeter line
+ double yintcpt; //y-intercept of the focus-perimeter line
+ double solutionX;//intersection with circle X coordinate
+ double solutionY;//intersection with circle Y coordinate
+ final float constX = (a00*x) + (a01*y) + a02;//const part of X coord
+ final float constY = (a10*x) + (a11*y) + a12; //const part of Y coord
+ final float precalc2 = 2 * centerY;//const in inner loop quad. formula
+ final float precalc3 =-2 * centerX;//const in inner loop quad. formula
+ float X; // User space point X coordinate
+ float Y; // User space point Y coordinate
+ float g;//value between 0 and 1 specifying position in the gradient
+ float det; //determinant of quadratic formula (should always be >0)
+ float currentToFocusSq;//sq distance from the current pt. to focus
+ float intersectToFocusSq;//sq distance from the intersect pt. to focus
+ float deltaXSq; //temp variable for a change in X squared.
+ float deltaYSq; //temp variable for a change in Y squared.
+ int indexer = off; //index variable for pixels array
+ int i, j; //indexing variables for FOR loops
+ int pixInc = w+adjust;//incremental index change for pixels array
+
+ for (j = 0; j < h; j++) { //for every row
+
+ X = (a01*j) + constX; //constants from column to column
+ Y = (a11*j) + constY;
+
+ //for every column (inner loop begins here)
+ for (i = 0; i < w; i++) {
+
+ // special case to avoid divide by zero or very near zero
+ if (((X-focusX)>-0.000001) &&
+ ((X-focusX)< 0.000001)) {
+ solutionX = focusX;
+
+ solutionY = centerY;
+
+ solutionY += (Y > focusY)?trivial:-trivial;
+ }
+
+ else {
+
+ //slope of the focus-current line
+ slope = (Y - focusY) / (X - focusX);
+
+ yintcpt = Y - (slope * X); //y-intercept of that same line
+
+ //use the quadratic formula to calculate the intersection
+ //point
+ A = (slope * slope) + 1;
+
+ B = precalc3 + (-2 * slope * (centerY - yintcpt));
+
+ C = constC + (yintcpt* (yintcpt - precalc2));
+
+ det = (float)Math.sqrt((B * B) - ( 4 * A * C));
+
+ solutionX = -B;
+
+ //choose the positive or negative root depending
+ //on where the X coord lies with respect to the focus.
+ solutionX += (X < focusX)?-det:det;
+
+ solutionX = solutionX / (2 * A);//divisor
+
+ solutionY = (slope * solutionX) + yintcpt;
+ }
+
+ //calculate the square of the distance from the current point
+ //to the focus and the square of the distance from the
+ //intersection point to the focus. Want the squares so we can
+ //do 1 square root after division instead of 2 before.
+
+ deltaXSq = (float)solutionX - focusX;
+ deltaXSq = deltaXSq * deltaXSq;
+
+ deltaYSq = (float)solutionY - focusY;
+ deltaYSq = deltaYSq * deltaYSq;
+
+ intersectToFocusSq = deltaXSq + deltaYSq;
+
+ deltaXSq = X - focusX;
+ deltaXSq = deltaXSq * deltaXSq;
+
+ deltaYSq = Y - focusY;
+ deltaYSq = deltaYSq * deltaYSq;
+
+ currentToFocusSq = deltaXSq + deltaYSq;
+
+ //want the percentage (0-1) of the current point along the
+ //focus-circumference line
+ g = (float)Math.sqrt(currentToFocusSq / intersectToFocusSq);
+
+ //Get the color at this point
+ pixels[indexer + i] = indexIntoGradientsArrays(g);
+
+ X += a00; //incremental change in X, Y
+ Y += a10;
+ } //end inner loop
+ indexer += pixInc;
+ } //end outer loop
+ }
+
+
+ /** Fill the raster, cycling the gradient colors when a point
+ * falls outside of the perimeter of the 100% stop circle. Use
+ * the anti-aliased gradient lookup.
+ *
+ * This calculation first computes the intersection point of the line
+ * from the focus through the current point in the raster, and the
+ * perimeter of the gradient circle.
+ *
+ * Then it determines the percentage distance of the current point along
+ * that line (focus is 0%, perimeter is 100%).
+ *
+ * Equation of a circle centered at (a,b) with radius r:
+ * (x-a)^2 + (y-b)^2 = r^2
+ * Equation of a line with slope m and y-intercept b
+ * y = mx + b
+ * replacing y in the cirlce equation and solving using the quadratic
+ * formula produces the following set of equations. Constant factors have
+ * been extracted out of the inner loop.
+ * */
+ private void antiAliasFillRaster(int pixels[], int off,
+ int adjust,
+ int x, int y,
+ int w, int h) {
+ // Constant part of the C factor of the quadratic equation
+ final double constC =
+ -(radiusSq) + (centerX * centerX) + (centerY * centerY);
+ //coefficients of the quadratic equation (Ax^2 + Bx + C = 0)
+ final float precalc2 = 2 * centerY;//const in inner loop quad. formula
+ final float precalc3 =-2 * centerX;//const in inner loop quad. formula
+
+ //const part of X,Y coord (shifted to bottom left corner of pixel.
+ final float constX = (a00*(x-.5f)) + (a01*(y+.5f)) + a02;
+ final float constY = (a10*(x-.5f)) + (a11*(y+.5f)) + a12;
+ float X; // User space point X coordinate
+ float Y; // User space point Y coordinate
+ int i, j; //indexing variables for FOR loops
+ int indexer = off-1; //index variable for pixels array
+
+ // Size of a pixel in user space.
+ double pixSzSq = (float)(a00*a00+a01*a01+a10*a10+a11*a11);
+ double [] prevGs = new double[w+1];
+ double deltaXSq, deltaYSq;
+ double solutionX, solutionY;
+ double slope, yintcpt, A, B, C, det;
+ double intersectToFocusSq, currentToFocusSq;
+ double g00, g01, g10, g11;
+
+ // Set X,Y to top left corner of first pixel of first row.
+ X = constX - a01;
+ Y = constY - a11;
+
+ // Calc top row of g's.
+ for (i=0; i <= w; i++) {
+ // special case to avoid divide by zero or very near zero
+ if (((X-focusX)>-0.000001) &&
+ ((X-focusX)< 0.000001)) {
+ solutionX = focusX;
+ solutionY = centerY;
+ solutionY += (Y > focusY)?trivial:-trivial;
+ }
+ else {
+ // Formula for Circle: (X-Xc)^2 + (Y-Yc)^2 - R^2 = 0
+ // Formula line: Y = Slope*x + Y0;
+ //
+ // So you substitue line into Circle and apply
+ // Quadradic formula.
+
+
+ //slope of the focus-current line
+ slope = (Y - focusY) / (X - focusX);
+
+ yintcpt = Y - (slope * X); //y-intercept of that same line
+
+ //use the quadratic formula to calculate the intersection
+ //point
+ A = (slope * slope) + 1;
+
+ B = precalc3 + (-2 * slope * (centerY - yintcpt));
+
+ C = constC + (yintcpt* (yintcpt - precalc2));
+
+ det = Math.sqrt((B * B) - ( 4 * A * C));
+
+ solutionX = -B;
+
+ //choose the positive or negative root depending
+ //on where the X coord lies with respect to the focus.
+ solutionX += (X < focusX)?-det:det;
+
+ solutionX = solutionX / (2 * A);//divisor
+
+ solutionY = (slope * solutionX) + yintcpt;
+ }
+
+ //calculate the square of the distance from the current point
+ //to the focus and the square of the distance from the
+ //intersection point to the focus. Want the squares so we can
+ //do 1 square root after division instead of 2 before.
+ deltaXSq = solutionX - focusX;
+ deltaXSq = deltaXSq * deltaXSq;
+
+ deltaYSq = solutionY - focusY;
+ deltaYSq = deltaYSq * deltaYSq;
+
+ intersectToFocusSq = deltaXSq + deltaYSq;
+
+ deltaXSq = X - focusX;
+ deltaXSq = deltaXSq * deltaXSq;
+
+ deltaYSq = Y - focusY;
+ deltaYSq = deltaYSq * deltaYSq;
+
+ currentToFocusSq = deltaXSq + deltaYSq;
+
+ //want the percentage (0-1) of the current point along the
+ //focus-circumference line
+ prevGs[i] = Math.sqrt(currentToFocusSq / intersectToFocusSq);
+
+ X += a00; //incremental change in X, Y
+ Y += a10;
+ }
+
+ for (j = 0; j < h; j++) { //for every row
+
+ // Set X,Y to bottom edge of pixel row.
+ X = (a01*j) + constX; //constants from row to row
+ Y = (a11*j) + constY;
+
+ g10 = prevGs[0];
+ // special case to avoid divide by zero or very near zero
+ if (((X-focusX)>-0.000001) &&
+ ((X-focusX)< 0.000001)) {
+ solutionX = focusX;
+ solutionY = centerY;
+ solutionY += (Y > focusY)?trivial:-trivial;
+ }
+ else {
+ // Formula for Circle: (X-Xc)^2 + (Y-Yc)^2 - R^2 = 0
+ // Formula line: Y = Slope*x + Y0;
+ //
+ // So you substitue line into Circle and apply
+ // Quadradic formula.
+
+
+ //slope of the focus-current line
+ slope = (Y - focusY) / (X - focusX);
+
+ yintcpt = Y - (slope * X); //y-intercept of that same line
+
+ //use the quadratic formula to calculate the intersection
+ //point
+ A = (slope * slope) + 1;
+
+ B = precalc3 + (-2 * slope * (centerY - yintcpt));
+
+ C = constC + (yintcpt* (yintcpt - precalc2));
+
+ det = Math.sqrt((B * B) - ( 4 * A * C));
+
+ solutionX = -B;
+
+ //choose the positive or negative root depending
+ //on where the X coord lies with respect to the focus.
+ solutionX += (X < focusX)?-det:det;
+
+ solutionX = solutionX / (2 * A);//divisor
+
+ solutionY = (slope * solutionX) + yintcpt;
+ }
+
+ //calculate the square of the distance from the current point
+ //to the focus and the square of the distance from the
+ //intersection point to the focus. Want the squares so we can
+ //do 1 square root after division instead of 2 before.
+ deltaXSq = solutionX - focusX;
+ deltaXSq = deltaXSq * deltaXSq;
+
+ deltaYSq = solutionY - focusY;
+ deltaYSq = deltaYSq * deltaYSq;
+
+ intersectToFocusSq = deltaXSq + deltaYSq;
+
+ deltaXSq = X - focusX;
+ deltaXSq = deltaXSq * deltaXSq;
+
+ deltaYSq = Y - focusY;
+ deltaYSq = deltaYSq * deltaYSq;
+
+ currentToFocusSq = deltaXSq + deltaYSq;
+ g11 = Math.sqrt(currentToFocusSq / intersectToFocusSq);
+ prevGs[0] = g11;
+
+ X += a00; //incremental change in X, Y
+ Y += a10;
+
+ //for every column (inner loop begins here)
+ for (i=1; i <= w; i++) {
+ g00 = g10;
+ g01 = g11;
+ g10 = prevGs[i];
+
+ // special case to avoid divide by zero or very near zero
+ if (((X-focusX)>-0.000001) &&
+ ((X-focusX)< 0.000001)) {
+ solutionX = focusX;
+ solutionY = centerY;
+ solutionY += (Y > focusY)?trivial:-trivial;
+ }
+ else {
+ // Formula for Circle: (X-Xc)^2 + (Y-Yc)^2 - R^2 = 0
+ // Formula line: Y = Slope*x + Y0;
+ //
+ // So you substitue line into Circle and apply
+ // Quadradic formula.
+
+
+ //slope of the focus-current line
+ slope = (Y - focusY) / (X - focusX);
+
+ yintcpt = Y - (slope * X); //y-intercept of that same line
+
+ //use the quadratic formula to calculate the intersection
+ //point
+ A = (slope * slope) + 1;
+
+ B = precalc3 + (-2 * slope * (centerY - yintcpt));
+
+ C = constC + (yintcpt* (yintcpt - precalc2));
+
+ det = Math.sqrt((B * B) - ( 4 * A * C));
+
+ solutionX = -B;
+
+ //choose the positive or negative root depending
+ //on where the X coord lies with respect to the focus.
+ solutionX += (X < focusX)?-det:det;
+
+ solutionX = solutionX / (2 * A);//divisor
+
+ solutionY = (slope * solutionX) + yintcpt;
+ }
+
+ //calculate the square of the distance from the current point
+ //to the focus and the square of the distance from the
+ //intersection point to the focus. Want the squares so we can
+ //do 1 square root after division instead of 2 before.
+ deltaXSq = solutionX - focusX;
+ deltaXSq = deltaXSq * deltaXSq;
+
+ deltaYSq = solutionY - focusY;
+ deltaYSq = deltaYSq * deltaYSq;
+
+ intersectToFocusSq = deltaXSq + deltaYSq;
+
+ deltaXSq = X - focusX;
+ deltaXSq = deltaXSq * deltaXSq;
+
+ deltaYSq = Y - focusY;
+ deltaYSq = deltaYSq * deltaYSq;
+
+ currentToFocusSq = deltaXSq + deltaYSq;
+ g11 = Math.sqrt(currentToFocusSq / intersectToFocusSq);
+ prevGs[i] = g11;
+
+ //Get the color at this point
+ pixels[indexer+i] = indexGradientAntiAlias
+ ((float)((g00+g01+g10+g11)/4),
+ (float)Math.max(Math.abs(g11-g00),
+ Math.abs(g10-g01)));
+
+ X += a00; //incremental change in X, Y
+ Y += a10;
+ } //end inner loop
+ indexer += (w+adjust);
+ } //end outer loop
+ }
+}
diff --git a/src/main/java/com/kitfox/svg/composite/AdobeComposite.java b/src/main/java/com/kitfox/svg/composite/AdobeComposite.java
new file mode 100644
index 0000000..00cd929
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/composite/AdobeComposite.java
@@ -0,0 +1,70 @@
+/*
+ * AdobeComposite.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 1, 2004, 6:40 AM
+ */
+
+package com.kitfox.svg.composite;
+
+import java.awt.*;
+import java.awt.image.*;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class AdobeComposite implements Composite
+{
+ public static final int CT_NORMAL = 0;
+ public static final int CT_MULTIPLY = 1;
+ public static final int CT_LAST = 2;
+
+ final int compositeType;
+ final float extraAlpha;
+
+ /** Creates a new instance of AdobeComposite */
+ public AdobeComposite(int compositeType, float extraAlpha)
+ {
+ this.compositeType = compositeType;
+ this.extraAlpha = extraAlpha;
+
+ if (compositeType < 0 || compositeType >= CT_LAST)
+ {
+ new Exception("Invalid composite type").printStackTrace();
+ }
+
+ if (extraAlpha < 0f || extraAlpha > 1f)
+ {
+ new Exception("Invalid alpha").printStackTrace();
+ }
+ }
+
+ public int getCompositeType() { return compositeType; }
+
+ public CompositeContext createContext(ColorModel srcColorModel, ColorModel dstColorModel, RenderingHints hints)
+ {
+ return new AdobeCompositeContext(compositeType, extraAlpha);
+ }
+
+}
diff --git a/src/main/java/com/kitfox/svg/composite/AdobeCompositeContext.java b/src/main/java/com/kitfox/svg/composite/AdobeCompositeContext.java
new file mode 100644
index 0000000..86185a2
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/composite/AdobeCompositeContext.java
@@ -0,0 +1,97 @@
+/*
+ * AdobeCompositeContext.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 1, 2004, 6:41 AM
+ */
+
+package com.kitfox.svg.composite;
+
+import java.awt.*;
+import java.awt.image.*;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class AdobeCompositeContext implements CompositeContext
+{
+ final int compositeType;
+ final float extraAlpha;
+
+ float[] rgba_src = new float[4];
+ float[] rgba_dstIn = new float[4];
+ float[] rgba_dstOut = new float[4];
+
+ /** Creates a new instance of AdobeCompositeContext */
+ public AdobeCompositeContext(int compositeType, float extraAlpha)
+ {
+ this.compositeType = compositeType;
+ this.extraAlpha = extraAlpha;
+
+ rgba_dstOut[3] = 1f;
+ }
+
+ public void compose(Raster src, Raster dstIn, WritableRaster dstOut)
+ {
+ int width = src.getWidth();
+ int height = src.getHeight();
+
+ for (int j = 0; j < height; j++)
+ {
+ for (int i = 0; i < width; i++)
+ {
+ src.getPixel(i, j, rgba_src);
+ dstIn.getPixel(i, j, rgba_dstIn);
+
+ //Ignore transparent pixels
+ if (rgba_src[3] == 0)
+ {
+// dstOut.setPixel(i, j, rgba_dstIn);
+ continue;
+ }
+
+ float alpha = rgba_src[3];
+
+ switch (compositeType)
+ {
+ default:
+ case AdobeComposite.CT_NORMAL:
+ rgba_dstOut[0] = rgba_src[0] * alpha + rgba_dstIn[0] * (1f - alpha);
+ rgba_dstOut[1] = rgba_src[1] * alpha + rgba_dstIn[1] * (1f - alpha);
+ rgba_dstOut[2] = rgba_src[2] * alpha + rgba_dstIn[2] * (1f - alpha);
+ break;
+ case AdobeComposite.CT_MULTIPLY:
+ rgba_dstOut[0] = rgba_src[0] * rgba_dstIn[0] * alpha + rgba_dstIn[0] * (1f - alpha);
+ rgba_dstOut[1] = rgba_src[1] * rgba_dstIn[1] * alpha + rgba_dstIn[1] * (1f - alpha);
+ rgba_dstOut[2] = rgba_src[2] * rgba_dstIn[2] * alpha + rgba_dstIn[2] * (1f - alpha);
+ break;
+ }
+ }
+ }
+ }
+
+ public void dispose() {
+ }
+
+}
diff --git a/src/main/java/com/kitfox/svg/package-info.java b/src/main/java/com/kitfox/svg/package-info.java
new file mode 100644
index 0000000..ebced40
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/package-info.java
@@ -0,0 +1,9 @@
+/**
+ * Provides the nodes of an SVG scene graph. This graph can be queried, updated
+ * and picked against at runtime. See the online docs for instructions on
+ * how to use SVGSalamander
+ *
+ * @author Mark McKay (C) 2005
+ */
+
+package com.kitfox.svg;
\ No newline at end of file
diff --git a/src/main/java/com/kitfox/svg/pathcmd/Arc.java b/src/main/java/com/kitfox/svg/pathcmd/Arc.java
new file mode 100644
index 0000000..c48d149
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/pathcmd/Arc.java
@@ -0,0 +1,245 @@
+/*
+ * MoveTo.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 26, 2004, 8:40 PM
+ */
+
+package com.kitfox.svg.pathcmd;
+
+//import org.apache.batik.ext.awt.geom.ExtendedGeneralPath;
+import java.awt.*;
+import java.awt.geom.*;
+
+/**
+ * This is a little used SVG function, as most editors will save curves as
+ * Beziers. To reduce the need to rely on the Batik library, this functionallity
+ * is being bypassed for the time being. In the future, it would be nice to
+ * extend the GeneralPath command to include the arcTo ability provided by Batik.
+ *
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class Arc extends PathCommand
+{
+
+ public float rx = 0f;
+ public float ry = 0f;
+ public float xAxisRot = 0f;
+ public boolean largeArc = false;
+ public boolean sweep = false;
+ public float x = 0f;
+ public float y = 0f;
+
+ /** Creates a new instance of MoveTo */
+ public Arc() {
+ }
+
+ public Arc(boolean isRelative, float rx, float ry, float xAxisRot, boolean largeArc, boolean sweep, float x, float y) {
+ super(isRelative);
+ this.rx = rx;
+ this.ry = ry;
+ this.xAxisRot = xAxisRot;
+ this.largeArc = largeArc;
+ this.sweep = sweep;
+ this.x = x;
+ this.y = y;
+ }
+
+// public void appendPath(ExtendedGeneralPath path, BuildHistory hist)
+ public void appendPath(GeneralPath path, BuildHistory hist)
+ {
+ float offx = isRelative ? hist.history[0].x : 0f;
+ float offy = isRelative ? hist.history[0].y : 0f;
+
+ arcTo(path, rx, ry, xAxisRot, largeArc, sweep, x + offx, y + offy, hist.history[0].x, hist.history[0].y);
+// path.lineTo(x + offx, y + offy);
+ hist.setPoint(x + offx, y + offy);
+ }
+
+ public int getNumKnotsAdded()
+ {
+ return 6;
+ }
+
+ /**
+ * Adds an elliptical arc, defined by two radii, an angle from the
+ * x-axis, a flag to choose the large arc or not, a flag to
+ * indicate if we increase or decrease the angles and the final
+ * point of the arc.
+ *
+ * @param rx the x radius of the ellipse
+ * @param ry the y radius of the ellipse
+ *
+ * @param angle the angle from the x-axis of the current
+ * coordinate system to the x-axis of the ellipse in degrees.
+ *
+ * @param largeArcFlag the large arc flag. If true the arc
+ * spanning less than or equal to 180 degrees is chosen, otherwise
+ * the arc spanning greater than 180 degrees is chosen
+ *
+ * @param sweepFlag the sweep flag. If true the line joining
+ * center to arc sweeps through decreasing angles otherwise it
+ * sweeps through increasing angles
+ *
+ * @param x the absolute x coordinate of the final point of the arc.
+ * @param y the absolute y coordinate of the final point of the arc.
+ * @param x0 - The absolute x coordinate of the initial point of the arc.
+ * @param y0 - The absolute y coordinate of the initial point of the arc.
+ */
+ public void arcTo(GeneralPath path, float rx, float ry,
+ float angle,
+ boolean largeArcFlag,
+ boolean sweepFlag,
+ float x, float y, float x0, float y0)
+ {
+
+ // Ensure radii are valid
+ if (rx == 0 || ry == 0) {
+ path.lineTo((float) x, (float) y);
+ return;
+ }
+
+ if (x0 == x && y0 == y) {
+ // If the endpoints (x, y) and (x0, y0) are identical, then this
+ // is equivalent to omitting the elliptical arc segment entirely.
+ return;
+ }
+
+ Arc2D arc = computeArc(x0, y0, rx, ry, angle,
+ largeArcFlag, sweepFlag, x, y);
+ if (arc == null) return;
+
+ AffineTransform t = AffineTransform.getRotateInstance
+ (Math.toRadians(angle), arc.getCenterX(), arc.getCenterY());
+ Shape s = t.createTransformedShape(arc);
+ path.append(s, true);
+ }
+
+
+ /**
+ * This constructs an unrotated Arc2D from the SVG specification of an
+ * Elliptical arc. To get the final arc you need to apply a rotation
+ * transform such as:
+ *
+ * AffineTransform.getRotateInstance
+ * (angle, arc.getX()+arc.getWidth()/2, arc.getY()+arc.getHeight()/2);
+ */
+ public static Arc2D computeArc(double x0, double y0,
+ double rx, double ry,
+ double angle,
+ boolean largeArcFlag,
+ boolean sweepFlag,
+ double x, double y) {
+ //
+ // Elliptical arc implementation based on the SVG specification notes
+ //
+
+ // Compute the half distance between the current and the final point
+ double dx2 = (x0 - x) / 2.0;
+ double dy2 = (y0 - y) / 2.0;
+ // Convert angle from degrees to radians
+ angle = Math.toRadians(angle % 360.0);
+ double cosAngle = Math.cos(angle);
+ double sinAngle = Math.sin(angle);
+
+ //
+ // Step 1 : Compute (x1, y1)
+ //
+ double x1 = (cosAngle * dx2 + sinAngle * dy2);
+ double y1 = (-sinAngle * dx2 + cosAngle * dy2);
+ // Ensure radii are large enough
+ rx = Math.abs(rx);
+ ry = Math.abs(ry);
+ double Prx = rx * rx;
+ double Pry = ry * ry;
+ double Px1 = x1 * x1;
+ double Py1 = y1 * y1;
+ // check that radii are large enough
+ double radiiCheck = Px1/Prx + Py1/Pry;
+ if (radiiCheck > 1) {
+ rx = Math.sqrt(radiiCheck) * rx;
+ ry = Math.sqrt(radiiCheck) * ry;
+ Prx = rx * rx;
+ Pry = ry * ry;
+ }
+
+ //
+ // Step 2 : Compute (cx1, cy1)
+ //
+ double sign = (largeArcFlag == sweepFlag) ? -1 : 1;
+ double sq = ((Prx*Pry)-(Prx*Py1)-(Pry*Px1)) / ((Prx*Py1)+(Pry*Px1));
+ sq = (sq < 0) ? 0 : sq;
+ double coef = (sign * Math.sqrt(sq));
+ double cx1 = coef * ((rx * y1) / ry);
+ double cy1 = coef * -((ry * x1) / rx);
+
+ //
+ // Step 3 : Compute (cx, cy) from (cx1, cy1)
+ //
+ double sx2 = (x0 + x) / 2.0;
+ double sy2 = (y0 + y) / 2.0;
+ double cx = sx2 + (cosAngle * cx1 - sinAngle * cy1);
+ double cy = sy2 + (sinAngle * cx1 + cosAngle * cy1);
+
+ //
+ // Step 4 : Compute the angleStart (angle1) and the angleExtent (dangle)
+ //
+ double ux = (x1 - cx1) / rx;
+ double uy = (y1 - cy1) / ry;
+ double vx = (-x1 - cx1) / rx;
+ double vy = (-y1 - cy1) / ry;
+ double p, n;
+ // Compute the angle start
+ n = Math.sqrt((ux * ux) + (uy * uy));
+ p = ux; // (1 * ux) + (0 * uy)
+ sign = (uy < 0) ? -1d : 1d;
+ double angleStart = Math.toDegrees(sign * Math.acos(p / n));
+
+ // Compute the angle extent
+ n = Math.sqrt((ux * ux + uy * uy) * (vx * vx + vy * vy));
+ p = ux * vx + uy * vy;
+ sign = (ux * vy - uy * vx < 0) ? -1d : 1d;
+ double angleExtent = Math.toDegrees(sign * Math.acos(p / n));
+ if(!sweepFlag && angleExtent > 0) {
+ angleExtent -= 360f;
+ } else if (sweepFlag && angleExtent < 0) {
+ angleExtent += 360f;
+ }
+ angleExtent %= 360f;
+ angleStart %= 360f;
+
+ //
+ // We can now build the resulting Arc2D in double precision
+ //
+ Arc2D.Double arc = new Arc2D.Double();
+ arc.x = cx - rx;
+ arc.y = cy - ry;
+ arc.width = rx * 2.0;
+ arc.height = ry * 2.0;
+ arc.start = -angleStart;
+ arc.extent = -angleExtent;
+
+ return arc;
+ }
+}
diff --git a/src/main/java/com/kitfox/svg/pathcmd/BuildHistory.java b/src/main/java/com/kitfox/svg/pathcmd/BuildHistory.java
new file mode 100644
index 0000000..d16c11a
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/pathcmd/BuildHistory.java
@@ -0,0 +1,62 @@
+/*
+ * BuildHistory.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 26, 2004, 9:18 PM
+ */
+
+package com.kitfox.svg.pathcmd;
+
+import java.awt.geom.Point2D;
+
+/**
+ * When building a path from command segments, most need to cache information
+ * (such as the point finished at) for future commands. This structure allows
+ * that
+ *
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class BuildHistory {
+
+// Point2D.Float[] history = new Point2D.Float[2];
+ Point2D.Float[] history = {new Point2D.Float(), new Point2D.Float()};
+ int length = 0;
+
+ /** Creates a new instance of BuildHistory */
+ public BuildHistory() {
+ }
+
+ public void setPoint(float x, float y)
+ {
+ history[0].setLocation(x, y);
+ length = 1;
+ }
+
+ public void setPointAndKnot(float x, float y, float kx, float ky)
+ {
+ history[0].setLocation(x, y);
+ history[1].setLocation(kx, ky);
+ length = 2;
+ }
+}
diff --git a/src/main/java/com/kitfox/svg/pathcmd/Cubic.java b/src/main/java/com/kitfox/svg/pathcmd/Cubic.java
new file mode 100644
index 0000000..f61ec4f
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/pathcmd/Cubic.java
@@ -0,0 +1,74 @@
+/*
+ * MoveTo.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 26, 2004, 8:40 PM
+ */
+
+package com.kitfox.svg.pathcmd;
+
+//import org.apache.batik.ext.awt.geom.ExtendedGeneralPath;
+import java.awt.geom.*;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class Cubic extends PathCommand {
+
+ public float k1x = 0f;
+ public float k1y = 0f;
+ public float k2x = 0f;
+ public float k2y = 0f;
+ public float x = 0f;
+ public float y = 0f;
+
+ /** Creates a new instance of MoveTo */
+ public Cubic() {
+ }
+
+ public Cubic(boolean isRelative, float k1x, float k1y, float k2x, float k2y, float x, float y) {
+ super(isRelative);
+ this.k1x = k1x;
+ this.k1y = k1y;
+ this.k2x = k2x;
+ this.k2y = k2y;
+ this.x = x;
+ this.y = y;
+ }
+
+// public void appendPath(ExtendedGeneralPath path, BuildHistory hist)
+ public void appendPath(GeneralPath path, BuildHistory hist)
+ {
+ float offx = isRelative ? hist.history[0].x : 0f;
+ float offy = isRelative ? hist.history[0].y : 0f;
+
+ path.curveTo(k1x + offx, k1y + offy, k2x + offx, k2y + offy, x + offx, y + offy);
+ hist.setPointAndKnot(x + offx, y + offy, k2x + offx, k2y + offy);
+ }
+
+ public int getNumKnotsAdded()
+ {
+ return 6;
+ }
+}
diff --git a/src/main/java/com/kitfox/svg/pathcmd/CubicSmooth.java b/src/main/java/com/kitfox/svg/pathcmd/CubicSmooth.java
new file mode 100644
index 0000000..a54b9e4
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/pathcmd/CubicSmooth.java
@@ -0,0 +1,78 @@
+/*
+ * MoveTo.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 26, 2004, 8:40 PM
+ */
+
+package com.kitfox.svg.pathcmd;
+
+//import org.apache.batik.ext.awt.geom.ExtendedGeneralPath;
+import java.awt.geom.*;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class CubicSmooth extends PathCommand {
+
+ public float x = 0f;
+ public float y = 0f;
+ public float k2x = 0f;
+ public float k2y = 0f;
+
+ /** Creates a new instance of MoveTo */
+ public CubicSmooth() {
+ }
+
+ public CubicSmooth(boolean isRelative, float k2x, float k2y, float x, float y) {
+ super(isRelative);
+ this.k2x = k2x;
+ this.k2y = k2y;
+ this.x = x;
+ this.y = y;
+ }
+
+// public void appendPath(ExtendedGeneralPath path, BuildHistory hist)
+ public void appendPath(GeneralPath path, BuildHistory hist)
+ {
+ float offx = isRelative ? hist.history[0].x : 0f;
+ float offy = isRelative ? hist.history[0].y : 0f;
+
+ float oldKx = hist.history.length >= 2 ? hist.history[1].x : hist.history[0].x;
+ float oldKy = hist.history.length >= 2 ? hist.history[1].y : hist.history[0].y;
+ float oldX = hist.history[0].x;
+ float oldY = hist.history[0].y;
+ //Calc knot as reflection of old knot
+ float k1x = oldX * 2f - oldKx;
+ float k1y = oldY * 2f - oldKy;
+
+ path.curveTo(k1x, k1y, k2x + offx, k2y + offy, x + offx, y + offy);
+ hist.setPointAndKnot(x + offx, y + offy, k2x + offx, k2y + offy);
+ }
+
+ public int getNumKnotsAdded()
+ {
+ return 6;
+ }
+}
diff --git a/src/main/java/com/kitfox/svg/pathcmd/Horizontal.java b/src/main/java/com/kitfox/svg/pathcmd/Horizontal.java
new file mode 100644
index 0000000..5aa6c37
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/pathcmd/Horizontal.java
@@ -0,0 +1,65 @@
+/*
+ * MoveTo.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 26, 2004, 8:40 PM
+ */
+
+package com.kitfox.svg.pathcmd;
+
+//import org.apache.batik.ext.awt.geom.ExtendedGeneralPath;
+import java.awt.geom.*;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class Horizontal extends PathCommand {
+
+ public float x = 0f;
+
+ /** Creates a new instance of MoveTo */
+ public Horizontal() {
+ }
+
+ public Horizontal(boolean isRelative, float x) {
+ super(isRelative);
+ this.x = x;
+ }
+
+
+// public void appendPath(ExtendedGeneralPath path, BuildHistory hist)
+ public void appendPath(GeneralPath path, BuildHistory hist)
+ {
+ float offx = isRelative ? hist.history[0].x : 0f;
+ float offy = hist.history[0].y;
+
+ path.lineTo(x + offx, offy);
+ hist.setPoint(x + offx, offy);
+ }
+
+ public int getNumKnotsAdded()
+ {
+ return 2;
+ }
+}
diff --git a/src/main/java/com/kitfox/svg/pathcmd/LineTo.java b/src/main/java/com/kitfox/svg/pathcmd/LineTo.java
new file mode 100644
index 0000000..2c76508
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/pathcmd/LineTo.java
@@ -0,0 +1,67 @@
+/*
+ * MoveTo.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 26, 2004, 8:40 PM
+ */
+
+package com.kitfox.svg.pathcmd;
+
+//import org.apache.batik.ext.awt.geom.ExtendedGeneralPath;
+import java.awt.geom.*;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class LineTo extends PathCommand {
+
+ public float x = 0f;
+ public float y = 0f;
+
+ /** Creates a new instance of MoveTo */
+ public LineTo() {
+ }
+
+ public LineTo(boolean isRelative, float x, float y) {
+ super(isRelative);
+ this.x = x;
+ this.y = y;
+ }
+
+
+// public void appendPath(ExtendedGeneralPath path, BuildHistory hist)
+ public void appendPath(GeneralPath path, BuildHistory hist)
+ {
+ float offx = isRelative ? hist.history[0].x : 0f;
+ float offy = isRelative ? hist.history[0].y : 0f;
+
+ path.lineTo(x + offx, y + offy);
+ hist.setPoint(x + offx, y + offy);
+ }
+
+ public int getNumKnotsAdded()
+ {
+ return 2;
+ }
+}
diff --git a/src/main/java/com/kitfox/svg/pathcmd/MoveTo.java b/src/main/java/com/kitfox/svg/pathcmd/MoveTo.java
new file mode 100644
index 0000000..7339607
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/pathcmd/MoveTo.java
@@ -0,0 +1,66 @@
+/*
+ * MoveTo.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 26, 2004, 8:40 PM
+ */
+
+package com.kitfox.svg.pathcmd;
+
+//import org.apache.batik.ext.awt.geom.ExtendedGeneralPath;
+import java.awt.geom.*;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class MoveTo extends PathCommand {
+
+ public float x = 0f;
+ public float y = 0f;
+
+ /** Creates a new instance of MoveTo */
+ public MoveTo() {
+ }
+
+ public MoveTo(boolean isRelative, float x, float y) {
+ super(isRelative);
+ this.x = x;
+ this.y = y;
+ }
+
+// public void appendPath(ExtendedGeneralPath path, BuildHistory hist)
+ public void appendPath(GeneralPath path, BuildHistory hist)
+ {
+ float offx = isRelative ? hist.history[0].x : 0f;
+ float offy = isRelative ? hist.history[0].y : 0f;
+
+ path.moveTo(x + offx, y + offy);
+ hist.setPoint(x + offx, y + offy);
+ }
+
+ public int getNumKnotsAdded()
+ {
+ return 2;
+ }
+}
diff --git a/src/main/java/com/kitfox/svg/pathcmd/PathCommand.java b/src/main/java/com/kitfox/svg/pathcmd/PathCommand.java
new file mode 100644
index 0000000..33faca1
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/pathcmd/PathCommand.java
@@ -0,0 +1,56 @@
+/*
+ * PathCommand.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 26, 2004, 8:39 PM
+ */
+
+package com.kitfox.svg.pathcmd;
+
+//import org.apache.batik.ext.awt.geom.ExtendedGeneralPath;
+import java.awt.geom.*;
+
+/**
+ * This is the element of a path and contains instructions for rendering a
+ * portion of the path
+ *
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+abstract public class PathCommand {
+
+ public boolean isRelative = false;
+
+ /** Creates a new instance of PathCommand */
+ public PathCommand() {
+ }
+
+ public PathCommand(boolean isRelative) {
+ this.isRelative = isRelative;
+ }
+
+// abstract public void appendPath(ExtendedGeneralPath path, BuildHistory hist);
+ abstract public void appendPath(GeneralPath path, BuildHistory hist);
+
+ abstract public int getNumKnotsAdded();
+}
diff --git a/src/main/java/com/kitfox/svg/pathcmd/PathUtil.java b/src/main/java/com/kitfox/svg/pathcmd/PathUtil.java
new file mode 100644
index 0000000..afd562d
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/pathcmd/PathUtil.java
@@ -0,0 +1,72 @@
+/*
+ * PathUtil.java
+ *
+ * Created on May 10, 2005, 5:56 AM
+ *
+ * To change this template, choose Tools | Options and locate the template under
+ * the Source Creation and Management node. Right-click the template and choose
+ * Open. You can then make changes to the template in the Source Editor.
+ */
+
+package com.kitfox.svg.pathcmd;
+
+import java.awt.geom.*;
+
+/**
+ *
+ * @author kitfox
+ */
+public class PathUtil
+{
+
+ /** Creates a new instance of PathUtil */
+ public PathUtil()
+ {
+ }
+
+ /**
+ * Converts a GeneralPath into an SVG representation
+ */
+ public static String buildPathString(GeneralPath path)
+ {
+ float[] coords = new float[6];
+
+ StringBuffer sb = new StringBuffer();
+
+ for (PathIterator pathIt = path.getPathIterator(new AffineTransform()); !pathIt.isDone(); pathIt.next())
+ {
+ int segId = pathIt.currentSegment(coords);
+
+ switch (segId)
+ {
+ case PathIterator.SEG_CLOSE:
+ {
+ sb.append(" Z");
+ break;
+ }
+ case PathIterator.SEG_CUBICTO:
+ {
+ sb.append(" C " + coords[0] + " " + coords[1] + " " + coords[2] + " " + coords[3] + " " + coords[4] + " " + coords[5]);
+ break;
+ }
+ case PathIterator.SEG_LINETO:
+ {
+ sb.append(" L " + coords[0] + " " + coords[1]);
+ break;
+ }
+ case PathIterator.SEG_MOVETO:
+ {
+ sb.append(" M " + coords[0] + " " + coords[1]);
+ break;
+ }
+ case PathIterator.SEG_QUADTO:
+ {
+ sb.append(" Q " + coords[0] + " " + coords[1] + " " + coords[2] + " " + coords[3]);
+ break;
+ }
+ }
+ }
+
+ return sb.toString();
+ }
+}
diff --git a/src/main/java/com/kitfox/svg/pathcmd/Quadratic.java b/src/main/java/com/kitfox/svg/pathcmd/Quadratic.java
new file mode 100644
index 0000000..34ace18
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/pathcmd/Quadratic.java
@@ -0,0 +1,70 @@
+/*
+ * MoveTo.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 26, 2004, 8:40 PM
+ */
+
+package com.kitfox.svg.pathcmd;
+
+//import org.apache.batik.ext.awt.geom.ExtendedGeneralPath;
+import java.awt.geom.*;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class Quadratic extends PathCommand {
+
+ public float kx = 0f;
+ public float ky = 0f;
+ public float x = 0f;
+ public float y = 0f;
+
+ /** Creates a new instance of MoveTo */
+ public Quadratic() {
+ }
+
+ public Quadratic(boolean isRelative, float kx, float ky, float x, float y) {
+ super(isRelative);
+ this.kx = kx;
+ this.ky = ky;
+ this.x = x;
+ this.y = y;
+ }
+
+// public void appendPath(ExtendedGeneralPath path, BuildHistory hist)
+ public void appendPath(GeneralPath path, BuildHistory hist)
+ {
+ float offx = isRelative ? hist.history[0].x : 0f;
+ float offy = isRelative ? hist.history[0].y : 0f;
+
+ path.quadTo(kx + offx, ky + offy, x + offx, y + offy);
+ hist.setPointAndKnot(x + offx, y + offy, kx + offx, ky + offy);
+ }
+
+ public int getNumKnotsAdded()
+ {
+ return 4;
+ }
+}
diff --git a/src/main/java/com/kitfox/svg/pathcmd/QuadraticSmooth.java b/src/main/java/com/kitfox/svg/pathcmd/QuadraticSmooth.java
new file mode 100644
index 0000000..2bbc6bf
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/pathcmd/QuadraticSmooth.java
@@ -0,0 +1,74 @@
+/*
+ * MoveTo.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 26, 2004, 8:40 PM
+ */
+
+package com.kitfox.svg.pathcmd;
+
+//import org.apache.batik.ext.awt.geom.ExtendedGeneralPath;
+import java.awt.geom.*;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class QuadraticSmooth extends PathCommand {
+
+ public float x = 0f;
+ public float y = 0f;
+
+ /** Creates a new instance of MoveTo */
+ public QuadraticSmooth() {
+ }
+
+ public QuadraticSmooth(boolean isRelative, float x, float y) {
+ super(isRelative);
+ this.x = x;
+ this.y = y;
+ }
+
+// public void appendPath(ExtendedGeneralPath path, BuildHistory hist)
+ public void appendPath(GeneralPath path, BuildHistory hist)
+ {
+ float offx = isRelative ? hist.history[0].x : 0f;
+ float offy = isRelative ? hist.history[0].y : 0f;
+
+ float oldKx = hist.history.length >= 2 ? hist.history[1].x : hist.history[0].x;
+ float oldKy = hist.history.length >= 2 ? hist.history[1].y : hist.history[0].y;
+ float oldX = hist.history[0].x;
+ float oldY = hist.history[0].y;
+ //Calc knot as reflection of old knot
+ float kx = oldX * 2f - oldKx;
+ float ky = oldY * 2f - oldKy;
+
+ path.quadTo(kx, ky, x + offx, y + offy);
+ hist.setPointAndKnot(x + offx, y + offy, kx, ky);
+ }
+
+ public int getNumKnotsAdded()
+ {
+ return 4;
+ }
+}
diff --git a/src/main/java/com/kitfox/svg/pathcmd/Terminal.java b/src/main/java/com/kitfox/svg/pathcmd/Terminal.java
new file mode 100644
index 0000000..a9d1c9f
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/pathcmd/Terminal.java
@@ -0,0 +1,56 @@
+/*
+ * MoveTo.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 26, 2004, 8:40 PM
+ */
+
+package com.kitfox.svg.pathcmd;
+
+//import org.apache.batik.ext.awt.geom.ExtendedGeneralPath;
+import java.awt.geom.*;
+
+/**
+ * Finishes a path
+ *
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class Terminal extends PathCommand {
+
+ /** Creates a new instance of MoveTo */
+ public Terminal() {
+ }
+
+
+// public void appendPath(ExtendedGeneralPath path, BuildHistory hist)
+ public void appendPath(GeneralPath path, BuildHistory hist)
+ {
+ path.closePath();
+ }
+
+ public int getNumKnotsAdded()
+ {
+ return 0;
+ }
+}
diff --git a/src/main/java/com/kitfox/svg/pathcmd/Vertical.java b/src/main/java/com/kitfox/svg/pathcmd/Vertical.java
new file mode 100644
index 0000000..ca7bda4
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/pathcmd/Vertical.java
@@ -0,0 +1,64 @@
+/*
+ * MoveTo.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 26, 2004, 8:40 PM
+ */
+
+package com.kitfox.svg.pathcmd;
+
+//import org.apache.batik.ext.awt.geom.ExtendedGeneralPath;
+import java.awt.geom.*;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class Vertical extends PathCommand {
+
+ public float y = 0f;
+
+ /** Creates a new instance of MoveTo */
+ public Vertical() {
+ }
+
+ public Vertical(boolean isRelative, float y) {
+ super(isRelative);
+ this.y = y;
+ }
+
+// public void appendPath(ExtendedGeneralPath path, BuildHistory hist)
+ public void appendPath(GeneralPath path, BuildHistory hist)
+ {
+ float offx = hist.history[0].x;
+ float offy = isRelative ? hist.history[0].y : 0f;
+
+ path.lineTo(offx, y + offy);
+ hist.setPoint(offx, y + offy);
+ }
+
+ public int getNumKnotsAdded()
+ {
+ return 2;
+ }
+}
diff --git a/src/main/java/com/kitfox/svg/pattern/PatternPaint.java b/src/main/java/com/kitfox/svg/pattern/PatternPaint.java
new file mode 100644
index 0000000..30864a9
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/pattern/PatternPaint.java
@@ -0,0 +1,60 @@
+/*
+ * PatternPaint.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 1, 2004, 3:37 AM
+ */
+
+package com.kitfox.svg.pattern;
+
+import java.awt.*;
+import java.awt.geom.*;
+import java.awt.image.*;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class PatternPaint implements Paint
+{
+ BufferedImage source; //Image we're rendering from
+ AffineTransform xform;
+
+ /** Creates a new instance of PatternPaint */
+ public PatternPaint(BufferedImage source, AffineTransform xform)
+ {
+ this.source = source;
+ this.xform = xform;
+ }
+
+ public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds, AffineTransform xform, RenderingHints hints)
+ {
+ return new PatternPaintContext(source, deviceBounds, xform, this.xform);
+ }
+
+ public int getTransparency()
+ {
+ return source.getColorModel().getTransparency();
+ }
+
+}
diff --git a/src/main/java/com/kitfox/svg/pattern/PatternPaintContext.java b/src/main/java/com/kitfox/svg/pattern/PatternPaintContext.java
new file mode 100644
index 0000000..d7e4c2e
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/pattern/PatternPaintContext.java
@@ -0,0 +1,120 @@
+/*
+ * PatternPaintContext.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 1, 2004, 3:37 AM
+ */
+
+package com.kitfox.svg.pattern;
+
+import java.awt.*;
+import java.awt.geom.*;
+import java.awt.image.*;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class PatternPaintContext implements PaintContext
+{
+ BufferedImage source; //Image we're rendering from
+ Rectangle deviceBounds; //int size of rectangle we're rendering to
+// AffineTransform userXform; //xform from user space to device space
+// AffineTransform distortXform; //distortion applied to this pattern
+
+ AffineTransform xform; //distortion applied to this pattern
+
+ int sourceWidth;
+ int sourceHeight;
+
+ //Raster we use to build tile
+ BufferedImage buf;
+
+ /** Creates a new instance of PatternPaintContext */
+ public PatternPaintContext(BufferedImage source, Rectangle deviceBounds, AffineTransform userXform, AffineTransform distortXform)
+ {
+//System.err.println("Bounds " + deviceBounds);
+ this.source = source;
+ this.deviceBounds = deviceBounds;
+ try {
+// this.distortXform = distortXform.createInverse();
+// this.userXform = userXform.createInverse();
+
+// xform = userXform.createInverse();
+// xform.concatenate(distortXform.createInverse());
+ xform = distortXform.createInverse();
+ xform.concatenate(userXform.createInverse());
+ }
+ catch (Exception e) { e.printStackTrace(); }
+
+ sourceWidth = source.getWidth();
+ sourceHeight = source.getHeight();
+ }
+
+ public void dispose() {
+ }
+
+ public ColorModel getColorModel() {
+ return source.getColorModel();
+ }
+
+ public Raster getRaster(int x, int y, int w, int h)
+ {
+//System.err.println("" + x + ", " + y + ", " + w + ", " + h);
+ if (buf == null || buf.getWidth() != w || buf.getHeight() != buf.getHeight())
+ {
+ buf = new BufferedImage(w, h, source.getType());
+ }
+
+// Point2D.Float srcPt = new Point2D.Float(), srcPt2 = new Point2D.Float(), destPt = new Point2D.Float();
+ Point2D.Float srcPt = new Point2D.Float(), destPt = new Point2D.Float();
+ for (int j = 0; j < h; j++)
+ {
+ for (int i = 0; i < w; i++)
+ {
+ destPt.setLocation(i + x, j + y);
+
+ xform.transform(destPt, srcPt);
+
+// userXform.transform(destPt, srcPt2);
+// distortXform.transform(srcPt2, srcPt);
+
+ int ii = ((int)srcPt.x) % sourceWidth;
+ if (ii < 0) ii += sourceWidth;
+ int jj = ((int)srcPt.y) % sourceHeight;
+ if (jj < 0) jj += sourceHeight;
+
+ buf.setRGB(i, j, source.getRGB(ii, jj));
+ }
+ }
+
+ return buf.getData();
+ }
+
+ public static void main(String[] argv)
+ {
+ int i = -4;
+ System.err.println("Hello " + (i % 4));
+ }
+
+}
diff --git a/src/main/java/com/kitfox/svg/xml/ColorTable.java b/src/main/java/com/kitfox/svg/xml/ColorTable.java
new file mode 100644
index 0000000..f3db3b2
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/xml/ColorTable.java
@@ -0,0 +1,273 @@
+/*
+ * ColorTable.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 26, 2004, 4:34 AM
+ */
+
+package com.kitfox.svg.xml;
+
+import java.awt.*;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class ColorTable
+{
+
+ static final Map colorTable;
+ static {
+ HashMap table = new HashMap();
+ table.put("aliceblue", new Color(0xf0f8ff));
+ table.put("antiquewhite", new Color(0xfaebd7));
+ table.put("aqua", new Color(0x00ffff));
+ table.put("aquamarine", new Color(0x7fffd4));
+ table.put("azure", new Color(0xf0ffff));
+ table.put("beige", new Color(0xf5f5dc));
+ table.put("bisque", new Color(0xffe4c4));
+ table.put("black", new Color(0x000000));
+ table.put("blanchedalmond", new Color(0xffebcd));
+ table.put("blue", new Color(0x0000ff));
+ table.put("blueviolet", new Color(0x8a2be2));
+ table.put("brown", new Color(0xa52a2a));
+ table.put("burlywood", new Color(0xdeb887));
+ table.put("cadetblue", new Color(0x5f9ea0));
+ table.put("chartreuse", new Color(0x7fff00));
+ table.put("chocolate", new Color(0xd2691e));
+ table.put("coral", new Color(0xff7f50));
+ table.put("cornflowerblue", new Color(0x6495ed));
+ table.put("cornsilk", new Color(0xfff8dc));
+ table.put("crimson", new Color(0xdc143c));
+ table.put("cyan", new Color(0x00ffff));
+ table.put("darkblue", new Color(0x00008b));
+ table.put("darkcyan", new Color(0x008b8b));
+ table.put("darkgoldenrod", new Color(0xb8860b));
+ table.put("darkgray", new Color(0xa9a9a9));
+ table.put("darkgreen", new Color(0x006400));
+ table.put("darkkhaki", new Color(0xbdb76b));
+ table.put("darkmagenta", new Color(0x8b008b));
+ table.put("darkolivegreen", new Color(0x556b2f));
+ table.put("darkorange", new Color(0xff8c00));
+ table.put("darkorchid", new Color(0x9932cc));
+ table.put("darkred", new Color(0x8b0000));
+ table.put("darksalmon", new Color(0xe9967a));
+ table.put("darkseagreen", new Color(0x8fbc8f));
+ table.put("darkslateblue", new Color(0x483d8b));
+ table.put("darkslategray", new Color(0x2f4f4f));
+ table.put("darkturquoise", new Color(0x00ced1));
+ table.put("darkviolet", new Color(0x9400d3));
+ table.put("deeppink", new Color(0xff1493));
+ table.put("deepskyblue", new Color(0x00bfff));
+ table.put("dimgray", new Color(0x696969));
+ table.put("dodgerblue", new Color(0x1e90ff));
+ table.put("feldspar", new Color(0xd19275));
+ table.put("firebrick", new Color(0xb22222));
+ table.put("floralwhite", new Color(0xfffaf0));
+ table.put("forestgreen", new Color(0x228b22));
+ table.put("fuchsia", new Color(0xff00ff));
+ table.put("gainsboro", new Color(0xdcdcdc));
+ table.put("ghostwhite", new Color(0xf8f8ff));
+ table.put("gold", new Color(0xffd700));
+ table.put("goldenrod", new Color(0xdaa520));
+ table.put("gray", new Color(0x808080));
+ table.put("green", new Color(0x008000));
+ table.put("greenyellow", new Color(0xadff2f));
+ table.put("honeydew", new Color(0xf0fff0));
+ table.put("hotpink", new Color(0xff69b4));
+ table.put("indianred", new Color(0xcd5c5c));
+ table.put("indigo", new Color(0x4b0082));
+ table.put("ivory", new Color(0xfffff0));
+ table.put("khaki", new Color(0xf0e68c));
+ table.put("lavender", new Color(0xe6e6fa));
+ table.put("lavenderblush", new Color(0xfff0f5));
+ table.put("lawngreen", new Color(0x7cfc00));
+ table.put("lemonchiffon", new Color(0xfffacd));
+ table.put("lightblue", new Color(0xadd8e6));
+ table.put("lightcoral", new Color(0xf08080));
+ table.put("lightcyan", new Color(0xe0ffff));
+ table.put("lightgoldenrodyellow", new Color(0xfafad2));
+ table.put("lightgrey", new Color(0xd3d3d3));
+ table.put("lightgreen", new Color(0x90ee90));
+ table.put("lightpink", new Color(0xffb6c1));
+ table.put("lightsalmon", new Color(0xffa07a));
+ table.put("lightseagreen", new Color(0x20b2aa));
+ table.put("lightskyblue", new Color(0x87cefa));
+ table.put("lightslateblue", new Color(0x8470ff));
+ table.put("lightslategray", new Color(0x778899));
+ table.put("lightsteelblue", new Color(0xb0c4de));
+ table.put("lightyellow", new Color(0xffffe0));
+ table.put("lime", new Color(0x00ff00));
+ table.put("limegreen", new Color(0x32cd32));
+ table.put("linen", new Color(0xfaf0e6));
+ table.put("magenta", new Color(0xff00ff));
+ table.put("maroon", new Color(0x800000));
+ table.put("mediumaquamarine", new Color(0x66cdaa));
+ table.put("mediumblue", new Color(0x0000cd));
+ table.put("mediumorchid", new Color(0xba55d3));
+ table.put("mediumpurple", new Color(0x9370d8));
+ table.put("mediumseagreen", new Color(0x3cb371));
+ table.put("mediumslateblue", new Color(0x7b68ee));
+ table.put("mediumspringgreen", new Color(0x00fa9a));
+ table.put("mediumturquoise", new Color(0x48d1cc));
+ table.put("mediumvioletred", new Color(0xc71585));
+ table.put("midnightblue", new Color(0x191970));
+ table.put("mintcream", new Color(0xf5fffa));
+ table.put("mistyrose", new Color(0xffe4e1));
+ table.put("moccasin", new Color(0xffe4b5));
+ table.put("navajowhite", new Color(0xffdead));
+ table.put("navy", new Color(0x000080));
+ table.put("oldlace", new Color(0xfdf5e6));
+ table.put("olive", new Color(0x808000));
+ table.put("olivedrab", new Color(0x6b8e23));
+ table.put("orange", new Color(0xffa500));
+ table.put("orangered", new Color(0xff4500));
+ table.put("orchid", new Color(0xda70d6));
+ table.put("palegoldenrod", new Color(0xeee8aa));
+ table.put("palegreen", new Color(0x98fb98));
+ table.put("paleturquoise", new Color(0xafeeee));
+ table.put("palevioletred", new Color(0xd87093));
+ table.put("papayawhip", new Color(0xffefd5));
+ table.put("peachpuff", new Color(0xffdab9));
+ table.put("peru", new Color(0xcd853f));
+ table.put("pink", new Color(0xffc0cb));
+ table.put("plum", new Color(0xdda0dd));
+ table.put("powderblue", new Color(0xb0e0e6));
+ table.put("purple", new Color(0x800080));
+ table.put("red", new Color(0xff0000));
+ table.put("rosybrown", new Color(0xbc8f8f));
+ table.put("royalblue", new Color(0x4169e1));
+ table.put("saddlebrown", new Color(0x8b4513));
+ table.put("salmon", new Color(0xfa8072));
+ table.put("sandybrown", new Color(0xf4a460));
+ table.put("seagreen", new Color(0x2e8b57));
+ table.put("seashell", new Color(0xfff5ee));
+ table.put("sienna", new Color(0xa0522d));
+ table.put("silver", new Color(0xc0c0c0));
+ table.put("skyblue", new Color(0x87ceeb));
+ table.put("slateblue", new Color(0x6a5acd));
+ table.put("slategray", new Color(0x708090));
+ table.put("snow", new Color(0xfffafa));
+ table.put("springgreen", new Color(0x00ff7f));
+ table.put("steelblue", new Color(0x4682b4));
+ table.put("tan", new Color(0xd2b48c));
+ table.put("teal", new Color(0x008080));
+ table.put("thistle", new Color(0xd8bfd8));
+ table.put("tomato", new Color(0xff6347));
+ table.put("turquoise", new Color(0x40e0d0));
+ table.put("violet", new Color(0xee82ee));
+ table.put("violetred", new Color(0xd02090));
+ table.put("wheat", new Color(0xf5deb3));
+ table.put("white", new Color(0xffffff));
+ table.put("whitesmoke", new Color(0xf5f5f5));
+ table.put("yellow", new Color(0xffff00));
+ table.put("yellowgreen", new Color(0x9acd32));
+
+ colorTable = Collections.unmodifiableMap(table);
+ }
+
+ static ColorTable singleton = new ColorTable();
+
+ /** Creates a new instance of ColorTable */
+ protected ColorTable() {
+// buildColorList();
+ }
+
+ static public ColorTable instance() { return singleton; }
+
+ public Color lookupColor(String name) {
+ Object obj = colorTable.get(name.toLowerCase());
+ if (obj == null) return null;
+
+ return (Color)obj;
+ }
+
+ 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;
+ }
+
+ 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;
+ }
+
+}
diff --git a/src/main/java/com/kitfox/svg/xml/NumberWithUnits.java b/src/main/java/com/kitfox/svg/xml/NumberWithUnits.java
new file mode 100644
index 0000000..77bb895
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/xml/NumberWithUnits.java
@@ -0,0 +1,89 @@
+/*
+ * NumberWithUnits.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 February 18, 2004, 2:43 PM
+ */
+
+package com.kitfox.svg.xml;
+
+import java.io.Serializable;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class NumberWithUnits implements Serializable
+{
+ public static final long serialVersionUID = 0;
+
+ public static final int UT_UNITLESS = 0;
+ public static final int UT_PX = 1; //Pixels
+ public static final int UT_CM = 2; //Centimeters
+ public static final int UT_MM = 3; //Millimeters
+ public static final int UT_IN = 4; //Inches
+ public static final int UT_EM = 5; //Default font height
+ public static final int UT_EX = 6; //Height of character 'x' in default font
+ public static final int UT_PT = 7; //Points - 1/72 of an inch
+ public static final int UT_PC = 8; //Picas - 1/6 of an inch
+ public static final int UT_PERCENT = 9; //Percent - relative width
+
+ float value = 0f;
+ int unitType = UT_UNITLESS;
+
+ /** Creates a new instance of NumberWithUnits */
+ public NumberWithUnits()
+ {
+ }
+
+ public NumberWithUnits(String value)
+ {
+ set(value);
+ }
+
+ public NumberWithUnits(float value, int unitType)
+ {
+ this.value = value;
+ this.unitType = unitType;
+ }
+
+ public float getValue() { return value; }
+ public int getUnits() { return unitType; }
+
+ public void set(String value)
+ {
+ this.value = XMLParseUtil.findFloat(value);
+ unitType = UT_UNITLESS;
+
+ if (value.indexOf("px") != -1) { unitType = UT_PX; return; }
+ if (value.indexOf("cm") != -1) { unitType = UT_CM; return; }
+ if (value.indexOf("mm") != -1) { unitType = UT_MM; return; }
+ if (value.indexOf("in") != -1) { unitType = UT_IN; return; }
+ if (value.indexOf("em") != -1) { unitType = UT_EM; return; }
+ if (value.indexOf("ex") != -1) { unitType = UT_EX; return; }
+ if (value.indexOf("pt") != -1) { unitType = UT_PT; return; }
+ if (value.indexOf("pc") != -1) { unitType = UT_PC; return; }
+ if (value.indexOf("%") != -1) { unitType = UT_PERCENT; return; }
+ }
+
+}
diff --git a/src/main/java/com/kitfox/svg/xml/ReadableXMLElement.java b/src/main/java/com/kitfox/svg/xml/ReadableXMLElement.java
new file mode 100644
index 0000000..c7416ba
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/xml/ReadableXMLElement.java
@@ -0,0 +1,48 @@
+/*
+ * LoadableObject.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 1, 2003, 1:46 AM
+ */
+
+package com.kitfox.svg.xml;
+
+import org.w3c.dom.*;
+import java.net.*;
+import java.util.*;
+import java.lang.reflect.*;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public interface ReadableXMLElement {
+
+ /**
+ * Initializes this element from the passed DOM tree.
+ * @param root - DOM tree to build from
+ * @param docRoot - URL of the document this DOM tree was created from
+ */
+ public void read(Element root, URL docRoot);
+
+}
diff --git a/src/main/java/com/kitfox/svg/xml/StyleAttribute.java b/src/main/java/com/kitfox/svg/xml/StyleAttribute.java
new file mode 100644
index 0000000..70cf71a
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/xml/StyleAttribute.java
@@ -0,0 +1,290 @@
+/*
+ * StyleAttribute.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 27, 2004, 2:53 PM
+ */
+
+package com.kitfox.svg.xml;
+
+import java.awt.*;
+import java.net.*;
+import java.io.*;
+import java.util.regex.*;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class StyleAttribute implements Serializable
+{
+ public static final long serialVersionUID = 0;
+
+ static final Matcher matchUrl = Pattern.compile("\\s*url\\((.*)\\)\\s*").matcher("");
+ static final Matcher matchFpNumUnits = Pattern.compile("\\s*([-+]?((\\d*\\.\\d+)|(\\d+))([-+]?[eE]\\d+)?)\\s*(px|cm|mm|in|pc|pt|em|ex)\\s*").matcher("");
+
+ String name;
+ String stringValue;
+
+ boolean colorCompatable = false;
+ boolean urlCompatable = false;
+
+ /** Creates a new instance of StyleAttribute */
+ public StyleAttribute()
+ {
+ this(null, null);
+ }
+
+ public StyleAttribute(String name)
+ {
+ this.name = name;
+ stringValue = null;
+ }
+
+ public StyleAttribute(String name, String stringValue)
+ {
+ this.name = name;
+ this.stringValue = stringValue;
+ }
+
+ public String getName() { return name; }
+ public StyleAttribute setName(String name) { this.name = name; return this; }
+
+ public String getStringValue() { return stringValue; }
+
+ public String[] getStringList()
+ {
+ return XMLParseUtil.parseStringList(stringValue);
+ }
+
+ public void setStringValue(String value)
+ {
+ stringValue = value;
+ }
+
+ public boolean getBooleanValue() {
+ return stringValue.toLowerCase().equals("true");
+ }
+
+ public int getIntValue() {
+ return XMLParseUtil.findInt(stringValue);
+ }
+
+ public int[] getIntList() {
+ return XMLParseUtil.parseIntList(stringValue);
+ }
+
+ public double getDoubleValue() {
+ return XMLParseUtil.findDouble(stringValue);
+ }
+
+ public double[] getDoubleList() {
+ return XMLParseUtil.parseDoubleList(stringValue);
+ }
+
+ public float getFloatValue() {
+ return XMLParseUtil.findFloat(stringValue);
+ }
+
+ public float[] getFloatList() {
+ return XMLParseUtil.parseFloatList(stringValue);
+ }
+
+ public float getRatioValue() {
+ return (float)XMLParseUtil.parseRatio(stringValue);
+// try { return Float.parseFloat(stringValue); }
+// catch (Exception e) {}
+// return 0f;
+ }
+
+ public String getUnits() {
+ matchFpNumUnits.reset(stringValue);
+ if (!matchFpNumUnits.matches()) return null;
+ return matchFpNumUnits.group(6);
+ }
+
+ public NumberWithUnits getNumberWithUnits() {
+ return XMLParseUtil.parseNumberWithUnits(stringValue);
+ }
+
+ public float getFloatValueWithUnits()
+ {
+ NumberWithUnits number = getNumberWithUnits();
+ return convertUnitsToPixels(number.getUnits(), number.getValue());
+ }
+
+ static public float convertUnitsToPixels(int unitType, float value)
+ {
+ if (unitType == NumberWithUnits.UT_UNITLESS || unitType == NumberWithUnits.UT_PERCENT)
+ {
+ return value;
+ }
+
+ float pixPerInch;
+ try
+ {
+ pixPerInch = (float)Toolkit.getDefaultToolkit().getScreenResolution();
+ }
+ catch (HeadlessException ex)
+ {
+ //default to 72 dpi
+ pixPerInch = 72;
+ }
+ final float inchesPerCm = .3936f;
+
+ switch (unitType)
+ {
+ case NumberWithUnits.UT_IN:
+ return value * pixPerInch;
+ case NumberWithUnits.UT_CM:
+ return value * inchesPerCm * pixPerInch;
+ case NumberWithUnits.UT_MM:
+ return value * .1f * inchesPerCm * pixPerInch;
+ case NumberWithUnits.UT_PT:
+ return value * (1f / 72f) * pixPerInch;
+ case NumberWithUnits.UT_PC:
+ return value * (1f / 6f) * pixPerInch;
+ }
+
+ return value;
+ }
+
+ public Color getColorValue()
+ {
+ return ColorTable.parseColor(stringValue);
+ }
+
+ public String parseURLFn()
+ {
+ matchUrl.reset(stringValue);
+ if (!matchUrl.matches()) return null;
+ return matchUrl.group(1);
+ }
+
+ public URL getURLValue(URL docRoot)
+ {
+ String fragment = parseURLFn();
+ if (fragment == null) return null;
+ try {
+ return new URL(docRoot, fragment);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public URL getURLValue(URI docRoot)
+ {
+ String fragment = parseURLFn();
+ if (fragment == null) return null;
+ try {
+ URI ref = docRoot.resolve(fragment);
+ return ref.toURL();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public URI getURIValue()
+ {
+ return getURIValue(null);
+ }
+
+ /**
+ * Parse this sytle attribute as a URL and return it in URI form resolved
+ * against the passed base.
+ *
+ * @param base - URI to resolve against. If null, will return value without
+ * attempting to resolve it.
+ */
+ public URI getURIValue(URI base)
+ {
+ try {
+ String fragment = parseURLFn();
+ if (fragment == null) fragment = stringValue;
+ if (fragment == null) return null;
+
+ //======================
+ //This gets around a bug in the 1.5.0 JDK
+ if (Pattern.matches("[a-zA-Z]:!\\\\.*", fragment))
+ {
+ File file = new File(fragment);
+ return file.toURI();
+ }
+ //======================
+
+ //[scheme:]scheme-specific-part[#fragment]
+
+ URI uriFrag = new URI(fragment);
+ if (uriFrag.isAbsolute())
+ {
+ //Has scheme
+ return uriFrag;
+ }
+
+ if (base == null) return uriFrag;
+
+ URI relBase = new URI(null, base.getSchemeSpecificPart(), null);
+ URI relUri;
+ if (relBase.isOpaque())
+ {
+ relUri = new URI(null, base.getSchemeSpecificPart(), uriFrag.getFragment());
+ }
+ else
+ {
+ relUri = relBase.resolve(uriFrag);
+ }
+ return new URI(base.getScheme() + ":" + relUri);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ try
+ {
+ URI uri = new URI("jar:http://www.kitfox.com/jackal/jackal.jar!/res/doc/about.svg");
+ uri = uri.resolve("#myFragment");
+
+ System.err.println(uri.toString());
+
+ uri = new URI("http://www.kitfox.com/jackal/jackal.html");
+ uri = uri.resolve("#myFragment");
+
+ System.err.println(uri.toString());
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/com/kitfox/svg/xml/WritableXMLElement.java b/src/main/java/com/kitfox/svg/xml/WritableXMLElement.java
new file mode 100644
index 0000000..09bf00e
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/xml/WritableXMLElement.java
@@ -0,0 +1,48 @@
+/*
+ * LoadableObject.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 1, 2003, 1:46 AM
+ */
+
+package com.kitfox.svg.xml;
+
+import org.w3c.dom.*;
+import java.net.*;
+import java.util.*;
+import java.lang.reflect.*;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public interface WritableXMLElement {
+
+ /**
+ * Initializes this element from the passed DOM tree.
+ * @param root - DOM tree to build from
+ * @param docRoot - URL of the document this DOM tree was created from
+ */
+// public void write(Element root, URL docRoot);
+
+}
diff --git a/src/main/java/com/kitfox/svg/xml/XMLParseUtil.java b/src/main/java/com/kitfox/svg/xml/XMLParseUtil.java
new file mode 100644
index 0000000..b9cbec0
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/xml/XMLParseUtil.java
@@ -0,0 +1,806 @@
+/*
+ * XMLParseUtil.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 February 18, 2004, 1:49 PM
+ */
+
+package com.kitfox.svg.xml;
+
+import org.w3c.dom.*;
+import java.awt.*;
+import java.net.*;
+import java.util.*;
+import java.util.regex.*;
+import java.lang.reflect.*;
+
+/**
+ * @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)?").matcher("");
+ static final Matcher intMatch = Pattern.compile("[-+]?\\d+").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)
+ {
+ System.err.println("XMLParseUtil: regex parse problem: '" + val + "'");
+ e.printStackTrace();
+ }
+
+ val = fpMatch.group(1);
+ //System.err.println("Parsing " + val);
+
+ double retVal = 0;
+ try
+ {
+ retVal = Double.parseDouble(val);
+
+ float pixPerInch = (float)Toolkit.getDefaultToolkit().getScreenResolution();
+ 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);
+ if (fpMatch.group(6).equals("%")) 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);
+
+ URL url;
+ 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) { e.printStackTrace(); 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();
+
+/*
+ Class[] params = {Element.class, URL.class};
+ Method loadMethod = null;
+ try { loadMethod = classType.getMethod("load", params); }
+ catch (Exception e) { e.printStackTrace(); 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) { e.printStackTrace(); continue; }
+ newObj.read(ele, docRoot);
+/*
+ Object[] args = {ele, source};
+ Object obj = null;
+ try { obj = loadMethod.invoke(null, args); }
+ catch (Exception e) { e.printStackTrace(); }
+
+ */
+ 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();
+
+ /*
+ Class[] params = {Element.class, URL.class};
+ Method loadMethod = null;
+ try { loadMethod = classType.getMethod("load", params); }
+ catch (Exception e) { e.printStackTrace(); 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) { e.printStackTrace(); continue; }
+ newObj.read(ele, docRoot);
+ /*
+ Object[] args = {ele, source};
+ Object obj = null;
+ try { obj = loadMethod.invoke(null, args); }
+ catch (Exception e) { e.printStackTrace(); }
+ */
+
+ 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) { e.printStackTrace(); 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++] = ((Integer)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++] = (String)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 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;"
+ * @param map - A map to which these styles will be added
+ */
+ public static HashMap parseStyle(String styleString, HashMap map) {
+ final Pattern patSemi = Pattern.compile(";");
+ final Pattern patColonSpace = Pattern.compile(":");
+
+ //Strips left and right whitespace
+ final Matcher matcherContent = Pattern.compile("\\s*([^\\s](.*[^\\s])?)\\s*").matcher("");
+
+ String[] styles = patSemi.split(styleString);
+
+ for (int i = 0; i < styles.length; i++) {
+ String[] vals = patColonSpace.split(styles[i]);
+
+ matcherContent.reset(vals[0]);
+ matcherContent.matches();
+ vals[0] = matcherContent.group(1);
+
+ matcherContent.reset(vals[1]);
+ matcherContent.matches();
+ vals[1] = matcherContent.group(1);
+
+ map.put(vals[0], new StyleAttribute(vals[0], vals[1]));
+ }
+
+ return map;
+ }
+}
diff --git a/src/main/java/com/kitfox/svg/xml/cpx/CPXConsts.java b/src/main/java/com/kitfox/svg/xml/cpx/CPXConsts.java
new file mode 100644
index 0000000..6cbcaba
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/xml/cpx/CPXConsts.java
@@ -0,0 +1,40 @@
+/*
+ * CPXConst.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 February 12, 2004, 12:51 PM
+ */
+
+package com.kitfox.svg.xml.cpx;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public interface CPXConsts {
+
+ static final byte[] MAGIC_NUMBER = {'C', 'P', 'X', 0};
+
+ static final int XL_PLAIN = 0;
+ static final int XL_ZIP_CRYPT = 1;
+}
diff --git a/src/main/java/com/kitfox/svg/xml/cpx/CPXInputStream.java b/src/main/java/com/kitfox/svg/xml/cpx/CPXInputStream.java
new file mode 100644
index 0000000..1e6ee36
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/xml/cpx/CPXInputStream.java
@@ -0,0 +1,293 @@
+/*
+ * CPXInputStream.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 February 12, 2004, 10:34 AM
+ */
+
+package com.kitfox.svg.xml.cpx;
+
+import java.io.*;
+import java.util.*;
+import java.util.zip.*;
+import java.security.*;
+import javax.crypto.*;
+
+/**
+ * This class reads/decodes the CPX file format. This format is a simple
+ * compression/encryption transformer for XML data. This stream takes in
+ * encrypted XML and outputs decrypted. It does this by checking for a magic
+ * number at the start of the stream. If absent, it treats the stream as
+ * raw XML data and passes it through unaltered. This is to aid development
+ * in debugging versions, where the XML files will not be in CPX format.
+ *
+ * See http://java.sun.com/developer/technicalArticles/Security/Crypto/
+ *
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class CPXInputStream extends FilterInputStream implements CPXConsts {
+
+
+ SecureRandom sec = new SecureRandom();
+
+ Inflater inflater = new Inflater();
+
+ int xlateMode;
+
+ //Keep header bytes in case this stream turns out to be plain text
+ byte[] head = new byte[4];
+ int headSize = 0;
+ int headPtr = 0;
+
+ boolean reachedEOF = false;
+ byte[] inBuffer = new byte[2048];
+ byte[] decryptBuffer = new byte[2048];
+
+ /** Creates a new instance of CPXInputStream */
+ public CPXInputStream(InputStream in) throws IOException {
+ super(in);
+
+ //Determine processing type
+ for (int i = 0; i < 4; i++)
+ {
+ int val = in.read();
+ head[i] = (byte)val;
+ if (val == -1 || head[i] != MAGIC_NUMBER[i])
+ {
+ headSize = i + 1;
+ xlateMode = XL_PLAIN;
+ return;
+ }
+ }
+
+ xlateMode = XL_ZIP_CRYPT;
+ }
+
+ /**
+ * We do not allow marking
+ */
+ public boolean markSupported() { return false; }
+
+ /**
+ * Closes this input stream and releases any system resources
+ * associated with the stream.
+ * This
+ * method simply performs in.close()
.
+ *
+ * @exception IOException if an I/O error occurs.
+ * @see java.io.FilterInputStream#in
+ */
+ public void close() throws IOException {
+ reachedEOF = true;
+ in.close();
+ }
+
+ /**
+ * Reads the next byte of data from this input stream. The value
+ * byte is returned as an int
in the range
+ * 0
to 255
. If no byte is available
+ * because the end of the stream has been reached, the value
+ * -1
is returned. This method blocks until input data
+ * is available, the end of the stream is detected, or an exception
+ * is thrown.
+ * in.read()
and returns the result.
+ *
+ * @return the next byte of data, or -1
if the end of the
+ * stream is reached.
+ * @exception IOException if an I/O error occurs.
+ * @see java.io.FilterInputStream#in
+ */
+ public int read() throws IOException
+ {
+ final byte[] b = new byte[1];
+ int retVal = read(b, 0, 1);
+ if (retVal == -1) return -1;
+ return b[0];
+ }
+
+ /**
+ * Reads up to byte.length
bytes of data from this
+ * input stream into an array of bytes. This method blocks until some
+ * input is available.
+ * read(b, 0, b.length)
and returns
+ * the result. It is important that it does
+ * not do in.read(b)
instead;
+ * certain subclasses of FilterInputStream
+ * depend on the implementation strategy actually
+ * used.
+ *
+ * @param b the buffer into which the data is read.
+ * @return the total number of bytes read into the buffer, or
+ * -1
if there is no more data because the end of
+ * the stream has been reached.
+ * @exception IOException if an I/O error occurs.
+ * @see java.io.FilterInputStream#read(byte[], int, int)
+ */
+ public int read(byte[] b) throws IOException
+ {
+ return read(b, 0, b.length);
+ }
+
+ /**
+ * Reads up to len
bytes of data from this input stream
+ * into an array of bytes. This method blocks until some input is
+ * available.
+ * in.read(b, off, len)
+ * and returns the result.
+ *
+ * @param b the buffer into which the data is read.
+ * @param off the start offset of the data.
+ * @param len the maximum number of bytes read.
+ * @return the total number of bytes read into the buffer, or
+ * -1
if there is no more data because the end of
+ * the stream has been reached.
+ * @exception IOException if an I/O error occurs.
+ * @see java.io.FilterInputStream#in
+ */
+ public int read(byte[] b, int off, int len) throws IOException
+ {
+ if (reachedEOF) return -1;
+
+ if (xlateMode == XL_PLAIN)
+ {
+ int count = 0;
+ //Write header if appropriate
+ while (headPtr < headSize && len > 0)
+ {
+ b[off++] = head[headPtr++];
+ count++;
+ len--;
+ }
+
+ return (len == 0) ? count : count + in.read(b, off, len);
+ }
+
+ //Decrypt and inflate
+ if (inflater.needsInput() && !decryptChunk())
+ {
+ reachedEOF = true;
+
+ //Read remaining bytes
+ int numRead;
+ try {
+ numRead = inflater.inflate(b, off, len);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ return -1;
+ }
+
+ if (!inflater.finished())
+ {
+ new Exception("Inflation incomplete").printStackTrace();
+ }
+
+ return numRead == 0 ? -1 : numRead;
+ }
+
+ try {
+ return inflater.inflate(b, off, len);
+ }
+ catch (DataFormatException e)
+ {
+ e.printStackTrace();
+ return -1;
+ }
+ }
+
+
+ /**
+ * Call when inflater indicates that it needs more bytes.
+ * @return - true if we decrypted more bytes to deflate, false if we
+ * encountered the end of stream
+ */
+ protected boolean decryptChunk() throws IOException
+ {
+ while (inflater.needsInput())
+ {
+ int numInBytes = in.read(inBuffer);
+ if (numInBytes == -1) return false;
+// int numDecryptBytes = cipher.update(inBuffer, 0, numInBytes, decryptBuffer);
+// inflater.setInput(decryptBuffer, 0, numDecryptBytes);
+inflater.setInput(inBuffer, 0, numInBytes);
+ }
+
+ return true;
+ }
+
+ /**
+ * This method returns 1 if we've not reached EOF, 0 if we have. Programs
+ * should not rely on this to determine the number of bytes that can be
+ * read without blocking.
+ */
+ public int available() { return reachedEOF ? 0 : 1; }
+
+ /**
+ * Skips bytes by reading them into a cached buffer
+ */
+ public long skip(long n) throws IOException
+ {
+ int skipSize = (int)n;
+ if (skipSize > inBuffer.length) skipSize = inBuffer.length;
+ return read(inBuffer, 0, skipSize);
+ }
+
+}
+
+/*
+ import java.security.KeyPairGenerator;
+ import java.security.KeyPair;
+ import java.security.KeyPairGenerator;
+ import java.security.PrivateKey;
+ import java.security.PublicKey;
+ import java.security.SecureRandom;
+ import java.security.Cipher;
+
+ ....
+
+ java.security.Security.addProvider(new cryptix.provider.Cryptix());
+
+ SecureRandom random = new SecureRandom(SecureRandom.getSeed(30));
+ KeyPairGenerator keygen = KeyPairGenerator.getInstance("RSA");
+ keygen.initialize(1024, random);
+ keypair = keygen.generateKeyPair();
+
+ PublicKey pubkey = keypair.getPublic();
+ PrivateKey privkey = keypair.getPrivate();
+ */
+
+/*
+ *
+ *Generate key pairs
+KeyPairGenerator keyGen =
+ KeyPairGenerator.getInstance("DSA");
+KeyGen.initialize(1024, new SecureRandom(userSeed));
+KeyPair pair = KeyGen.generateKeyPair();
+ */
\ No newline at end of file
diff --git a/src/main/java/com/kitfox/svg/xml/cpx/CPXOutputStream.java b/src/main/java/com/kitfox/svg/xml/cpx/CPXOutputStream.java
new file mode 100644
index 0000000..f129cfb
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/xml/cpx/CPXOutputStream.java
@@ -0,0 +1,174 @@
+/*
+ * CPXOutputStream.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 February 12, 2004, 12:50 PM
+ */
+
+package com.kitfox.svg.xml.cpx;
+
+import java.io.*;
+import java.util.zip.*;
+import java.security.*;
+import javax.crypto.*;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class CPXOutputStream extends FilterOutputStream implements CPXConsts {
+
+ Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
+
+ /** Creates a new instance of CPXOutputStream */
+ public CPXOutputStream(OutputStream os) throws IOException {
+ super(os);
+
+ //Write magic number
+ os.write(MAGIC_NUMBER);
+ }
+
+ /**
+ * Writes the specified byte
to this output stream.
+ * write
method of FilterOutputStream
+ * calls the write
method of its underlying output stream,
+ * that is, it performs out.write(b).
+ * byte
.
+ * @exception IOException if an I/O error occurs.
+ */
+ public void write(int b) throws IOException {
+ final byte[] buf = new byte[1];
+ buf[0] = (byte)b;
+ write(buf, 0, 1);
+ }
+
+ /**
+ * Writes b.length
bytes to this output stream.
+ * write
method of FilterOutputStream
+ * calls its write
method of three arguments with the
+ * arguments b
, 0
, and
+ * b.length
.
+ * write
method of its underlying stream with the single
+ * argument b
.
+ *
+ * @param b the data to be written.
+ * @exception IOException if an I/O error occurs.
+ * @see java.io.FilterOutputStream#write(byte[], int, int)
+ */
+ public void write(byte b[]) throws IOException {
+ write(b, 0, b.length);
+ }
+
+ byte[] deflateBuffer = new byte[2048];
+
+ /**
+ * Writes len
bytes from the specified
+ * byte
array starting at offset off
to
+ * this output stream.
+ * write
method of FilterOutputStream
+ * calls the write
method of one argument on each
+ * byte
to output.
+ * write
method
+ * of its underlying input stream with the same arguments. Subclasses
+ * of FilterOutputStream
should provide a more efficient
+ * implementation of this method.
+ *
+ * @param b the data.
+ * @param off the start offset in the data.
+ * @param len the number of bytes to write.
+ * @exception IOException if an I/O error occurs.
+ * @see java.io.FilterOutputStream#write(int)
+ */
+ public void write(byte b[], int off, int len) throws IOException
+ {
+ deflater.setInput(b, off, len);
+
+ processAllData();
+ /*
+ int numDeflatedBytes;
+ while ((numDeflatedBytes = deflater.deflate(deflateBuffer)) != 0)
+ {
+// byte[] cipherBuf = cipher.update(deflateBuffer, 0, numDeflatedBytes);
+// out.write(cipherBytes);
+out.write(deflateBuffer, 0, numDeflatedBytes);
+ }
+ */
+ }
+
+ protected void processAllData() throws IOException
+ {
+ int numDeflatedBytes;
+ while ((numDeflatedBytes = deflater.deflate(deflateBuffer)) != 0)
+ {
+// byte[] cipherBuf = cipher.update(deflateBuffer, 0, numDeflatedBytes);
+// out.write(cipherBytes);
+out.write(deflateBuffer, 0, numDeflatedBytes);
+ }
+ }
+
+ /**
+ * Flushes this output stream and forces any buffered output bytes
+ * to be written out to the stream.
+ * flush
method of FilterOutputStream
+ * calls the flush
method of its underlying output stream.
+ *
+ * @exception IOException if an I/O error occurs.
+ * @see java.io.FilterOutputStream#out
+ */
+ public void flush() throws IOException {
+ out.flush();
+ }
+
+ /**
+ * Closes this output stream and releases any system resources
+ * associated with the stream.
+ * close
method of FilterOutputStream
+ * calls its flush
method, and then calls the
+ * close
method of its underlying output stream.
+ *
+ * @exception IOException if an I/O error occurs.
+ * @see java.io.FilterOutputStream#flush()
+ * @see java.io.FilterOutputStream#out
+ */
+ public void close() throws IOException {
+ deflater.finish();
+ processAllData();
+
+ try {
+ flush();
+ } catch (IOException ignored) {
+ }
+ out.close();
+ }
+}
diff --git a/src/main/java/com/kitfox/svg/xml/cpx/CPXTest.java b/src/main/java/com/kitfox/svg/xml/cpx/CPXTest.java
new file mode 100644
index 0000000..1b6954a
--- /dev/null
+++ b/src/main/java/com/kitfox/svg/xml/cpx/CPXTest.java
@@ -0,0 +1,100 @@
+/*
+ * CPXTest.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 February 12, 2004, 2:45 PM
+ */
+
+package com.kitfox.svg.xml.cpx;
+
+import java.io.*;
+import java.net.*;
+
+/**
+ * @author Mark McKay
+ * @author Mark McKay
+ */
+public class CPXTest {
+
+ /** Creates a new instance of CPXTest */
+ public CPXTest() {
+
+// FileInputStream fin = new FileInputStream();
+ writeTest();
+ readTest();
+ }
+
+ public void writeTest()
+ {
+ try {
+
+ InputStream is = CPXTest.class.getResourceAsStream("/data/readme.txt");
+//System.err.println("Is " + is);
+
+ FileOutputStream fout = new FileOutputStream("C:\\tmp\\cpxFile.cpx");
+ CPXOutputStream cout = new CPXOutputStream(fout);
+
+ byte[] buffer = new byte[1024];
+ int numBytes;
+ while ((numBytes = is.read(buffer)) != -1)
+ {
+ cout.write(buffer, 0, numBytes);
+ }
+ cout.close();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ public void readTest()
+ {
+ try {
+
+// InputStream is = CPXTest.class.getResourceAsStream("/rawdata/test/cpx/text.txt");
+// InputStream is = CPXTest.class.getResourceAsStream("/rawdata/test/cpx/cpxFile.cpx");
+ FileInputStream is = new FileInputStream("C:\\tmp\\cpxFile.cpx");
+ CPXInputStream cin = new CPXInputStream(is);
+
+ BufferedReader br = new BufferedReader(new InputStreamReader(cin));
+ String line;
+ while ((line = br.readLine()) != null)
+ {
+ System.err.println(line);
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * @param args the command line arguments
+ */
+ public static void main(String[] args) {
+ new CPXTest();
+ }
+
+}
diff --git a/src/main/java/xml/formControls.js b/src/main/java/xml/formControls.js
new file mode 100644
index 0000000..7c951c2
--- /dev/null
+++ b/src/main/java/xml/formControls.js
@@ -0,0 +1,27 @@
+
+/**
+ * Layout a form control. Must be called to initialize and whenever the
+ * form's dimensions change.
+ */
+function pack()
+{
+}
+
+function moveLabel(name, x, y, width, height)
+{
+ var clipRect = svgDocument.getElementById(name + "-clip-rect");
+ clipRect.setAttribute("x", x);
+ clipRect.setAttribute("y", y);
+ clipRect.setAttribute("width", width);
+ clipRect.setAttribute("height", height);
+
+ var rect = svgDocument.getElementById(name + "-rect");
+ rect.setAttribute("x", x);
+ rect.setAttribute("y", y);
+ rect.setAttribute("width", width);
+ rect.setAttribute("height", height);
+
+ var text = svgDocument.getElementById(name + "-text");
+ rect.setAttribute("x", x);
+ rect.setAttribute("y", y);
+}
\ No newline at end of file
diff --git a/src/main/java/xml/postXform.html b/src/main/java/xml/postXform.html
new file mode 100644
index 0000000..970f622
--- /dev/null
+++ b/src/main/java/xml/postXform.html
@@ -0,0 +1,13 @@
+
+ SVG image
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/xml/postXform.svg b/src/main/java/xml/postXform.svg
new file mode 100644
index 0000000..6a360ea
--- /dev/null
+++ b/src/main/java/xml/postXform.svg
@@ -0,0 +1,142 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/xml/sampleForm.xml b/src/main/java/xml/sampleForm.xml
new file mode 100644
index 0000000..3e8479a
--- /dev/null
+++ b/src/main/java/xml/sampleForm.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
diff --git a/src/main/res/example/duke.svg b/src/main/res/example/duke.svg
new file mode 100644
index 0000000..d13b507
--- /dev/null
+++ b/src/main/res/example/duke.svg
@@ -0,0 +1,8 @@
+
+
\ No newline at end of file
diff --git a/src/main/res/res/help/about/about.html b/src/main/res/res/help/about/about.html
new file mode 100644
index 0000000..8923f65
--- /dev/null
+++ b/src/main/res/res/help/about/about.html
@@ -0,0 +1,19 @@
+
+
+
+
+SVG Salamander
+ Created by Mark McKay
+ Copyright 2005
+
+ http://svgsalamander.dev.java.net
+ http://www.kitfox.com
+
+ Last built: 2007, April, 18 02:09
+SVG Salamander
+ Created by Mark McKay
+ Copyright 2005
+
+ http://svgsalamander.dev.java.net
+ http://www.kitfox.com
+
+ Last built:
+