Line 0
Link Here
|
|
|
1 |
/***************************************************************************** |
2 |
* Copyright (C) The Apache Software Foundation. All rights reserved. * |
3 |
* ------------------------------------------------------------------------- * |
4 |
* This software is published under the terms of the Apache Software License * |
5 |
* version 1.1, a copy of which has been included with this distribution in * |
6 |
* the LICENSE file. * |
7 |
*****************************************************************************/ |
8 |
|
9 |
package org.apache.batik.transcoder.imagemap; |
10 |
|
11 |
import org.apache.batik.transcoder.SVGAbstractTranscoder; |
12 |
import org.apache.batik.transcoder.TranscoderOutput; |
13 |
import org.apache.batik.transcoder.TranscoderException; |
14 |
import org.apache.batik.bridge.SVGUtilities; |
15 |
import org.apache.batik.bridge.BridgeContext; |
16 |
import org.apache.batik.gvt.GraphicsNode; |
17 |
import org.apache.batik.dom.svg.SVGOMDocument; |
18 |
|
19 |
import javax.xml.transform.sax.TransformerHandler; |
20 |
import javax.xml.transform.sax.SAXTransformerFactory; |
21 |
import javax.xml.transform.sax.SAXSource; |
22 |
import javax.xml.transform.stream.StreamResult; |
23 |
import javax.xml.transform.OutputKeys; |
24 |
import javax.xml.transform.Transformer; |
25 |
import javax.xml.transform.TransformerConfigurationException; |
26 |
|
27 |
import java.awt.geom.PathIterator; |
28 |
import java.awt.geom.AffineTransform; |
29 |
|
30 |
import org.w3c.dom.Document; |
31 |
import org.w3c.dom.Node; |
32 |
import org.w3c.dom.traversal.DocumentTraversal; |
33 |
import org.w3c.dom.traversal.NodeFilter; |
34 |
import org.w3c.dom.traversal.NodeIterator; |
35 |
import org.w3c.dom.svg.SVGAElement; |
36 |
import org.w3c.dom.svg.SVGDocument; |
37 |
import org.w3c.dom.svg.SVGElement; |
38 |
|
39 |
import org.xml.sax.helpers.AttributesImpl; |
40 |
import org.xml.sax.SAXException; |
41 |
|
42 |
/** |
43 |
* This class transcodes an input to a html imagemap. |
44 |
* |
45 |
* @author <a href="mailto:[EMAIL PROTECTED]";>Torsten Knodt</a> |
46 |
*/ |
47 |
public class ImageMapTranscoder extends SVGAbstractTranscoder { |
48 |
|
49 |
private final String XHTML_URI = "http://www.w3.org/1999/xhtml";; |
50 |
private final String CDATA = "CDATA"; |
51 |
private final String AREA_TAG = "area"; |
52 |
private final String MAP_TAG = "map"; |
53 |
private final String TITLE_ATTR = "title"; |
54 |
private final String ALT_ATTR = "alt"; |
55 |
private final String AREA_TAG_SHAPE_ATTR = "shape"; |
56 |
private final String AREA_TAG_SHAPE_ATTR_POLY = "poly"; |
57 |
private final String AREA_TAG_COORDS_ATTR = "coords"; |
58 |
private final String AREA_TAG_HREF_ATTR = "href"; |
59 |
private final String XHTML_SYSID = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd";; |
60 |
private final String XHTML_PUBID = "-//W3C//DTD XHTML 1.0 Strict//EN"; |
61 |
|
62 |
/** |
63 |
* Constructs a new <tt>ImageMapTranscoder</tt>. |
64 |
*/ |
65 |
public ImageMapTranscoder() { |
66 |
} |
67 |
|
68 |
/** |
69 |
* Get SAX Transformer factory |
70 |
* |
71 |
* @result SAX Transformer factory to use |
72 |
*/ |
73 |
private SAXTransformerFactory getTransformerHandlerFactory() |
74 |
{ |
75 |
return (SAXTransformerFactory) SAXTransformerFactory.newInstance(); |
76 |
} |
77 |
|
78 |
/** |
79 |
* Get SAX Transformer from Factory |
80 |
* |
81 |
* @param output the output where to transcode |
82 |
* @result the transformer handler |
83 |
*/ |
84 |
private TransformerHandler getTransformerHandler(TranscoderOutput output) |
85 |
throws TransformerConfigurationException |
86 |
{ |
87 |
SAXTransformerFactory tfactory = getTransformerHandlerFactory(); |
88 |
if (tfactory.getFeature(SAXSource.FEATURE) && tfactory.getFeature(StreamResult.FEATURE)) |
89 |
{ |
90 |
TransformerHandler ch = tfactory.newTransformerHandler(); |
91 |
ch.setResult (new StreamResult (output.getOutputStream())); |
92 |
return ch; |
93 |
} |
94 |
return null; |
95 |
} |
96 |
|
97 |
/** |
98 |
* Configure the SAX transformer handler |
99 |
* |
100 |
* @param th the SAX transformer handler |
101 |
*/ |
102 |
private void configureTransformerHandler(TransformerHandler th) |
103 |
{ |
104 |
// th.setSystemId (XHTML_SYSID); |
105 |
// FIXME: ID/ name attribute must be added to map element to make output valid |
106 |
} |
107 |
|
108 |
/** |
109 |
* Configure the SAX transformer |
110 |
* |
111 |
* @param th the SAX transformer |
112 |
*/ |
113 |
private void configureTransformer(Transformer t) |
114 |
{ |
115 |
// t.setOutputProperty (OutputKeys.DOCTYPE_PUBLIC, XHTML_PUBID); |
116 |
// t.setOutputProperty (OutputKeys.DOCTYPE_SYSTEM, XHTML_SYSID); |
117 |
// FIXME: ID/ name attribute must be added to map element to make output valid |
118 |
t.setOutputProperty (OutputKeys.INDENT, "yes"); |
119 |
t.setOutputProperty (OutputKeys.MEDIA_TYPE, "text/xml"); |
120 |
t.setOutputProperty (OutputKeys.STANDALONE, "no"); |
121 |
t.setOutputProperty (OutputKeys.OMIT_XML_DECLARATION, "no"); |
122 |
t.setOutputProperty (OutputKeys.METHOD, "xml"); |
123 |
} |
124 |
|
125 |
/** |
126 |
* Sets the title attribute for an element |
127 |
* |
128 |
* @param element the element to fetch the title for |
129 |
* @param att the Attribute Implementation to use |
130 |
*/ |
131 |
private void setTitle(SVGElement element, AttributesImpl att) |
132 |
{ |
133 |
// We could also use xlink:title, but I think svg:desc is better. |
134 |
String s = SVGUtilities.getDescription (element); |
135 |
if (s != null) |
136 |
if (! s.equals ("")) |
137 |
att.addAttribute("", TITLE_ATTR, TITLE_ATTR, CDATA, s); |
138 |
} |
139 |
|
140 |
/** |
141 |
* Sets the alt attribute for an element |
142 |
* |
143 |
* @param element the element to fetch the title for |
144 |
* @param att the Attribute Implementation to use |
145 |
*/ |
146 |
private void setAlt(SVGElement element, AttributesImpl att) |
147 |
{ |
148 |
// Here svg:title should be used instead of svg:desc, I think. Perhaps xlink:title would also be an idea. |
149 |
String s = SVGUtilities.getDescription (element); |
150 |
if (s == null) |
151 |
s = ""; |
152 |
att.addAttribute("", ALT_ATTR, ALT_ATTR, CDATA, s); |
153 |
} |
154 |
|
155 |
/** |
156 |
* Reimplemented here to set the dynamic flag so the BridgeContext |
157 |
* tracks the DOM node <-> gvt node association. |
158 |
* @param doc the SVG document to create the BridgeContext for |
159 |
* @return the newly instantiated BridgeContext |
160 |
*/ |
161 |
protected BridgeContext createBridgeContext(SVGOMDocument doc) { |
162 |
BridgeContext c = super.createBridgeContext(doc); |
163 |
c.setDynamicState(BridgeContext.DYNAMIC); |
164 |
return c; |
165 |
} |
166 |
|
167 |
/** |
168 |
* Transcodes the specified Document as an image in the specified output. |
169 |
* |
170 |
* @param document the document to transcode |
171 |
* @param uri the uri of the document or null if any |
172 |
* @param output the ouput where to transcode |
173 |
* @exception TranscoderException if an error occured while transcoding |
174 |
*/ |
175 |
protected void transcode(Document document, |
176 |
String uri, |
177 |
TranscoderOutput output) |
178 |
throws TranscoderException |
179 |
{ |
180 |
super.transcode(document, uri, output); |
181 |
|
182 |
TransformerHandler filter; |
183 |
try { |
184 |
filter = getTransformerHandler(output); |
185 |
} |
186 |
catch (TransformerConfigurationException ex) { |
187 |
throw new TranscoderException (ex); |
188 |
} |
189 |
configureTransformerHandler (filter); |
190 |
configureTransformer (filter.getTransformer()); |
191 |
|
192 |
AttributesImpl myatt = new AttributesImpl(); |
193 |
setTitle ((SVGElement) (document.getDocumentElement()), myatt); |
194 |
try { |
195 |
filter.startDocument(); |
196 |
// filter.startDTD(MAP_TAG, XHTML_PUBID, XHTML_SYSID); |
197 |
// filter.endDTD(); |
198 |
// FIXME: ID/ name attribute must be added to map element to make output valid |
199 |
filter.startElement (XHTML_URI, MAP_TAG, MAP_TAG, myatt); |
200 |
} |
201 |
catch (SAXException ex) { |
202 |
throw new TranscoderException (ex); |
203 |
} |
204 |
|
205 |
NodeIterator i = ((DocumentTraversal) document).createNodeIterator(document, NodeFilter.SHOW_ELEMENT, null, true); |
206 |
Node node; |
207 |
while ((node = i.nextNode()) != null) |
208 |
{ |
209 |
SVGAElement e; |
210 |
try { |
211 |
e = (SVGAElement) node; |
212 |
} |
213 |
catch (ClassCastException ex) { |
214 |
continue; |
215 |
} |
216 |
|
217 |
GraphicsNode gn = ctx.getGraphicsNode(e); |
218 |
System.out.println("Node: " + e + ", GraphicsNode: " + gn); |
219 |
AffineTransform tx = gn.getGlobalTransform(); |
220 |
tx.preConcatenate(curTxf); |
221 |
|
222 |
int state = PathIterator.SEG_CLOSE; |
223 |
String coords = ""; |
224 |
int[] lastpoints = null; |
225 |
|
226 |
for ( PathIterator path = gn.getOutline().getPathIterator(tx, 1.0); |
227 |
! path.isDone(); |
228 |
path.next() ) |
229 |
{ |
230 |
float[] points = new float[6]; |
231 |
int seg = path.currentSegment (points); |
232 |
int[] newpoints = { Math.round(points[0]), Math.round(points[1]) }; |
233 |
|
234 |
switch (seg) |
235 |
{ |
236 |
case PathIterator.SEG_CLOSE: |
237 |
// End of path segment, so push out Tag |
238 |
if (! coords.equals("")) |
239 |
{ |
240 |
AttributesImpl att = new AttributesImpl(); |
241 |
att.addAttribute("", AREA_TAG_SHAPE_ATTR, AREA_TAG_SHAPE_ATTR, CDATA, AREA_TAG_SHAPE_ATTR_POLY); |
242 |
att.addAttribute("", AREA_TAG_COORDS_ATTR, AREA_TAG_COORDS_ATTR, CDATA, coords); |
243 |
att.addAttribute("", AREA_TAG_HREF_ATTR, AREA_TAG_HREF_ATTR, CDATA, e.getHref().getBaseVal()); |
244 |
setTitle (e, att); |
245 |
setAlt (e, att); |
246 |
try { |
247 |
filter.startElement(XHTML_URI, AREA_TAG, AREA_TAG, att); |
248 |
filter.endElement(XHTML_URI, AREA_TAG, AREA_TAG); |
249 |
} catch (SAXException ex) { throw new TranscoderException (ex); } |
250 |
coords = ""; |
251 |
lastpoints = null; |
252 |
} |
253 |
break; |
254 |
case PathIterator.SEG_CUBICTO: |
255 |
// Cubic segment not allowed in flattened path |
256 |
throw new TranscoderException ("Cubic segment not allowed in flattened path"); |
257 |
case PathIterator.SEG_QUADTO: |
258 |
// Quad segment not allowed in flattened path |
259 |
throw new TranscoderException ("Quad segment not allowed in flattened path"); |
260 |
case PathIterator.SEG_LINETO: |
261 |
if (coords.equals("")) |
262 |
throw new TranscoderException ("Line segment not allowed at begin of path"); |
263 |
// Add line segment to polygon coords |
264 |
if (! lastpoints.equals (newpoints)) |
265 |
coords = coords + "," + newpoints[0] + "," + newpoints[1]; |
266 |
break; |
267 |
case PathIterator.SEG_MOVETO: |
268 |
if (! coords.equals("")) |
269 |
throw new TranscoderException ("Move segment only allowed at begin of path"); |
270 |
// Use move segment as polygon start coords |
271 |
coords = newpoints[0] + "," + newpoints[1]; |
272 |
break; |
273 |
default: |
274 |
throw new TranscoderException ("Unknown segment with type number " + seg); |
275 |
} |
276 |
|
277 |
lastpoints = newpoints; |
278 |
} |
279 |
} |
280 |
try { |
281 |
filter.endElement (XHTML_URI, MAP_TAG, MAP_TAG); |
282 |
filter.endDocument(); |
283 |
} |
284 |
catch (SAXException ex) { |
285 |
throw new TranscoderException (ex); |
286 |
} |
287 |
} |
288 |
|
289 |
} |