KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > batik > bridge > SVGImageElementBridge


1 /*
2
3    Copyright 2001-2004 The Apache Software Foundation
4
5    Licensed under the Apache License, Version 2.0 (the "License");
6    you may not use this file except in compliance with the License.
7    You may obtain a copy of the License at
8
9        http://www.apache.org/licenses/LICENSE-2.0
10
11    Unless required by applicable law or agreed to in writing, software
12    distributed under the License is distributed on an "AS IS" BASIS,
13    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14    See the License for the specific language governing permissions and
15    limitations under the License.
16
17  */

18 package org.apache.batik.bridge;
19
20 import java.awt.RenderingHints JavaDoc;
21 import java.awt.Shape JavaDoc;
22 import java.awt.color.ColorSpace JavaDoc;
23 import java.awt.color.ICC_Profile JavaDoc;
24 import java.awt.geom.AffineTransform JavaDoc;
25 import java.awt.geom.Rectangle2D JavaDoc;
26 import java.io.BufferedInputStream JavaDoc;
27 import java.io.InputStream JavaDoc;
28 import java.io.IOException JavaDoc;
29 import java.util.ArrayList JavaDoc;
30 import java.util.List JavaDoc;
31
32 import org.apache.batik.css.engine.CSSEngine;
33 import org.apache.batik.css.engine.SVGCSSEngine;
34 import org.apache.batik.dom.svg.SVGOMDocument;
35 import org.apache.batik.dom.svg.SVGOMElement;
36 import org.apache.batik.dom.svg.XMLBaseSupport;
37 import org.apache.batik.dom.util.XLinkSupport;
38 import org.apache.batik.ext.awt.color.ICCColorSpaceExt;
39 import org.apache.batik.ext.awt.image.renderable.ClipRable8Bit;
40 import org.apache.batik.ext.awt.image.renderable.Filter;
41 import org.apache.batik.ext.awt.image.spi.ImageTagRegistry;
42 import org.apache.batik.gvt.CanvasGraphicsNode;
43 import org.apache.batik.gvt.CompositeGraphicsNode;
44 import org.apache.batik.gvt.GraphicsNode;
45 import org.apache.batik.gvt.ImageNode;
46 import org.apache.batik.gvt.RasterImageNode;
47 import org.apache.batik.gvt.ShapeNode;
48 import org.apache.batik.util.ParsedURL;
49 import org.apache.batik.util.MimeTypeConstants;
50 import org.w3c.dom.Document JavaDoc;
51 import org.w3c.dom.Element JavaDoc;
52 import org.w3c.dom.Node JavaDoc;
53 import org.w3c.dom.events.DocumentEvent JavaDoc;
54 import org.w3c.dom.events.Event JavaDoc;
55 import org.w3c.dom.events.EventListener JavaDoc;
56 import org.w3c.dom.events.EventTarget JavaDoc;
57 import org.w3c.dom.events.MouseEvent JavaDoc;
58 import org.w3c.dom.events.MutationEvent JavaDoc;
59 import org.w3c.dom.svg.SVGDocument;
60 import org.w3c.dom.svg.SVGSVGElement;
61
62 /**
63  * Bridge class for the <image> element.
64  *
65  * @author <a HREF="mailto:tkormann@apache.org">Thierry Kormann</a>
66  * @version $Id: SVGImageElementBridge.java,v 1.72 2005/03/27 08:58:30 cam Exp $
67  */

