KickJava   Java API By Example, From Geeks To Geeks.

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


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.geom.AffineTransform JavaDoc;
21 import java.awt.geom.Point2D JavaDoc;
22 import java.awt.geom.Rectangle2D JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.LinkedList JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.StringTokenizer JavaDoc;
28
29 import org.apache.batik.css.engine.CSSEngine;
30 import org.apache.batik.dom.svg.SVGOMDocument;
31 import org.apache.batik.dom.util.XLinkSupport;
32 import org.apache.batik.dom.util.XMLSupport;
33 import org.apache.batik.gvt.GraphicsNode;
34 import org.apache.batik.parser.AWTTransformProducer;
35 import org.apache.batik.parser.ParseException;
36 import org.apache.batik.util.ParsedURL;
37 import org.apache.batik.util.SVG12Constants;
38 import org.apache.batik.util.SVGConstants;
39 import org.w3c.dom.Element JavaDoc;
40 import org.w3c.dom.Node JavaDoc;
41 import org.w3c.dom.svg.SVGDocument;
42 import org.w3c.dom.svg.SVGElement;
43 import org.w3c.dom.svg.SVGLangSpace;
44 import org.w3c.dom.svg.SVGNumberList;
45
46 /**
47  * A collection of utility methods for SVG.
48  *
49  * @author <a HREF="mailto:tkormann@apache.org">Thierry Kormann</a>
50  * @author <a HREF="mailto:stephane@hillion.org">Stephane Hillion</a>
51  * @version $Id: SVGUtilities.java,v 1.32 2005/03/27 08:58:30 cam Exp $
52  */

