001 /*
002 * SVGLoader.java
003 *
004 *
005 * The Salamander Project - 2D and 3D graphics libraries in Java
006 * Copyright (C) 2004 Mark McKay
007 *
008 * This library is free software; you can redistribute it and/or
009 * modify it under the terms of the GNU Lesser General Public
010 * License as published by the Free Software Foundation; either
011 * version 2.1 of the License, or (at your option) any later version.
012 *
013 * This library is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016 * Lesser General Public License for more details.
017 *
018 * You should have received a copy of the GNU Lesser General Public
019 * License along with this library; if not, write to the Free Software
020 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021 *
022 * Mark McKay can be contacted at mark@kitfox.com. Salamander and other
023 * projects can be found at http://www.kitfox.com
024 *
025 * Created on February 18, 2004, 5:09 PM
026 */
027
028 package com.kitfox.svg;
029
030
031 import java.util.*;
032 import java.net.*;
033 import org.xml.sax.*;
034 import org.xml.sax.helpers.DefaultHandler;
035
036 import com.kitfox.svg.animation.*;
037
038 /**
039 * @author Mark McKay
040 * @author <a href="mailto:mark@kitfox.com">Mark McKay</a>
041 */
042 public class SVGLoader extends DefaultHandler
043 {
044 final HashMap nodeClasses = new HashMap();
045 //final HashMap attribClasses = new HashMap();
046 final LinkedList buildStack = new LinkedList();
047
048 final HashSet ignoreClasses = new HashSet();
049
050 final SVGLoaderHelper helper;
051
052 /**
053 * The diagram that represents the base of this SVG document we're loading.
054 * Will be augmented to include node indexing info and other useful stuff.
055 */
056 final SVGDiagram diagram;
057
058 // SVGElement loadRoot;
059
060 //Used to keep track of document elements that are not part of the SVG namespace
061 int skipNonSVGTagDepth = 0;
062 int indent = 0;
063
064 final boolean verbose;
065
066 /** Creates a new instance of SVGLoader */
067 public SVGLoader(URI xmlBase, SVGUniverse universe)
068 {
069 this(xmlBase, universe, false);
070 }
071
072 public SVGLoader(URI xmlBase, SVGUniverse universe, boolean verbose)
073 {
074 this.verbose = verbose;
075
076 diagram = new SVGDiagram(xmlBase, universe);
077
078 //Compile a list of important builder classes
079 nodeClasses.put("a", A.class);
080 nodeClasses.put("animate", Animate.class);
081 nodeClasses.put("animatecolor", AnimateColor.class);
082 nodeClasses.put("animatemotion", AnimateMotion.class);
083 nodeClasses.put("animatetransform", AnimateTransform.class);
084 nodeClasses.put("circle", Circle.class);
085 nodeClasses.put("clippath", ClipPath.class);
086 nodeClasses.put("defs", Defs.class);
087 nodeClasses.put("desc", Desc.class);
088 nodeClasses.put("ellipse", Ellipse.class);
089 nodeClasses.put("filter", Filter.class);
090 nodeClasses.put("font", Font.class);
091 nodeClasses.put("font-face", FontFace.class);
092 nodeClasses.put("g", Group.class);
093 nodeClasses.put("glyph", Glyph.class);
094 nodeClasses.put("hkern", Hkern.class);
095 nodeClasses.put("image", ImageSVG.class);
096 nodeClasses.put("line", Line.class);
097 nodeClasses.put("lineargradient", LinearGradient.class);
098 nodeClasses.put("marker", Marker.class);
099 nodeClasses.put("metadata", Metadata.class);
100 nodeClasses.put("missing-glyph", MissingGlyph.class);
101 nodeClasses.put("path", Path.class);
102 nodeClasses.put("pattern", PatternSVG.class);
103 nodeClasses.put("polygon", Polygon.class);
104 nodeClasses.put("polyline", Polyline.class);
105 nodeClasses.put("radialgradient", RadialGradient.class);
106 nodeClasses.put("rect", Rect.class);
107 nodeClasses.put("set", SetSmil.class);
108 nodeClasses.put("shape", ShapeElement.class);
109 nodeClasses.put("stop", Stop.class);
110 nodeClasses.put("style", Style.class);
111 nodeClasses.put("svg", SVGRoot.class);
112 nodeClasses.put("symbol", Symbol.class);
113 nodeClasses.put("text", Text.class);
114 nodeClasses.put("title", Title.class);
115 nodeClasses.put("tspan", Tspan.class);
116 nodeClasses.put("use", Use.class);
117
118 ignoreClasses.add("midpointstop");
119
120 //attribClasses.put("clip-path", StyleUrl.class);
121 //attribClasses.put("color", StyleColor.class);
122
123 helper = new SVGLoaderHelper(xmlBase, universe, diagram);
124 }
125
126 private String printIndent(int indent, String indentStrn)
127 {
128 StringBuffer sb = new StringBuffer();
129 for (int i = 0; i < indent; i++)
130 {
131 sb.append(indentStrn);
132 }
133 return sb.toString();
134 }
135
136 public void startDocument() throws SAXException
137 {
138 // System.err.println("Start doc");
139
140 // buildStack.clear();
141 }
142
143 public void endDocument() throws SAXException
144 {
145 // System.err.println("End doc");
146 }
147
148 public void startElement(String namespaceURI, String sName, String qName, Attributes attrs) throws SAXException
149 {
150 if (verbose)
151 {
152 System.err.println(printIndent(indent, " ") + "Starting parse of tag " + sName+ ": " + namespaceURI);
153 }
154 indent++;
155
156 if (skipNonSVGTagDepth != 0 || (!namespaceURI.equals("") && !namespaceURI.equals(SVGElement.SVG_NS)))
157 {
158 skipNonSVGTagDepth++;
159 return;
160 }
161
162 sName = sName.toLowerCase();
163
164 //javax.swing.JOptionPane.showMessageDialog(null, sName);
165
166 Object obj = nodeClasses.get(sName);
167 if (obj == null)
168 {
169 if (!ignoreClasses.contains(sName))
170 {
171 if (verbose)
172 {
173 System.err.println("SVGLoader: Could not identify tag '" + sName + "'");
174 }
175 }
176 return;
177 }
178
179 //Debug info tag depth
180 //for (int i = 0; i < buildStack.size(); i++) System.err.print(" ");
181 //System.err.println("+" + sName);
182
183 try {
184 Class cls = (Class)obj;
185 SVGElement svgEle = (SVGElement)cls.newInstance();
186
187 SVGElement parent = null;
188 if (buildStack.size() != 0) parent = (SVGElement)buildStack.getLast();
189 svgEle.loaderStartElement(helper, attrs, parent);
190
191 buildStack.addLast(svgEle);
192 }
193 catch (Exception e)
194 {
195 e.printStackTrace();
196 throw new SAXException(e);
197 }
198
199 }
200
201 public void endElement(String namespaceURI, String sName, String qName)
202 throws SAXException
203 {
204 indent--;
205 if (verbose)
206 {
207 System.err.println(printIndent(indent, " ") + "Ending parse of tag " + sName+ ": " + namespaceURI);
208 }
209
210 if (skipNonSVGTagDepth != 0)
211 {
212 skipNonSVGTagDepth--;
213 return;
214 }
215
216 sName = sName.toLowerCase();
217
218 Object obj = nodeClasses.get(sName);
219 if (obj == null) return;
220
221 //Debug info tag depth
222 //for (int i = 0; i < buildStack.size(); i++) System.err.print(" ");
223 //System.err.println("-" + sName);
224
225 try {
226 SVGElement svgEle = (SVGElement)buildStack.removeLast();
227
228 svgEle.loaderEndElement(helper);
229
230 SVGElement parent = null;
231 if (buildStack.size() != 0) parent = (SVGElement)buildStack.getLast();
232 //else loadRoot = (SVGElement)svgEle;
233
234 if (parent != null) parent.loaderAddChild(helper, svgEle);
235 else diagram.setRoot((SVGRoot)svgEle);
236
237 }
238 catch (Exception e)
239 {
240 e.printStackTrace();
241 throw new SAXException(e);
242 }
243 }
244
245 public void characters(char buf[], int offset, int len)
246 throws SAXException
247 {
248 if (skipNonSVGTagDepth != 0)
249 {
250 return;
251 }
252
253 if (buildStack.size() != 0)
254 {
255 SVGElement parent = (SVGElement)buildStack.getLast();
256 String s = new String(buf, offset, len);
257 parent.loaderAddText(helper, s);
258 }
259 }
260
261 public void processingInstruction(String target, String data)
262 throws SAXException
263 {
264 //Check for external style sheet
265 }
266
267 // public SVGElement getLoadRoot() { return loadRoot; }
268 public SVGDiagram getLoadedDiagram() { return diagram; }
269 }