View Javadoc

1   /**********************************************
2    * Copyright (C) 2010 Lukas Laag
3    * This file is part of lib-gwt-svg.
4    * 
5    * libgwtsvg is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU Lesser General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    * 
10   * libgwtsvg is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU Lesser General Public License for more details.
14   * 
15   * You should have received a copy of the GNU Lesser General Public License
16   * along with libgwtsvg.  If not, see http://www.gnu.org/licenses/
17   **********************************************/
18  package org.vectomatic.dom.svg.impl;
19  
20  import java.util.ArrayList;
21  import java.util.Iterator;
22  import java.util.List;
23  
24  import org.vectomatic.dom.svg.OMSVGScriptElement;
25  import org.vectomatic.dom.svg.utils.DOMHelper;
26  import org.vectomatic.dom.svg.utils.ParserException;
27  import org.vectomatic.dom.svg.utils.SVGPrefixResolver;
28  
29  import com.google.gwt.core.client.JavaScriptException;
30  import com.google.gwt.core.client.JavaScriptObject;
31  import com.google.gwt.dom.client.Document;
32  import com.google.gwt.dom.client.Element;
33  import com.google.gwt.dom.client.Node;
34  import com.google.gwt.dom.client.Text;
35  
36  public class SVGParserImpl {
37  	@SuppressWarnings("unused")
38  	private final JavaScriptObject domParser = createDOMParser();
39  	
40  	private native JavaScriptObject createDOMParser() /*-{
41  	  return new DOMParser();
42  	}-*/;
43  
44  	public final native Document parseFromString(String rawText, String contentType) /*-{
45        return this.@org.vectomatic.dom.svg.impl.SVGParserImpl::domParser.parseFromString(rawText, contentType);
46  	}-*/;
47  
48  	/**
49  	 * Parses the supplied SVG text into a document
50  	 * @param rawSvg
51  	 * raw xml to be parsed
52  	 * @param enableScripts
53  	 * true to enable embedded scripts, false otherwise
54  	 * @return
55  	 * the document resulting from the parse
56  	 */
57  	public SVGSVGElement parse(String rawSvg, boolean enableScripts) throws ParserException {
58  		if (isIE()) {
59  			return parseIE(rawSvg, enableScripts);
60  		}
61  		SVGDocument doc = parseFromString(rawSvg, "text/xml").cast();
62  		Element elt = doc.getDocumentElement();
63  		if ("parsererror".equals(DOMHelper.getLocalName(elt))) {
64  			String message = "Parsing error";
65  			if (elt.getFirstChild() != null) {
66  				message = elt.getFirstChild().<Text>cast().getData();
67  			}
68  			throw new ParserException(ParserException.Type.NotWellFormed, message);
69  		}
70  		SVGSVGElement svg = DOMHelper.importNode(DOMHelper.getCurrentDocument(), elt, true).<SVGSVGElement>cast();
71  		return enableScripts ? enableScriptElements(svg) : svg;
72  	}
73  
74  	/**
75  	 * Re-enables scripts disabled by the DOMParser.parseFromString method.
76  	 * @param svg
77  	 * The svg for which scripts are to be re-enabled.
78  	 * @return
79  	 * The svg with re-enabled scripts.
80  	 */
81  	protected static SVGSVGElement enableScriptElements(SVGSVGElement svg) {
82  		// Put all scripts in a list (XPath result sets cannot be modified during traversal).
83  		List<SVGScriptElement> scripts = new ArrayList<SVGScriptElement>();
84  		Iterator<Node> iterator = DOMHelper.evaluateNodeListXPath(svg, "//svg:script", SVGPrefixResolver.INSTANCE);
85  		while (iterator.hasNext()) {
86  			scripts.add(iterator.next().<SVGScriptElement>cast());
87  		}
88  		for (SVGScriptElement script : scripts) {
89  			// Reparent the script subtree under a fresh script node
90  			SVGScriptElement newScript = new OMSVGScriptElement().getElement().<SVGScriptElement>cast();
91  			Node node;
92  			while((node = script.getFirstChild()) != null) {
93  				newScript.appendChild(script.removeChild(node));
94  			}
95  			script.getParentNode().replaceChild(newScript, script);
96  		}
97  		return svg;
98  	}
99  	
100 	public static native boolean isIE() /*-{
101 		return typeof document.documentMode === 'number';
102 	}-*/;
103 	
104 	public final SVGSVGElement parseIE(String rawSvg, boolean enableScripts) throws ParserException {
105 		SVGDocument doc = null;
106 		try {
107 			doc = parseFromString(rawSvg, "text/xml").cast();
108 			
109 		} catch(JavaScriptException e) {
110 			throw new ParserException(ParserException.Type.NotWellFormed, e.getMessage());
111 		}
112 		Element elt = doc.getDocumentElement();
113 		if ("parsererror".equals(DOMHelper.getLocalName(elt))) {
114 			String message = "Parsing error";
115 			if (elt.getFirstChild() != null) {
116 				message = elt.getFirstChild().<Text>cast().getData();
117 			}
118 			throw new ParserException(ParserException.Type.NotWellFormed, message);
119 		}
120 		SVGSVGElement svg = DOMHelper.importNode(DOMHelper.getCurrentDocument(), elt, true).cast();
121 		// IE9 bug workaround: update all SVG style elements by
122 		// adding a trailing whitespace char, otherwise IE9 will
123 		// ignore them
124 		Iterator<Text> iterator = DOMHelper.evaluateNodeListXPath(svg, ".//svg:style/text()", SVGPrefixResolver.INSTANCE);
125 		List<Text> styleTexts = new ArrayList<Text>();
126 		while(iterator.hasNext()) {
127 			Text styleText = iterator.next();
128 			styleTexts.add(styleText);
129 		}
130 		for (Text styleText : styleTexts) {
131 			styleText.<Text>cast().setData(styleText.<Text>cast().getData() + " ");
132 		}
133 		
134 		return enableScripts ? enableScriptElements(svg) : svg;
135 	}
136 
137 }