53 public abstract class SVGUtilities implements SVGConstants, ErrorConstants {
54
55     /**
56      * No instance of this class is required.
57      */

58     protected SVGUtilities() {}
59
60     ////////////////////////////////////////////////////////////////////////
61
// common methods
62
////////////////////////////////////////////////////////////////////////
63

64     /**
65      * Returns the node imported by the given node, or null.
66      */

67     public static Node getImportedChild(Node n) {
68         return CSSEngine.getImportedChild(n);
69     }
70
71     /**
72      * Returns the logical parent element of the given element.
73      * The parent element of a used element is the &lt;use> element
74      * which reference it.
75      */

76     public static Element JavaDoc getParentElement(Element JavaDoc elt) {
77         return CSSEngine.getParentElement(elt);
78     }
79
80     /**
81      * Converts an SVGNumberList into a float array.
82      * @param l the list to convert
83      */

84     public static float[] convertSVGNumberList(SVGNumberList l) {
85         int n = l.getNumberOfItems();
86         if (n == 0) {
87             return null;
88         }
89         float fl[] = new float[n];
90         for (int i=0; i < n; i++) {
91             fl[i] = l.getItem(i).getValue();
92         }
93         return fl;
94     }
95
96     /**
97      * Converts a string into a float.
98      * @param s the float representation to convert
99      */

100     public static float convertSVGNumber(String JavaDoc s) {
101         return Float.parseFloat(s);
102     }
103
104     /**
105      * Converts a string into an integer.
106      * @param s the integer representation to convert
107      */

108     public static int convertSVGInteger(String JavaDoc s) {
109         return Integer.parseInt(s);
110     }
111
112     /**
113      * Converts the specified ratio to float number.
114      * @param v the ratio value to convert
115      * @exception NumberFormatException if the ratio is not a valid
116      * number or percentage
117      */

118     public static float convertRatio(String JavaDoc v) {
119         float d = 1;
120         if (v.endsWith("%")) {
121             v = v.substring(0, v.length() - 1);
122             d = 100;
123         }
124         float r = Float.parseFloat(v)/d;
125         if (r < 0) {
126             r = 0;
127         } else if (r > 1) {
128             r = 1;
129         }
130         return r;
131     }
132
133     /**
134      * Returns the content of the 'desc' child of the given element.
135      */

136     public static String JavaDoc getDescription(SVGElement elt) {
137         String JavaDoc result = "";
138         boolean preserve = false;
139         Node n = elt.getFirstChild();
140         if (n != null && n.getNodeType() == Node.ELEMENT_NODE) {
141             String JavaDoc name =
142                 (n.getPrefix() == null) ? n.getNodeName() : n.getLocalName();
143             if (name.equals(SVG_DESC_TAG)) {
144                 preserve = ((SVGLangSpace)n).getXMLspace().equals
145                     (SVG_PRESERVE_VALUE);
146                 for (n = n.getFirstChild();
147                      n != null;
148                      n = n.getNextSibling()) {
149                     if (n.getNodeType() == Node.TEXT_NODE) {
150                         result += n.getNodeValue();
151                     }
152                 }
153             }
154         }
155         return (preserve)
156             ? XMLSupport.preserveXMLSpace(result)
157             : XMLSupport.defaultXMLSpace(result);
158     }
159
160     /**
161      * Tests whether or not the given element match a specified user agent.
162      *
163      * @param elt the element to check
164      * @param ua the user agent
165      */

166     public static boolean matchUserAgent(Element JavaDoc elt, UserAgent ua) {
167         test: if (elt.hasAttributeNS(null, SVG_SYSTEM_LANGUAGE_ATTRIBUTE)) {
168             // Tests the system languages.
169
String JavaDoc sl = elt.getAttributeNS(null,
170                                            SVG_SYSTEM_LANGUAGE_ATTRIBUTE);
171             if (sl.length() == 0) // SVG spec says empty returns false
172
return false;
173             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(sl, ", ");
174             while (st.hasMoreTokens()) {
175                 String JavaDoc s = st.nextToken();
176                 if (matchUserLanguage(s, ua.getLanguages())) {
177                     break test;
178                 }
179             }
180             return false;
181         }
182         if (elt.hasAttributeNS(null, SVG_REQUIRED_FEATURES_ATTRIBUTE)) {
183             // Tests the system features.
184
String JavaDoc rf = elt.getAttributeNS(null,
185                                            SVG_REQUIRED_FEATURES_ATTRIBUTE);
186             if (rf.length() == 0) // SVG spec says empty returns false
187
return false;
188             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(rf, " ");
189             while (st.hasMoreTokens()) {
190                 String JavaDoc s = st.nextToken();
191                 if (!ua.hasFeature(s)) {
192                     return false;
193                 }
194             }
195         }
196         if (elt.hasAttributeNS(null, SVG_REQUIRED_EXTENSIONS_ATTRIBUTE)) {
197             // Tests the system features.
198
String JavaDoc re = elt.getAttributeNS(null,
199                                            SVG_REQUIRED_EXTENSIONS_ATTRIBUTE);
200             if (re.length() == 0) // SVG spec says empty returns false
201
return false;
202             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(re, " ");
203             while (st.hasMoreTokens()) {
204                 String JavaDoc s = st.nextToken();
205                 if (!ua.supportExtension(s)) {
206                     return false;
207                 }
208             }
209         }
210         return true;
211     }
212
213     /**
214      * Tests whether or not the specified language specification matches
215      * the user preferences.
216      *
217      * @param s the langage to check
218      * @param userLanguages the user langages
219      */

220     protected static boolean matchUserLanguage(String JavaDoc s,
221                                                String JavaDoc userLanguages) {
222         StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(userLanguages, ", ");
223         while (st.hasMoreTokens()) {
224             String JavaDoc t = st.nextToken();
225             if (s.startsWith(t)) {
226                 if (s.length() > t.length()) {
227                     return (s.charAt(t.length()) == '-');
228                 }
229                 return true;
230             }
231         }
232         return false;
233     }
234
235     /**
236      * Returns the value of the specified attribute specified on the
237      * specified element or one of its ancestor. Ancestors are found
238      * using the xlink:href attribute.
239      *
240      * @param element the element to start with
241      * @param namespaceURI the namespace URI of the attribute to return
242      * @param attrName the name of the attribute to search
243      * @param ctx the bridge context
244      * @return the value of the attribute or an empty string if not defined
245      */

246     public static String JavaDoc getChainableAttributeNS(Element JavaDoc element,
247                                                  String JavaDoc namespaceURI,
248                                                  String JavaDoc attrName,
249                                                  BridgeContext ctx) {
250
251         DocumentLoader loader = ctx.getDocumentLoader();
252         Element JavaDoc e = element;
253         List JavaDoc refs = new LinkedList JavaDoc();
254         for (;;) {
255             String JavaDoc v = e.getAttributeNS(namespaceURI, attrName);
256             if (v.length() > 0) { // exit if attribute defined
257
return v;
258             }
259             String JavaDoc uriStr = XLinkSupport.getXLinkHref(e);
260             if (uriStr.length() == 0) { // exit if no more xlink:href
261
return "";
262             }
263             SVGDocument svgDoc = (SVGDocument)e.getOwnerDocument();
264             String JavaDoc baseURI = ((SVGOMDocument)svgDoc).getURL();
265
266             ParsedURL purl = new ParsedURL(baseURI, uriStr);
267             if (!purl.complete())
268                 throw new BridgeException(e, ERR_URI_MALFORMED,
269                                           new Object JavaDoc[] {uriStr});
270
271             Iterator JavaDoc iter = refs.iterator();
272             while (iter.hasNext()) {
273                 if (purl.equals(iter.next()))
274                     throw new BridgeException
275                         (e, ERR_XLINK_HREF_CIRCULAR_DEPENDENCIES,
276                          new Object JavaDoc[] {uriStr});
277             }
278
279             try {
280                 URIResolver resolver = new URIResolver(svgDoc, loader);
281                 e = resolver.getElement(purl.toString(), e);
282                 refs.add(purl);
283             } catch(IOException JavaDoc ex) {
284                 throw new BridgeException(e, ERR_URI_IO,
285                                           new Object JavaDoc[] {uriStr});
286             } catch(SecurityException JavaDoc ex) {
287                 throw new BridgeException(e, ERR_URI_UNSECURE,
288                                           new Object JavaDoc[] {uriStr});
289             }
290         }
291     }
292
293     /////////////////////////////////////////////////////////////////////////
294
// <linearGradient> and <radialGradient>
295
/////////////////////////////////////////////////////////////////////////
296

297     /**
298      * Returns a Point2D in user units according to the specified parameters.
299      *
300      * @param xStr the x coordinate
301      * @param xAttr the name of the attribute that represents the x coordinate
302      * @param yStr the y coordinate
303      * @param yAttr the name of the attribute that represents the y coordinate
304      * @param unitsType the coordinate system (OBJECT_BOUNDING_BOX |
305      * USER_SPACE_ON_USE)
306      * @param uctx the unit processor context
307      */

308     public static Point2D JavaDoc convertPoint(String JavaDoc xStr,
309                                        String JavaDoc xAttr,
310                                        String JavaDoc yStr,
311                                        String JavaDoc yAttr,
312                                        short unitsType,
313                                        UnitProcessor.Context uctx) {
314         float x, y;
315         switch (unitsType) {
316         case OBJECT_BOUNDING_BOX:
317             x = UnitProcessor.svgHorizontalCoordinateToObjectBoundingBox
318                 (xStr, xAttr, uctx);
319             y = UnitProcessor.svgVerticalCoordinateToObjectBoundingBox
320                 (yStr, yAttr, uctx);
321             break;
322         case USER_SPACE_ON_USE:
323             x = UnitProcessor.svgHorizontalCoordinateToUserSpace
324                 (xStr, xAttr, uctx);
325             y = UnitProcessor.svgVerticalCoordinateToUserSpace
326                 (yStr, yAttr, uctx);
327             break;
328         default:
329             throw new Error JavaDoc(); // can't be reached
330
}
331         return new Point2D.Float JavaDoc(x, y);
332     }
333
334     /**
335      * Returns a float in user units according to the specified parameters.
336      *
337      * @param length the length
338      * @param attr the name of the attribute that represents the length
339      * @param unitsType the coordinate system (OBJECT_BOUNDING_BOX |
340      * USER_SPACE_ON_USE)
341      * @param uctx the unit processor context
342      */

343     public static float convertLength(String JavaDoc length,
344                                       String JavaDoc attr,
345                                       short unitsType,
346                                       UnitProcessor.Context uctx) {
347         switch (unitsType) {
348         case OBJECT_BOUNDING_BOX:
349             return UnitProcessor.svgOtherLengthToObjectBoundingBox
350                 (length, attr, uctx);
351         case USER_SPACE_ON_USE:
352             return UnitProcessor.svgOtherLengthToUserSpace(length, attr, uctx);
353         default:
354             throw new Error JavaDoc(); // can't be reached
355
}
356     }
357
358     /////////////////////////////////////////////////////////////////////////
359
// <mask> region
360
/////////////////////////////////////////////////////////////////////////
361

362     /**
363      * Returns the mask region according to the x, y, width, height,
364      * and maskUnits attributes.
365      *
366      * @param maskElement the mask element that defines the various attributes
367      * @param maskedElement the element referencing the mask
368      * @param maskedNode the graphics node to mask (objectBoundingBox)
369      * @param ctx the bridge context
370      */

371     public static Rectangle2D JavaDoc convertMaskRegion(Element JavaDoc maskElement,
372                                                 Element JavaDoc maskedElement,
373                                                 GraphicsNode maskedNode,
374                                                 BridgeContext ctx) {
375
376         // 'x' attribute - default is -10%
377
String JavaDoc xStr = maskElement.getAttributeNS(null, SVG_X_ATTRIBUTE);
378         if (xStr.length() == 0) {
379             xStr = SVG_MASK_X_DEFAULT_VALUE;
380         }
381         // 'y' attribute - default is -10%
382
String JavaDoc yStr = maskElement.getAttributeNS(null, SVG_Y_ATTRIBUTE);
383         if (yStr.length() == 0) {
384             yStr = SVG_MASK_Y_DEFAULT_VALUE;
385         }
386         // 'width' attribute - default is 120%
387
String JavaDoc wStr = maskElement.getAttributeNS(null, SVG_WIDTH_ATTRIBUTE);
388         if (wStr.length() == 0) {
389             wStr = SVG_MASK_WIDTH_DEFAULT_VALUE;
390         }
391         // 'height' attribute - default is 120%
392
String JavaDoc hStr = maskElement.getAttributeNS(null, SVG_HEIGHT_ATTRIBUTE);
393         if (hStr.length() == 0) {
394             hStr = SVG_MASK_HEIGHT_DEFAULT_VALUE;
395         }
396         // 'maskUnits' attribute - default is 'objectBoundingBox'
397
short unitsType;
398         String JavaDoc units =
399             maskElement.getAttributeNS(null, SVG_MASK_UNITS_ATTRIBUTE);
400         if (units.length() == 0) {
401             unitsType = OBJECT_BOUNDING_BOX;
402         } else {
403             unitsType = parseCoordinateSystem
404                 (maskElement, SVG_MASK_UNITS_ATTRIBUTE, units);
405         }
406
407         // resolve units in the (referenced) maskedElement's coordinate system
408
UnitProcessor.Context uctx
409             = UnitProcessor.createContext(ctx, maskedElement);
410
411         return convertRegion(xStr,
412                              yStr,
413                              wStr,
414                              hStr,
415                              unitsType,
416                              maskedNode,
417                              uctx);
418     }
419
420     /////////////////////////////////////////////////////////////////////////
421
// <pattern> region
422
/////////////////////////////////////////////////////////////////////////
423

424     /**
425      * Returns the pattern region according to the x, y, width, height,
426      * and patternUnits attributes.
427      *
428      * @param patternElement the pattern element that defines the attributes
429      * @param paintedElement the element referencing the pattern
430      * @param paintedNode the graphics node to paint (objectBoundingBox)
431      * @param ctx the bridge context
432      */

433     public static Rectangle2D JavaDoc convertPatternRegion(Element JavaDoc patternElement,
434                                                    Element JavaDoc paintedElement,
435                                                    GraphicsNode paintedNode,
436                                                    BridgeContext ctx) {
437
438         // 'x' attribute - default is 0%
439
String JavaDoc xStr = getChainableAttributeNS
440             (patternElement, null, SVG_X_ATTRIBUTE, ctx);
441         if (xStr.length() == 0) {
442             xStr = SVG_PATTERN_X_DEFAULT_VALUE;
443         }
444         // 'y' attribute - default is 0%
445
String JavaDoc yStr = getChainableAttributeNS
446             (patternElement, null, SVG_Y_ATTRIBUTE, ctx);
447         if (yStr.length() == 0) {
448             yStr = SVG_PATTERN_Y_DEFAULT_VALUE;
449         }
450         // 'width' attribute - required
451
String JavaDoc wStr = getChainableAttributeNS
452             (patternElement, null, SVG_WIDTH_ATTRIBUTE, ctx);
453         if (wStr.length() == 0) {
454             throw new BridgeException(patternElement, ERR_ATTRIBUTE_MISSING,
455                                       new Object JavaDoc[] {SVG_WIDTH_ATTRIBUTE});
456         }
457         // 'height' attribute - required
458
String JavaDoc hStr = getChainableAttributeNS
459             (patternElement, null, SVG_HEIGHT_ATTRIBUTE, ctx);
460         if (hStr.length() == 0) {
461             throw new BridgeException(patternElement, ERR_ATTRIBUTE_MISSING,
462                                       new Object JavaDoc[] {SVG_HEIGHT_ATTRIBUTE});
463         }
464         // 'patternUnits' attribute - default is 'objectBoundingBox'
465
short unitsType;
466         String JavaDoc units = getChainableAttributeNS
467             (patternElement, null, SVG_PATTERN_UNITS_ATTRIBUTE, ctx);
468         if (units.length() == 0) {
469             unitsType = OBJECT_BOUNDING_BOX;
470         } else {
471             unitsType = parseCoordinateSystem
472                 (patternElement, SVG_PATTERN_UNITS_ATTRIBUTE, units);
473         }
474
475         // resolve units in the (referenced) paintedElement's coordinate system
476
UnitProcessor.Context uctx
477             = UnitProcessor.createContext(ctx, paintedElement);
478
479         return convertRegion(xStr,
480                              yStr,
481                              wStr,
482                              hStr,
483                              unitsType,
484                              paintedNode,
485                              uctx);
486     }
487
488     /////////////////////////////////////////////////////////////////////////
489
// <filter> and filter primitive
490
/////////////////////////////////////////////////////////////////////////
491

492     /**
493      * Returns an array of 2 float numbers that describes the filter
494      * resolution of the specified filter element.
495      *
496      * @param filterElement the filter element
497      * @param ctx the bridge context
498      */

499     public static
500         float [] convertFilterRes(Element JavaDoc filterElement, BridgeContext ctx) {
501
502         float [] filterRes = new float[2];
503         String JavaDoc s = getChainableAttributeNS
504             (filterElement, null, SVG_FILTER_RES_ATTRIBUTE, ctx);
505         Float JavaDoc [] vals = convertSVGNumberOptionalNumber
506             (filterElement, SVG_FILTER_RES_ATTRIBUTE, s);
507
508         if (filterRes[0] < 0 || filterRes[1] < 0) {
509             throw new BridgeException
510                 (filterElement, ERR_ATTRIBUTE_VALUE_MALFORMED,
511                  new Object JavaDoc[] {SVG_FILTER_RES_ATTRIBUTE, s});
512         }
513         
514         if (vals[0] == null)
515             filterRes[0] = -1;
516         else {
517             filterRes[0] = vals[0].floatValue();
518             if (filterRes[0] < 0)
519                 throw new BridgeException
520                     (filterElement, ERR_ATTRIBUTE_VALUE_MALFORMED,
521                      new Object JavaDoc[] {SVG_FILTER_RES_ATTRIBUTE, s});
522         }
523
524         if (vals[1] == null)
525             filterRes[1] = filterRes[0];
526         else {
527             filterRes[1] = vals[1].floatValue();
528             if (filterRes[1] < 0)
529                 throw new BridgeException
530                     (filterElement, ERR_ATTRIBUTE_VALUE_MALFORMED,
531                      new Object JavaDoc[] {SVG_FILTER_RES_ATTRIBUTE, s});
532         }
533         return filterRes;
534     }
535
536     /**
537      * This function parses attrValue for a number followed by an optional
538      * second Number. It always returns an array of two Floats. If either
539      * or both values are not provided the entries are set to null
540      */

541     public static Float JavaDoc []
542         convertSVGNumberOptionalNumber(Element JavaDoc elem,
543                                        String JavaDoc attrName,
544                                        String JavaDoc attrValue) {
545
546         Float JavaDoc [] ret = new Float JavaDoc[2];
547         if (attrValue.length() == 0)
548             return ret;
549
550         try {
551             StringTokenizer JavaDoc tokens = new StringTokenizer JavaDoc(attrValue, " ");
552             ret[0] = new Float JavaDoc(Float.parseFloat(tokens.nextToken()));
553             if (tokens.hasMoreTokens()) {
554                 ret[1] = new Float JavaDoc(Float.parseFloat(tokens.nextToken()));
555             }
556
557             if (tokens.hasMoreTokens()) {
558                 throw new BridgeException
559                     (elem, ERR_ATTRIBUTE_VALUE_MALFORMED,
560                      new Object JavaDoc[] {attrName, attrValue});
561             }
562         } catch (NumberFormatException JavaDoc ex) {
563             throw new BridgeException
564                 (elem, ERR_ATTRIBUTE_VALUE_MALFORMED,
565                  new Object JavaDoc[] {attrName, attrValue, ex});
566         }
567         return ret;
568     }
569
570
571    /**
572     * Returns the filter region according to the x, y, width, height,
573     * dx, dy, dw, dh and filterUnits attributes.
574     *
575     * @param filterElement the filter element that defines the attributes
576     * @param filteredElement the element referencing the filter
577     * @param filteredNode the graphics node to filter (objectBoundingBox)
578     * @param ctx the bridge context
579     */

580    public static
581        Rectangle2D JavaDoc convertFilterChainRegion(Element JavaDoc filterElement,
582                                             Element JavaDoc filteredElement,
583                                             GraphicsNode filteredNode,
584                                             BridgeContext ctx) {
585
586        // 'x' attribute - default is -10%
587
String JavaDoc xStr = getChainableAttributeNS
588            (filterElement, null, SVG_X_ATTRIBUTE, ctx);
589        if (xStr.length() == 0) {
590            xStr = SVG_FILTER_X_DEFAULT_VALUE;
591        }
592        // 'y' attribute - default is -10%
593
String JavaDoc yStr = getChainableAttributeNS
594            (filterElement, null, SVG_Y_ATTRIBUTE, ctx);
595        if (yStr.length() == 0) {
596            yStr = SVG_FILTER_Y_DEFAULT_VALUE;
597        }
598        // 'width' attribute - default is 120%
599
String JavaDoc wStr = getChainableAttributeNS
600            (filterElement, null, SVG_WIDTH_ATTRIBUTE, ctx);
601        if (wStr.length() == 0) {
602            wStr = SVG_FILTER_WIDTH_DEFAULT_VALUE;
603        }
604        // 'height' attribute - default is 120%
605
String JavaDoc hStr = getChainableAttributeNS
606            (filterElement, null, SVG_HEIGHT_ATTRIBUTE, ctx);
607        if (hStr.length() == 0) {
608            hStr = SVG_FILTER_HEIGHT_DEFAULT_VALUE;
609        }
610        // 'filterUnits' attribute - default is 'objectBoundingBox'
611
short unitsType;
612        String JavaDoc units = getChainableAttributeNS
613            (filterElement, null, SVG_FILTER_UNITS_ATTRIBUTE, ctx);
614        if (units.length() == 0) {
615            unitsType = OBJECT_BOUNDING_BOX;
616        } else {
617            unitsType = parseCoordinateSystem
618                (filterElement, SVG_FILTER_UNITS_ATTRIBUTE, units);
619        }
620
621        // resolve units in the (referenced) filteredElement's
622
// coordinate system
623
UnitProcessor.Context uctx
624            = UnitProcessor.createContext(ctx, filteredElement);
625
626        Rectangle2D JavaDoc region = convertRegion(xStr,
627                                           yStr,
628                                           wStr,
629                                           hStr,
630                                           unitsType,
631                                           filteredNode,
632                                           uctx);
633        //
634
// Account for region padding
635
//
636
units = getChainableAttributeNS
637            (filterElement, null,
638             SVG12Constants.SVG_FILTER_MARGINS_UNITS_ATTRIBUTE, ctx);
639        if (units.length() == 0) {
640            // Default to user space on use for margins, not objectBoundingBox
641
unitsType = USER_SPACE_ON_USE;
642        } else {
643            unitsType = parseCoordinateSystem
644                (filterElement,
645                 SVG12Constants.SVG_FILTER_MARGINS_UNITS_ATTRIBUTE, units);
646        }
647
648        // 'batik:dx' attribute - default is 0
649
String JavaDoc dxStr = filterElement.getAttributeNS(null,
650                                                    SVG12Constants.SVG_MX_ATRIBUTE);
651        if (dxStr.length() == 0) {
652            dxStr = SVG12Constants.SVG_FILTER_MX_DEFAULT_VALUE;
653        }
654        // 'batik:dy' attribute - default is 0
655
String JavaDoc dyStr = filterElement.getAttributeNS(null, SVG12Constants.SVG_MY_ATRIBUTE);
656        if (dyStr.length() == 0) {
657            dyStr = SVG12Constants.SVG_FILTER_MY_DEFAULT_VALUE;
658        }
659        // 'batik:dw' attribute - default is 0
660
String JavaDoc dwStr = filterElement.getAttributeNS(null, SVG12Constants.SVG_MW_ATRIBUTE);
661        if (dwStr.length() == 0) {
662            dwStr = SVG12Constants.SVG_FILTER_MW_DEFAULT_VALUE;
663        }
664        // 'batik:dh' attribute - default is 0
665
String JavaDoc dhStr = filterElement.getAttributeNS(null, SVG12Constants.SVG_MH_ATRIBUTE);
666        if (dhStr.length() == 0) {
667            dhStr = SVG12Constants.SVG_FILTER_MH_DEFAULT_VALUE;
668        }
669        
670        return extendRegion(dxStr,
671                            dyStr,
672                            dwStr,
673                            dhStr,
674                            unitsType,
675                            filteredNode,
676                            region,
677                            uctx);
678    }
679     
680    /**
681     * Returns a rectangle that represents the region extended by the
682     * specified differential coordinates.
683     *
684     * @param dxStr the differential x coordinate of the region
685     * @param dyStr the differential y coordinate of the region
686     * @param dwStr the differential width of the region
687     * @param dhStr the differential height of the region
688     * @param unitsType specifies whether the values are in userSpaceOnUse
689     * or objectBoundingBox space
690     * @param region the region to extend
691     * @param uctx the unit processor context (needed for userSpaceOnUse)
692     */

693     protected static Rectangle2D JavaDoc extendRegion(String JavaDoc dxStr,
694                                               String JavaDoc dyStr,
695                                               String JavaDoc dwStr,
696                                               String JavaDoc dhStr,
697                                               short unitsType,
698                                               GraphicsNode filteredNode,
699                                               Rectangle2D JavaDoc region,
700                                               UnitProcessor.Context uctx) {
701         
702         float dx,dy,dw,dh;
703         switch (unitsType) {
704         case USER_SPACE_ON_USE:
705             dx = UnitProcessor.svgHorizontalCoordinateToUserSpace
706                 (dxStr, SVG12Constants.SVG_MX_ATRIBUTE, uctx);
707             dy = UnitProcessor.svgVerticalCoordinateToUserSpace
708                 (dyStr, SVG12Constants.SVG_MY_ATRIBUTE, uctx);
709             dw = UnitProcessor.svgHorizontalCoordinateToUserSpace
710                 (dwStr, SVG12Constants.SVG_MW_ATRIBUTE, uctx);
711             dh = UnitProcessor.svgVerticalCoordinateToUserSpace
712                 (dhStr, SVG12Constants.SVG_MH_ATRIBUTE, uctx);
713             break;
714         case OBJECT_BOUNDING_BOX:
715             Rectangle2D JavaDoc bounds = filteredNode.getGeometryBounds();
716             if (bounds == null) {
717                 dx = dy = dw = dh = 0;
718             } else {
719                 dx = UnitProcessor.svgHorizontalCoordinateToObjectBoundingBox
720                     (dxStr, SVG12Constants.SVG_MX_ATRIBUTE, uctx);
721                 dx *= bounds.getWidth();
722
723                 dy = UnitProcessor.svgVerticalCoordinateToObjectBoundingBox
724                     (dyStr, SVG12Constants.SVG_MY_ATRIBUTE, uctx);
725                 dy *= bounds.getHeight();
726
727                 dw = UnitProcessor.svgHorizontalCoordinateToObjectBoundingBox
728                     (dwStr, SVG12Constants.SVG_MW_ATRIBUTE, uctx);
729                 dw *= bounds.getWidth();
730
731                 dh = UnitProcessor.svgVerticalCoordinateToObjectBoundingBox
732                     (dhStr, SVG12Constants.SVG_MH_ATRIBUTE, uctx);
733                 dh *= bounds.getHeight();
734             }
735             break;
736         default:
737             throw new Error JavaDoc(); // can't be reached
738
}
739         
740         region.setRect(region.getX() + dx,
741                        region.getY() + dy,
742                        region.getWidth() + dw,
743                        region.getHeight() + dh);
744
745         return region;
746     }
747
748     /**
749      * Returns the filter primitive region according to the x, y,
750      * width, height, and filterUnits attributes. Processing the
751      * element as the top one in the filter chain.
752      *
753      * @param filterPrimitiveElement the filter primitive element
754      * @param filteredElement the element referencing the filter
755      * @param filteredNode the graphics node to use (objectBoundingBox)
756      * @param defaultRegion the default region to filter
757      * @param filterRegion the filter chain region
758      * @param ctx the bridge context
759      */

760     public static Rectangle2D JavaDoc
761         convertFilterPrimitiveRegion(Element JavaDoc filterPrimitiveElement,
762                                      Element JavaDoc filteredElement,
763                                      GraphicsNode filteredNode,
764                                      Rectangle2D JavaDoc defaultRegion,
765                                      Rectangle2D JavaDoc filterRegion,
766                                      BridgeContext ctx) {
767
768         // 'primitiveUnits' - default is userSpaceOnUse
769
Node parentNode = filterPrimitiveElement.getParentNode();
770         String JavaDoc units = "";
771         if ((parentNode != null) &&
772             (parentNode.getNodeType() == Node.ELEMENT_NODE)) {
773             Element JavaDoc parent = (Element JavaDoc)parentNode;
774             units = getChainableAttributeNS(parent,
775                                             null,
776                                             SVG_PRIMITIVE_UNITS_ATTRIBUTE,
777                                             ctx);
778         }
779         short unitsType;
780         if (units.length() == 0) {
781             unitsType = USER_SPACE_ON_USE;
782         } else {
783             unitsType = parseCoordinateSystem
784                 (filterPrimitiveElement, SVG_FILTER_UNITS_ATTRIBUTE, units);
785         }
786
787         // 'x' attribute - default is defaultRegion.getX()
788
String JavaDoc xStr =
789             filterPrimitiveElement.getAttributeNS(null, SVG_X_ATTRIBUTE);
790
791         // 'y' attribute - default is defaultRegion.getY()
792
String JavaDoc yStr =
793             filterPrimitiveElement.getAttributeNS(null, SVG_Y_ATTRIBUTE);
794
795         // 'width' attribute - default is defaultRegion.getWidth()
796
String JavaDoc wStr =
797             filterPrimitiveElement.getAttributeNS(null, SVG_WIDTH_ATTRIBUTE);
798
799         // 'height' attribute - default is defaultRegion.getHeight()
800
String JavaDoc hStr =
801             filterPrimitiveElement.getAttributeNS(null, SVG_HEIGHT_ATTRIBUTE);
802
803         double x = defaultRegion.getX();
804         double y = defaultRegion.getY();
805         double w = defaultRegion.getWidth();
806         double h = defaultRegion.getHeight();
807
808         // resolve units in the (referenced) filteredElement's coordinate system
809
UnitProcessor.Context uctx
810             = UnitProcessor.createContext(ctx, filteredElement);
811
812         switch (unitsType) {
813         case OBJECT_BOUNDING_BOX:
814             Rectangle2D JavaDoc bounds = filteredNode.getGeometryBounds();
815             if (bounds != null) {
816                 if (xStr.length() != 0) {
817                     x = UnitProcessor.svgHorizontalCoordinateToObjectBoundingBox
818                         (xStr, SVG_X_ATTRIBUTE, uctx);
819                     x = bounds.getX() + x*bounds.getWidth();
820                 }
821                 if (yStr.length() != 0) {
822                     y = UnitProcessor.svgVerticalCoordinateToObjectBoundingBox
823                         (yStr, SVG_Y_ATTRIBUTE, uctx);
824                     y = bounds.getY() + y*bounds.getHeight();
825                 }
826                 if (wStr.length() != 0) {
827                     w = UnitProcessor.svgHorizontalLengthToObjectBoundingBox
828                         (wStr, SVG_WIDTH_ATTRIBUTE, uctx);
829                     w *= bounds.getWidth();
830                 }
831                 if (hStr.length() != 0) {
832                     h = UnitProcessor.svgVerticalLengthToObjectBoundingBox
833                         (hStr, SVG_HEIGHT_ATTRIBUTE, uctx);
834                     h *= bounds.getHeight();
835                 }
836             }
837             break;
838         case USER_SPACE_ON_USE:
839             if (xStr.length() != 0) {
840                 x = UnitProcessor.svgHorizontalCoordinateToUserSpace
841                     (xStr, SVG_X_ATTRIBUTE, uctx);
842             }
843             if (yStr.length() != 0) {
844                 y = UnitProcessor.svgVerticalCoordinateToUserSpace
845                     (yStr, SVG_Y_ATTRIBUTE, uctx);
846             }
847             if (wStr.length() != 0) {
848                 w = UnitProcessor.svgHorizontalLengthToUserSpace
849                     (wStr, SVG_WIDTH_ATTRIBUTE, uctx);
850             }
851             if (hStr.length() != 0) {
852                 h = UnitProcessor.svgVerticalLengthToUserSpace
853                     (hStr, SVG_HEIGHT_ATTRIBUTE, uctx);
854             }
855             break;
856         default:
857             throw new Error JavaDoc(); // can't be reached
858
}
859
860         Rectangle2D JavaDoc region = new Rectangle2D.Double JavaDoc(x, y, w, h);
861
862         // Now, extend filter primitive region with dx/dy/dw/dh
863
// settings (Batik extension). The dx/dy/dw/dh padding is
864
// *always* in userSpaceOnUse space.
865

866         units = "";
867         if ((parentNode != null) &&
868             (parentNode.getNodeType() == Node.ELEMENT_NODE)) {
869             Element JavaDoc parent = (Element JavaDoc)parentNode;
870             units = getChainableAttributeNS
871                 (parent, null,
872                  SVG12Constants.SVG_FILTER_PRIMITIVE_MARGINS_UNITS_ATTRIBUTE,
873                  ctx);
874         }
875
876         if (units.length() == 0) {
877             unitsType = USER_SPACE_ON_USE;
878         } else {
879             unitsType = parseCoordinateSystem
880                 (filterPrimitiveElement,
881                  SVG12Constants.SVG_FILTER_PRIMITIVE_MARGINS_UNITS_ATTRIBUTE, units);
882         }
883
884         // 'batik:dx' attribute - default is 0
885
String JavaDoc dxStr = filterPrimitiveElement.getAttributeNS
886             (null, SVG12Constants.SVG_MX_ATRIBUTE);
887         if (dxStr.length() == 0) {
888             dxStr = SVG12Constants.SVG_FILTER_MX_DEFAULT_VALUE;
889         }
890
891         // 'batik:dy' attribute - default is 0
892
String JavaDoc dyStr = filterPrimitiveElement.getAttributeNS
893             (null, SVG12Constants.SVG_MY_ATRIBUTE);
894         if (dyStr.length() == 0) {
895             dyStr = SVG12Constants.SVG_FILTER_MY_DEFAULT_VALUE;
896         }
897
898         // 'batik:dw' attribute - default is 0
899
String JavaDoc dwStr = filterPrimitiveElement.getAttributeNS
900             (null, SVG12Constants.SVG_MW_ATRIBUTE);
901         if (dwStr.length() == 0) {
902             dwStr = SVG12Constants.SVG_FILTER_MW_DEFAULT_VALUE;
903         }
904
905         // 'batik:dh' attribute - default is 0
906
String JavaDoc dhStr = filterPrimitiveElement.getAttributeNS
907             (null, SVG12Constants.SVG_MH_ATRIBUTE);
908         if (dhStr.length() == 0) {
909             dhStr = SVG12Constants.SVG_FILTER_MH_DEFAULT_VALUE;
910         }
911         
912         region = extendRegion(dxStr,
913                               dyStr,
914                               dwStr,
915                               dhStr,
916                               unitsType,
917                               filteredNode,
918                               region,
919                               uctx);
920         
921         Rectangle2D.intersect(region, filterRegion, region);
922
923         return region;
924     }
925
926     /////////////////////////////////////////////////////////////////////////
927
// region convenient methods
928
/////////////////////////////////////////////////////////////////////////
929

930
931     /** The userSpaceOnUse coordinate system constants. */
932     public static final short USER_SPACE_ON_USE = 1;
933
934     /** The objectBoundingBox coordinate system constants. */
935     public static final short OBJECT_BOUNDING_BOX = 2;
936
937     /** The strokeWidth coordinate system constants. */
938     public static final short STROKE_WIDTH = 3;
939
940     /**
941      * Parses the specified coordinate system defined by the specified element.
942      *
943      * @param e the element that defines the coordinate system
944      * @param attr the attribute which contains the coordinate system
945      * @param coordinateSystem the coordinate system to parse
946      * @return OBJECT_BOUNDING_BOX | USER_SPACE_ON_USE
947      */

948     public static short parseCoordinateSystem(Element JavaDoc e,
949                                               String JavaDoc attr,
950                                               String JavaDoc coordinateSystem) {
951         if (SVG_USER_SPACE_ON_USE_VALUE.equals(coordinateSystem)) {
952             return USER_SPACE_ON_USE;
953         } else if (SVG_OBJECT_BOUNDING_BOX_VALUE.equals(coordinateSystem)) {
954             return OBJECT_BOUNDING_BOX;
955         } else {
956             throw new BridgeException(e, ERR_ATTRIBUTE_VALUE_MALFORMED,
957                                       new Object JavaDoc[] {attr, coordinateSystem});
958         }
959     }
960
961     /**
962      * Parses the specified coordinate system defined by the specified
963      * marker element.
964      *
965      * @param e the element that defines the coordinate system
966      * @param attr the attribute which contains the coordinate system
967      * @param coordinateSystem the coordinate system to parse
968      * @return STROKE_WIDTH | USER_SPACE_ON_USE
969      */

970     public static short parseMarkerCoordinateSystem(Element JavaDoc e,
971                                                     String JavaDoc attr,
972                                                     String JavaDoc coordinateSystem) {
973         if (SVG_USER_SPACE_ON_USE_VALUE.equals(coordinateSystem)) {
974             return USER_SPACE_ON_USE;
975         } else if (SVG_STROKE_WIDTH_VALUE.equals(coordinateSystem)) {
976             return STROKE_WIDTH;
977         } else {
978             throw new BridgeException(e, ERR_ATTRIBUTE_VALUE_MALFORMED,
979                                       new Object JavaDoc[] {attr, coordinateSystem});
980         }
981     }
982
983     /**
984      * Returns a rectangle that represents the region defined by the
985      * specified coordinates.
986      *
987      * @param xStr the x coordinate of the region
988      * @param yStr the y coordinate of the region
989      * @param wStr the width of the region
990      * @param hStr the height of the region
991      * @param targetNode the graphics node (needed for objectBoundingBox)
992      * @param uctx the unit processor context (needed for userSpaceOnUse)
993      */

994     protected static Rectangle2D JavaDoc convertRegion(String JavaDoc xStr,
995                                                String JavaDoc yStr,
996                                                String JavaDoc wStr,
997                                                String JavaDoc hStr,
998                                                short unitsType,
999                                                GraphicsNode targetNode,
1000                                               UnitProcessor.Context uctx) {
1001
1002        // construct the mask region in the appropriate coordinate system
1003
double x, y, w, h;
1004        switch (unitsType) {
1005        case OBJECT_BOUNDING_BOX:
1006            x = UnitProcessor.svgHorizontalCoordinateToObjectBoundingBox
1007                (xStr, SVG_X_ATTRIBUTE, uctx);
1008            y = UnitProcessor.svgVerticalCoordinateToObjectBoundingBox
1009                (yStr, SVG_Y_ATTRIBUTE, uctx);
1010            w = UnitProcessor.svgHorizontalLengthToObjectBoundingBox
1011                (wStr, SVG_WIDTH_ATTRIBUTE, uctx);
1012            h = UnitProcessor.svgVerticalLengthToObjectBoundingBox
1013                (hStr, SVG_HEIGHT_ATTRIBUTE, uctx);
1014
1015            Rectangle2D JavaDoc bounds = targetNode.getGeometryBounds();
1016            if (bounds != null ) {
1017                x = bounds.getX() + x*bounds.getWidth();
1018                y = bounds.getY() + y*bounds.getHeight();
1019                w *= bounds.getWidth();
1020                h *= bounds.getHeight();
1021            } else {
1022                x = y = w = h = 0;
1023            }
1024            break;
1025        case USER_SPACE_ON_USE:
1026            x = UnitProcessor.svgHorizontalCoordinateToUserSpace
1027                (xStr, SVG_X_ATTRIBUTE, uctx);
1028            y = UnitProcessor.svgVerticalCoordinateToUserSpace
1029                (yStr, SVG_Y_ATTRIBUTE, uctx);
1030            w = UnitProcessor.svgHorizontalLengthToUserSpace
1031                (wStr, SVG_WIDTH_ATTRIBUTE, uctx);
1032            h = UnitProcessor.svgVerticalLengthToUserSpace
1033                (hStr, SVG_HEIGHT_ATTRIBUTE, uctx);
1034            break;
1035        default:
1036            throw new Error JavaDoc(); // can't be reached
1037
}
1038        return new Rectangle2D.Double JavaDoc(x, y, w, h);
1039    }
1040
1041    /////////////////////////////////////////////////////////////////////////
1042
// coordinate system and transformation support methods
1043
/////////////////////////////////////////////////////////////////////////
1044

1045    /**
1046     * Returns an AffineTransform according to the specified parameters.
1047     *
1048     * @param e the element that defines the transform
1049     * @param attr the name of the attribute that represents the transform
1050     * @param transform the transform to parse
1051     *
1052     */

1053    public static AffineTransform JavaDoc convertTransform(Element JavaDoc e,
1054                                                   String JavaDoc attr,
1055                                                   String JavaDoc transform) {
1056        try {
1057            return AWTTransformProducer.createAffineTransform(transform);
1058        } catch (ParseException ex) {
1059            throw new BridgeException(e, ERR_ATTRIBUTE_VALUE_MALFORMED,
1060                                      new Object JavaDoc[] {attr, transform, ex});
1061        }
1062    }
1063
1064    /**
1065     * Returns an AffineTransform to move to the objectBoundingBox
1066     * coordinate system.
1067     *
1068     * @param Tx the original transformation
1069     * @param node the graphics node that defines the coordinate
1070     * system to move into
1071     */

1072    public static AffineTransform JavaDoc toObjectBBox(AffineTransform JavaDoc Tx,
1073                                               GraphicsNode node) {
1074
1075        AffineTransform JavaDoc Mx = new AffineTransform JavaDoc();
1076        Rectangle2D JavaDoc bounds = node.getGeometryBounds();
1077        if (bounds != null) {
1078            Mx.translate(bounds.getX(), bounds.getY());
1079            Mx.scale(bounds.getWidth(), bounds.getHeight());
1080        }
1081        Mx.concatenate(Tx);
1082        return Mx;
1083    }
1084
1085    /**
1086     * Returns the specified a Rectangle2D move to the objectBoundingBox
1087     * coordinate system of the specified graphics node.
1088     *
1089     * @param r the original Rectangle2D
1090     * @param node the graphics node that defines the coordinate
1091     * system to move into
1092     */

1093    public static Rectangle2D JavaDoc toObjectBBox(Rectangle2D JavaDoc r,
1094                                           GraphicsNode node) {
1095
1096        Rectangle2D JavaDoc bounds = node.getGeometryBounds();
1097        if(bounds != null){
1098            return new Rectangle2D.Double JavaDoc
1099                (bounds.getX() + r.getX()*bounds.getWidth(),
1100                 bounds.getY() + r.getY()*bounds.getHeight(),
1101                 r.getWidth() * bounds.getWidth(),
1102                 r.getHeight() * bounds.getHeight());
1103        } else {
1104            return new Rectangle2D.Double JavaDoc();
1105        }
1106    }
1107}
1108
Popular Tags