68 public class SVGImageElementBridge extends AbstractGraphicsNodeBridge {
69
70     protected SVGDocument imgDocument;
71     protected EventListener JavaDoc listener = null;
72     protected BridgeContext subCtx = null;
73     protected boolean hitCheckChildren = false;
74     /**
75      * Constructs a new bridge for the &lt;image> element.
76      */

77     public SVGImageElementBridge() {}
78
79     /**
80      * Returns 'image'.
81      */

82     public String JavaDoc getLocalName() {
83         return SVG_IMAGE_TAG;
84     }
85
86     /**
87      * Returns a new instance of this bridge.
88      */

89     public Bridge getInstance() {
90         return new SVGImageElementBridge();
91     }
92
93     /**
94      * Creates a graphics node using the specified BridgeContext and for the
95      * specified element.
96      *
97      * @param ctx the bridge context to use
98      * @param e the element that describes the graphics node to build
99      * @return a graphics node that represents the specified element
100      */

101     public GraphicsNode createGraphicsNode(BridgeContext ctx, Element e) {
102         ImageNode imageNode = (ImageNode)super.createGraphicsNode(ctx, e);
103         if (imageNode == null) {
104             return null;
105         }
106
107         hitCheckChildren = false;
108         GraphicsNode node = buildImageGraphicsNode(ctx,e);
109
110         if (node == null) {
111             String JavaDoc uriStr = XLinkSupport.getXLinkHref(e);
112             throw new BridgeException(e, ERR_URI_IMAGE_INVALID,
113                                       new Object JavaDoc[] {uriStr});
114         }
115
116         imageNode.setImage(node);
117         imageNode.setHitCheckChildren(hitCheckChildren);
118
119         // 'image-rendering' and 'color-rendering'
120
RenderingHints JavaDoc hints = null;
121         hints = CSSUtilities.convertImageRendering(e, hints);
122         hints = CSSUtilities.convertColorRendering(e, hints);
123         if (hints != null)
124             imageNode.setRenderingHints(hints);
125
126         return imageNode;
127     }
128
129     /**
130      * Create a Graphics node according to the
131      * resource pointed by the href : RasterImageNode
132      * for bitmaps, CompositeGraphicsNode for svg files.
133      *
134      * @param ctx : the bridge context to use
135      * @param e the element that describes the graphics node to build
136      *
137      * @return the graphic node that represent the resource
138      * pointed by the reference
139      */

140     protected GraphicsNode buildImageGraphicsNode
141         (BridgeContext ctx, Element e){
142
143         // 'xlink:href' attribute - required
144
String JavaDoc uriStr = XLinkSupport.getXLinkHref(e);
145         if (uriStr.length() == 0) {
146             throw new BridgeException(e, ERR_ATTRIBUTE_MISSING,
147                                       new Object JavaDoc[] {"xlink:href"});
148         }
149         if (uriStr.indexOf('#') != -1) {
150             throw new BridgeException(e, ERR_ATTRIBUTE_VALUE_MALFORMED,
151                                       new Object JavaDoc[] {"xlink:href", uriStr});
152         }
153
154         // Build the URL.
155
String JavaDoc baseURI = XMLBaseSupport.getCascadedXMLBase(e);
156         ParsedURL purl;
157         if (baseURI == null)
158             purl = new ParsedURL(uriStr);
159         else
160             purl = new ParsedURL(baseURI, uriStr);
161
162         return createImageGraphicsNode(ctx, e, purl);
163     }
164
165     protected GraphicsNode createImageGraphicsNode(BridgeContext ctx,
166                                                    Element e,
167                                                    ParsedURL purl)
168     {
169         Rectangle2D JavaDoc bounds = getImageBounds(ctx, e);
170         if ((bounds.getWidth() == 0) || (bounds.getHeight() == 0)) {
171             ShapeNode sn = new ShapeNode();
172             sn.setShape(bounds);
173             return sn;
174         }
175
176         SVGDocument svgDoc = (SVGDocument)e.getOwnerDocument();
177         String JavaDoc docURL = svgDoc.getURL();
178         ParsedURL pDocURL = null;
179         if (docURL != null)
180             pDocURL = new ParsedURL(docURL);
181
182         UserAgent userAgent = ctx.getUserAgent();
183
184         try {
185             userAgent.checkLoadExternalResource(purl, pDocURL);
186         } catch (SecurityException JavaDoc ex) {
187             throw new BridgeException(e, ERR_URI_UNSECURE,
188                                       new Object JavaDoc[] {purl});
189         }
190
191         DocumentLoader loader = ctx.getDocumentLoader();
192         ImageTagRegistry reg = ImageTagRegistry.getRegistry();
193         ICCColorSpaceExt colorspace = extractColorSpace(e, ctx);
194         {
195             /**
196              * Before we open the URL we see if we have the
197              * URL already cached and parsed
198              */

199             try {
200                 /* Check the document loader cache */
201                 Document doc = loader.checkCache(purl.toString());
202                 if (doc != null) {
203                     imgDocument = (SVGDocument)doc;
204                     return createSVGImageNode(ctx, e, imgDocument);
205                 }
206             } catch (BridgeException ex) {
207                 throw ex;
208             } catch (Exception JavaDoc ex) {
209                 /* Nothing to do */
210             }
211
212             /* Check the ImageTagRegistry Cache */
213             Filter img = reg.checkCache(purl, colorspace);
214             if (img != null) {
215                 return createRasterImageNode(ctx, e, img);
216             }
217         }
218
219         /* The Protected Stream ensures that the stream doesn't
220          * get closed unless we want it to. It is also based on
221          * a Buffered Reader so in general we can mark the start
222          * and reset rather than reopening the stream. Finally
223          * it hides the mark/reset methods so only we get to
224          * use them.
225          */

226         ProtectedStream reference = null;
227         try {
228             reference = openStream(e, purl);
229         } catch (SecurityException JavaDoc ex) {
230             throw new BridgeException(e, ERR_URI_UNSECURE,
231                                       new Object JavaDoc[] {purl});
232         } catch (IOException JavaDoc ioe) {
233             return createBrokenImageNode(ctx, e, purl.toString());
234         }
235
236         {
237             /**
238              * First see if we can id the file as a Raster via magic
239              * number. This is probably the fastest mechanism.
240              * We tell the registry what the source purl is but we
241              * tell it not to open that url.
242              */

243             Filter img = reg.readURL(reference, purl, colorspace,
244                                      false, false);
245             if (img != null) {
246                 // It's a bouncing baby Raster...
247
return createRasterImageNode(ctx, e, img);
248             }
249         }
250
251         try {
252             // Reset the stream for next try.
253
reference.retry();
254         } catch (IOException JavaDoc ioe) {
255             try {
256                 // Couldn't reset stream so reopen it.
257
reference = openStream(e, purl);
258             } catch (IOException JavaDoc ioe2) {
259                 // Since we already opened the stream this is unlikely.
260
return createBrokenImageNode(ctx, e, purl.toString());
261             }
262         }
263
264         try {
265             /**
266              * Next see if it's an XML document.
267              */

268             Document doc = loader.loadDocument(purl.toString(), reference);
269             imgDocument = (SVGDocument)doc;
270             return createSVGImageNode(ctx, e, imgDocument);
271         } catch (BridgeException ex) {
272             throw ex;
273         } catch (SecurityException JavaDoc ex) {
274             throw new BridgeException(e, ERR_URI_UNSECURE,
275                                       new Object JavaDoc[] {purl});
276         } catch (Exception JavaDoc ex) {
277             /* Nothing to do */
278             // ex.printStackTrace();
279
}
280
281         try {
282             reference.retry();
283         } catch (IOException JavaDoc ioe) {
284             try {
285                 // Couldn't reset stream so reopen it.
286
reference = openStream(e, purl);
287             } catch (IOException JavaDoc ioe2) {
288                 return createBrokenImageNode(ctx, e, purl.toString());
289             }
290         }
291
292         try {
293             // Finally try to load the image as a raster image (JPG or
294
// PNG) allowing the registry to open the url (so the
295
// JDK readers can be checked).
296
Filter img = reg.readURL(reference, purl, colorspace,
297                                      true, true);
298             if (img != null) {
299                 // It's a bouncing baby Raster...
300
return createRasterImageNode(ctx, e, img);
301             }
302         } finally {
303             reference.release();
304         }
305         return null;
306     }
307
308     static public class ProtectedStream extends BufferedInputStream JavaDoc {
309         final static int BUFFER_SIZE = 8192;
310         ProtectedStream(InputStream JavaDoc is) {
311             super(is, BUFFER_SIZE);
312             super.mark(BUFFER_SIZE); // Remember start
313
}
314         ProtectedStream(InputStream JavaDoc is, int size) {
315             super(is, size);
316             super.mark(size); // Remember start
317
}
318
319         public boolean markSupported() {
320             return false;
321         }
322         public void mark(int sz){
323         }
324         public void reset() throws IOException JavaDoc {
325             throw new IOException JavaDoc("Reset unsupported");
326         }
327
328         public void retry() throws IOException JavaDoc {
329             super.reset();
330         }
331
332         public void close() throws IOException JavaDoc {
333             /* do nothing */
334         }
335
336         public void release() {
337             try {
338                 super.close();
339             } catch (IOException JavaDoc ioe) {
340                 // Like Duh! what would you do close it again?
341
}
342         }
343     }
344
345     protected ProtectedStream openStream(Element e, ParsedURL purl)
346         throws IOException JavaDoc {
347         List JavaDoc mimeTypes = new ArrayList JavaDoc
348             (ImageTagRegistry.getRegistry().getRegisteredMimeTypes());
349         mimeTypes.add(MimeTypeConstants.MIME_TYPES_SVG);
350         InputStream JavaDoc reference = purl.openStream(mimeTypes.iterator());
351         return new ProtectedStream(reference);
352     }
353
354     /**
355      * Creates an <tt>ImageNode</tt>.
356      */

357     protected GraphicsNode instantiateGraphicsNode() {
358         return new ImageNode();
359     }
360
361     /**
362      * Returns false as image is not a container.
363      */

364     public boolean isComposite() {
365         return false;
366     }
367
368     // dynamic support
369

370     /**
371      * This method is invoked during the build phase if the document
372      * is dynamic. The responsability of this method is to ensure that
373      * any dynamic modifications of the element this bridge is
374      * dedicated to, happen on its associated GVT product.
375      */

376     protected void initializeDynamicSupport(BridgeContext ctx,
377                                             Element e,
378                                             GraphicsNode node) {
379         if (!ctx.isInteractive())
380             return;
381
382         // Bind the nodes for interactive and dynamic
383
// HACK due to the way images are represented in GVT
384
ImageNode imgNode = (ImageNode)node;
385         ctx.bind(e, node);
386
387         if (ctx.isDynamic()) {
388             // Only do this for dynamic not interactive.
389
this.e = e;
390             this.node = node;
391             this.ctx = ctx;
392             ((SVGOMElement)e).setSVGContext(this);
393         }
394     }
395
396     // BridgeUpdateHandler implementation //////////////////////////////////
397

398     /**
399      * Invoked when an MutationEvent of type 'DOMAttrModified' is fired.
400      */

401     public void handleDOMAttrModifiedEvent(MutationEvent JavaDoc evt) {
402
403         String JavaDoc attrName = evt.getAttrName();
404         Node evtNode = evt.getRelatedNode();
405
406         if (attrName.equals(SVG_X_ATTRIBUTE) ||
407             attrName.equals(SVG_Y_ATTRIBUTE) ||
408         attrName.equals(SVG_PRESERVE_ASPECT_RATIO_ATTRIBUTE)){
409             updateImageBounds();
410         } else if (( XLinkSupport.XLINK_NAMESPACE_URI.equals
411                      (evtNode.getNamespaceURI()) )
412                    && SVG_HREF_ATTRIBUTE.equals(evtNode.getLocalName()) ){
413             rebuildImageNode();
414     } else if(attrName.equals(SVG_WIDTH_ATTRIBUTE) ||
415                   attrName.equals(SVG_HEIGHT_ATTRIBUTE)) {
416             float oldV = 0, newV=0;
417             String JavaDoc s = evt.getPrevValue();
418             UnitProcessor.Context uctx = UnitProcessor.createContext(ctx, e);
419
420             if (s.length() != 0) {
421                 oldV = UnitProcessor.svgHorizontalCoordinateToUserSpace
422                     (s, attrName, uctx);
423             }
424             s = evt.getNewValue();
425             if (s.length() != 0) {
426                 newV = UnitProcessor.svgHorizontalCoordinateToUserSpace
427                     (s, attrName, uctx);
428             }
429             if (oldV == newV) return;
430             
431             if ((oldV == 0) || (newV == 0))
432                 rebuildImageNode();
433             else
434                 updateImageBounds();
435         } else {
436             super.handleDOMAttrModifiedEvent(evt);
437     }
438     }
439
440     protected void updateImageBounds() {
441         //retrieve the new bounds of the image tag
442
Rectangle2D JavaDoc bounds = getImageBounds(ctx, e);
443         GraphicsNode imageNode = ((ImageNode)node).getImage();
444         float [] vb = null;
445         if (imageNode instanceof RasterImageNode) {
446             //Raster image
447
Rectangle2D JavaDoc imgBounds =
448                 ((RasterImageNode)imageNode).getImageBounds();
449             // create the implicit viewBox for the raster
450
// image. The viewBox for a raster image is the size
451
// of the image
452
vb = new float[4];
453             vb[0] = 0; // x
454
vb[1] = 0; // y
455
vb[2] = (float)imgBounds.getWidth(); // width
456
vb[3] = (float)imgBounds.getHeight(); // height
457
} else {
458             if (imgDocument != null) {
459                 Element svgElement = imgDocument.getRootElement();
460                 String JavaDoc viewBox = svgElement.getAttributeNS
461                     (null, SVG_VIEW_BOX_ATTRIBUTE);
462                 vb = ViewBox.parseViewBoxAttribute(e, viewBox);
463             }
464         }
465         if (imageNode != null) {
466             // handles the 'preserveAspectRatio', 'overflow' and
467
// 'clip' and sets the appropriate AffineTransform to
468
// the image node
469
initializeViewport(ctx, e, imageNode, vb, bounds);
470         }
471             
472     }
473
474     protected void rebuildImageNode() {
475         // Reference copy of the imgDocument
476
if ((imgDocument != null) && (listener != null)) {
477             EventTarget JavaDoc tgt = (EventTarget JavaDoc)imgDocument.getRootElement();
478
479             tgt.removeEventListener(SVG_EVENT_CLICK, listener, false);
480             tgt.removeEventListener(SVG_EVENT_KEYDOWN, listener, false);
481             tgt.removeEventListener(SVG_EVENT_KEYPRESS, listener, false);
482             tgt.removeEventListener(SVG_EVENT_KEYUP, listener, false);
483             tgt.removeEventListener(SVG_EVENT_MOUSEDOWN, listener, false);
484             tgt.removeEventListener(SVG_EVENT_MOUSEMOVE, listener, false);
485             tgt.removeEventListener(SVG_EVENT_MOUSEOUT, listener, false);
486             tgt.removeEventListener(SVG_EVENT_MOUSEOVER, listener, false);
487             tgt.removeEventListener(SVG_EVENT_MOUSEUP, listener, false);
488             listener = null;
489         }
490
491         if (imgDocument != null) {
492             SVGSVGElement svgElement = imgDocument.getRootElement();
493             disposeTree(svgElement);
494         }
495
496         imgDocument = null;
497         subCtx = null;
498
499         //update of the reference of the image.
500
GraphicsNode inode = buildImageGraphicsNode(ctx,e);
501
502         ImageNode imgNode = (ImageNode)node;
503         imgNode.setImage(inode);
504
505         if (inode == null) {
506             String JavaDoc uriStr = XLinkSupport.getXLinkHref(e);
507             throw new BridgeException(e, ERR_URI_IMAGE_INVALID,
508                                       new Object JavaDoc[] {uriStr});
509         }
510     }
511
512     /**
513      * Invoked for each CSS property that has changed.
514      */

515     protected void handleCSSPropertyChanged(int property) {
516         switch(property) {
517         case SVGCSSEngine.IMAGE_RENDERING_INDEX:
518         case SVGCSSEngine.COLOR_INTERPOLATION_INDEX:
519             RenderingHints JavaDoc hints = CSSUtilities.convertImageRendering(e, null);
520             hints = CSSUtilities.convertColorRendering(e, hints);
521             if (hints != null) {
522                 node.setRenderingHints(hints);
523             }
524             break;
525         default:
526             super.handleCSSPropertyChanged(property);
527         }
528     }
529
530     // convenient methods //////////////////////////////////////////////////
531

532     /**
533      * Returns a GraphicsNode that represents an raster image in JPEG or PNG
534      * format.
535      *
536      * @param ctx the bridge context
537      * @param e the image element
538      * @param img the image to use in creating the graphics node
539      */

540     protected GraphicsNode createRasterImageNode(BridgeContext ctx,
541                                                  Element e,
542                                                  Filter img) {
543         Rectangle2D JavaDoc bounds = getImageBounds(ctx, e);
544         if ((bounds.getWidth() == 0) || (bounds.getHeight() == 0)) {
545             ShapeNode sn = new ShapeNode();
546             sn.setShape(bounds);
547             return sn;
548         }
549         Object JavaDoc obj = img.getProperty
550             (SVGBrokenLinkProvider.SVG_BROKEN_LINK_DOCUMENT_PROPERTY);
551         if ((obj != null) && (obj instanceof SVGDocument)) {
552             // Ok so we are dealing with a broken link.
553
SVGOMDocument doc = (SVGOMDocument)obj;
554             return createSVGImageNode(ctx, e, doc);
555         }
556
557         RasterImageNode node = new RasterImageNode();
558         node.setImage(img);
559         Rectangle2D JavaDoc imgBounds = img.getBounds2D();
560
561         // create the implicit viewBox for the raster image. The viewBox for a
562
// raster image is the size of the image
563
float [] vb = new float[4];
564         vb[0] = 0; // x
565
vb[1] = 0; // y
566
vb[2] = (float)imgBounds.getWidth(); // width
567
vb[3] = (float)imgBounds.getHeight(); // height
568

569         // handles the 'preserveAspectRatio', 'overflow' and 'clip' and sets the
570
// appropriate AffineTransform to the image node
571
initializeViewport(ctx, e, node, vb, bounds);
572
573         return node;
574     }
575
576     /**
577      * Returns a GraphicsNode that represents a svg document as an image.
578      *
579      * @param ctx the bridge context
580      * @param e the image element
581      * @param imgDocument the SVG document that represents the image
582      */

583     protected GraphicsNode createSVGImageNode(BridgeContext ctx,
584                                               Element e,
585                                               SVGDocument imgDocument) {
586         CSSEngine eng = ((SVGOMDocument)imgDocument).getCSSEngine();
587         if (eng != null) {
588             subCtx = (BridgeContext)eng.getCSSContext();
589         } else {
590             subCtx = new BridgeContext(ctx.getUserAgent(),
591                                        ctx.getDocumentLoader());
592             subCtx.setGVTBuilder(ctx.getGVTBuilder());
593             subCtx.setDocument(imgDocument);
594             subCtx.initializeDocument(imgDocument);
595         }
596
597         CompositeGraphicsNode result = new CompositeGraphicsNode();
598         // handles the 'preserveAspectRatio', 'overflow' and 'clip' and
599
// sets the appropriate AffineTransform to the image node
600
Rectangle2D JavaDoc bounds = getImageBounds(ctx, e);
601
602         if ((bounds.getWidth() == 0) || (bounds.getHeight() == 0)) {
603             ShapeNode sn = new ShapeNode();
604             sn.setShape(bounds);
605             result.getChildren().add(sn);
606             return result;
607         }
608
609         Rectangle2D JavaDoc r = CSSUtilities.convertEnableBackground(e);
610         if (r != null) {
611             result.setBackgroundEnable(r);
612         }
613
614         SVGSVGElement svgElement = imgDocument.getRootElement();
615         CanvasGraphicsNode node;
616         node = (CanvasGraphicsNode)subCtx.getGVTBuilder().build
617             (subCtx, svgElement);
618
619         if (eng == null) // If we "created" this document then add listerns.
620
subCtx.addUIEventListeners(imgDocument);
621
622         // HACK: remove the clip set by the SVGSVGElement as the overflow
623
// and clip properties must be ignored. The clip will be set later
624
// using the overflow and clip of the <image> element.
625
node.setClip(null);
626         // HACK: remove the viewingTransform set by the SVGSVGElement
627
// as the viewBox must be ignored. The viewingTransform will
628
// be set later using the width/height of the image element.
629
node.setViewingTransform(new AffineTransform JavaDoc());
630         result.getChildren().add(node);
631
632         // create the implicit viewBox for the SVG image. The viewBox for a
633
// SVG image is the viewBox of the outermost SVG element of the SVG file
634
String JavaDoc viewBox =
635             svgElement.getAttributeNS(null, SVG_VIEW_BOX_ATTRIBUTE);
636         float [] vb = ViewBox.parseViewBoxAttribute(e, viewBox);
637
638         initializeViewport(ctx, e, result, vb, bounds);
639
640         // add a listener on the outermost svg element of the SVG image.
641
// if an event occured inside the SVG image document, send it
642
// to the <image> element (inside the original document).
643
if (ctx.isInteractive()) {
644             listener = new ForwardEventListener(svgElement, e);
645             EventTarget JavaDoc tgt = (EventTarget JavaDoc)svgElement;
646
647             tgt.addEventListener(SVG_EVENT_CLICK, listener, false);
648             subCtx.storeEventListener(tgt, SVG_EVENT_CLICK, listener, false);
649
650             tgt.addEventListener(SVG_EVENT_KEYDOWN, listener, false);
651             subCtx.storeEventListener(tgt, SVG_EVENT_KEYDOWN, listener, false);
652
653             tgt.addEventListener(SVG_EVENT_KEYPRESS, listener, false);
654             subCtx.storeEventListener(tgt, SVG_EVENT_KEYPRESS, listener, false);
655
656             tgt.addEventListener(SVG_EVENT_KEYUP, listener, false);
657             subCtx.storeEventListener(tgt, SVG_EVENT_KEYUP, listener, false);
658
659             tgt.addEventListener(SVG_EVENT_MOUSEDOWN, listener, false);
660             subCtx.storeEventListener(tgt, SVG_EVENT_MOUSEDOWN, listener,false);
661
662             tgt.addEventListener(SVG_EVENT_MOUSEMOVE, listener, false);
663             subCtx.storeEventListener(tgt, SVG_EVENT_MOUSEMOVE, listener,false);
664
665             tgt.addEventListener(SVG_EVENT_MOUSEOUT, listener, false);
666             subCtx.storeEventListener(tgt, SVG_EVENT_MOUSEOUT, listener, false);
667
668             tgt.addEventListener(SVG_EVENT_MOUSEOVER, listener, false);
669             subCtx.storeEventListener(tgt, SVG_EVENT_MOUSEOVER, listener,false);
670
671             tgt.addEventListener(SVG_EVENT_MOUSEUP, listener, false);
672             subCtx.storeEventListener(tgt, SVG_EVENT_MOUSEUP, listener, false);
673         }
674
675         return result;
676     }
677
678     public void dispose() {
679         if ((imgDocument != null) && (listener != null)) {
680             EventTarget JavaDoc tgt = (EventTarget JavaDoc)imgDocument.getRootElement();
681
682             tgt.removeEventListener(SVG_EVENT_CLICK, listener, false);
683             tgt.removeEventListener(SVG_EVENT_KEYDOWN, listener, false);
684             tgt.removeEventListener(SVG_EVENT_KEYPRESS, listener, false);
685             tgt.removeEventListener(SVG_EVENT_KEYUP, listener, false);
686             tgt.removeEventListener(SVG_EVENT_MOUSEDOWN, listener, false);
687             tgt.removeEventListener(SVG_EVENT_MOUSEMOVE, listener, false);
688             tgt.removeEventListener(SVG_EVENT_MOUSEOUT, listener, false);
689             tgt.removeEventListener(SVG_EVENT_MOUSEOVER, listener, false);
690             tgt.removeEventListener(SVG_EVENT_MOUSEUP, listener, false);
691             listener = null;
692         }
693
694         if (imgDocument != null) {
695             SVGSVGElement svgElement = imgDocument.getRootElement();
696             disposeTree(svgElement);
697             imgDocument = null;
698             subCtx = null;
699         }
700         super.dispose();
701
702     }
703     /**
704      * A simple DOM listener to forward events from the SVG image document to
705      * the original document.
706      */

707     protected static class ForwardEventListener implements EventListener JavaDoc {
708
709         /**
710          * The root element of the SVG image.
711          */

712         protected Element svgElement;
713
714         /**
715          * The image element.
716          */

717         protected Element imgElement;
718
719         /**
720          * Constructs a new <tt>ForwardEventListener</tt>
721          */

722         public ForwardEventListener(Element svgElement, Element imgElement) {
723             this.svgElement = svgElement;
724             this.imgElement = imgElement;
725         }
726
727         public void handleEvent(Event JavaDoc e) {
728             MouseEvent JavaDoc evt = (MouseEvent JavaDoc) e;
729             MouseEvent JavaDoc newMouseEvent = (MouseEvent JavaDoc)
730                 // DOM Level 2 6.5 cast from Document to DocumentEvent is ok
731
((DocumentEvent JavaDoc)imgElement.getOwnerDocument()).createEvent("MouseEvents");
732
733             newMouseEvent.initMouseEvent(evt.getType(),
734                                          evt.getBubbles(),
735                                          evt.getCancelable(),
736                                          evt.getView(),
737                                          evt.getDetail(),
738                                          evt.getScreenX(),
739                                          evt.getScreenY(),
740                                          evt.getClientX(),
741                                          evt.getClientY(),
742                                          evt.getCtrlKey(),
743                                          evt.getAltKey(),
744                                          evt.getShiftKey(),
745                                          evt.getMetaKey(),
746                                          evt.getButton(),
747                                          (EventTarget JavaDoc)imgElement);
748             ((EventTarget JavaDoc)imgElement).dispatchEvent(newMouseEvent);
749         }
750     }
751
752     /**
753      * Initializes according to the specified element, the specified graphics
754      * node with the specified bounds. This method takes into account the
755      * 'viewBox', 'preserveAspectRatio', and 'clip' properties. According to
756      * those properties, a AffineTransform and a clip is set.
757      *
758      * @param ctx the bridge context
759      * @param e the image element that defines the properties
760      * @param node the graphics node
761      * @param vb the implicit viewBox definition
762      * @param bounds the bounds of the image element
763      */

764     protected static void initializeViewport(BridgeContext ctx,
765                                              Element e,
766                                              GraphicsNode node,
767                                              float [] vb,
768                                              Rectangle2D JavaDoc bounds) {
769
770         float x = (float)bounds.getX();
771         float y = (float)bounds.getY();
772         float w = (float)bounds.getWidth();
773         float h = (float)bounds.getHeight();
774
775         AffineTransform JavaDoc at
776             = ViewBox.getPreserveAspectRatioTransform(e, vb, w, h);
777         at.preConcatenate(AffineTransform.getTranslateInstance(x, y));
778         node.setTransform(at);
779
780         // 'overflow' and 'clip'
781
Shape JavaDoc clip = null;
782         if (CSSUtilities.convertOverflow(e)) { // overflow:hidden
783
float [] offsets = CSSUtilities.convertClip(e);
784             if (offsets == null) { // clip:auto
785
clip = new Rectangle2D.Float JavaDoc(x, y, w, h);
786             } else { // clip:rect(<x> <y> <w> <h>)
787
// offsets[0] = top
788
// offsets[1] = right
789
// offsets[2] = bottom
790
// offsets[3] = left
791
clip = new Rectangle2D.Float JavaDoc(x+offsets[3],
792                                              y+offsets[0],
793                                              w-offsets[1]-offsets[3],
794                                              h-offsets[2]-offsets[0]);
795             }
796         }
797
798         if (clip != null) {
799             try {
800                 at = at.createInverse(); // clip in user space
801
Filter filter = node.getGraphicsNodeRable(true);
802                 clip = at.createTransformedShape(clip);
803                 node.setClip(new ClipRable8Bit(filter, clip));
804             } catch (java.awt.geom.NoninvertibleTransformException JavaDoc ex) {}
805         }
806     }
807
808     /**
809      * Analyzes the color-profile property and builds an ICCColorSpaceExt
810      * object from it.
811      *
812      * @param element the element with the color-profile property
813      * @param ctx the bridge context
814      */

815     protected static ICCColorSpaceExt extractColorSpace(Element element,
816                                                         BridgeContext ctx) {
817
818         String JavaDoc colorProfileProperty = CSSUtilities.getComputedStyle
819             (element, SVGCSSEngine.COLOR_PROFILE_INDEX).getStringValue();
820
821         // The only cases that need special handling are 'sRGB' and 'name'
822
ICCColorSpaceExt colorSpace = null;
823         if (CSS_SRGB_VALUE.equalsIgnoreCase(colorProfileProperty)) {
824
825             colorSpace = new ICCColorSpaceExt
826                 (ICC_Profile.getInstance(ColorSpace.CS_sRGB),
827                  ICCColorSpaceExt.AUTO);
828
829         } else if (!CSS_AUTO_VALUE.equalsIgnoreCase(colorProfileProperty)
830                    && !"".equalsIgnoreCase(colorProfileProperty)){
831
832             // The value is neither 'sRGB' nor 'auto': it is a profile name.
833
SVGColorProfileElementBridge profileBridge =
834                 (SVGColorProfileElementBridge) ctx.getBridge
835                 (SVG_NAMESPACE_URI, SVG_COLOR_PROFILE_TAG);
836             if (profileBridge != null) {
837                 colorSpace = profileBridge.createICCColorSpaceExt
838                     (ctx, element, colorProfileProperty);
839
840             }
841         }
842         return colorSpace;
843     }
844
845     /**
846      * Returns the bounds of the specified image element.
847      *
848      * @param ctx the bridge context
849      * @param element the image element
850      */

851     protected static
852         Rectangle2D JavaDoc getImageBounds(BridgeContext ctx, Element element) {
853
854         UnitProcessor.Context uctx = UnitProcessor.createContext(ctx, element);
855
856         // 'x' attribute - default is 0
857
String JavaDoc s = element.getAttributeNS(null, SVG_X_ATTRIBUTE);
858         float x = 0;
859         if (s.length() != 0) {
860             x = UnitProcessor.svgHorizontalCoordinateToUserSpace
861                 (s, SVG_X_ATTRIBUTE, uctx);
862         }
863
864         // 'y' attribute - default is 0
865
s = element.getAttributeNS(null, SVG_Y_ATTRIBUTE);
866         float y = 0;
867         if (s.length() != 0) {
868             y = UnitProcessor.svgVerticalCoordinateToUserSpace
869                 (s, SVG_Y_ATTRIBUTE, uctx);
870         }
871
872         // 'width' attribute - required
873
s = element.getAttributeNS(null, SVG_WIDTH_ATTRIBUTE);
874         float w;
875         if (s.length() == 0) {
876             throw new BridgeException(element, ERR_ATTRIBUTE_MISSING,
877                                       new Object JavaDoc[] {SVG_WIDTH_ATTRIBUTE});
878         } else {
879             w = UnitProcessor.svgHorizontalLengthToUserSpace
880                 (s, SVG_WIDTH_ATTRIBUTE, uctx);
881         }
882
883         // 'height' attribute - required
884
s = element.getAttributeNS(null, SVG_HEIGHT_ATTRIBUTE);
885         float h;
886         if (s.length() == 0) {
887             throw new BridgeException(element, ERR_ATTRIBUTE_MISSING,
888                                       new Object JavaDoc[] {SVG_HEIGHT_ATTRIBUTE});
889         } else {
890             h = UnitProcessor.svgVerticalLengthToUserSpace
891                 (s, SVG_HEIGHT_ATTRIBUTE, uctx);
892         }
893
894         return new Rectangle2D.Float JavaDoc(x, y, w, h);
895     }
896
897     GraphicsNode createBrokenImageNode
898         (BridgeContext ctx, Element e, String JavaDoc uri) {
899         
900         String JavaDoc lname = "<Unknown Element>";
901         SVGDocument doc = null;
902         if (e != null) {
903             doc = (SVGDocument)e.getOwnerDocument();
904             lname = e.getLocalName();
905         }
906         String JavaDoc docUri;
907         if (doc == null) docUri = "<Unknown Document>";
908         else docUri = doc.getURL();
909         int line = ctx.getDocumentLoader().getLineNumber(e);
910         Object JavaDoc [] fullparams = new Object JavaDoc[4];
911         fullparams[0] = docUri;
912         fullparams[1] = new Integer JavaDoc(line);
913         fullparams[2] = lname;
914         fullparams[3] = uri;
915
916         SVGDocument blDoc = brokenLinkProvider.getBrokenLinkDocument
917             (this, ERR_URI_IO, fullparams);
918         hitCheckChildren = true;
919         return createSVGImageNode(ctx, e, blDoc);
920     }
921
922
923     static SVGBrokenLinkProvider brokenLinkProvider
924         = new SVGBrokenLinkProvider();
925     static {
926         ImageTagRegistry.setBrokenLinkProvider(brokenLinkProvider);
927     }
928 }
929
Popular Tags