| 1 18 package org.apache.batik.bridge; 19 20 import java.awt.geom.AffineTransform ; 21 import java.awt.geom.Point2D ; 22 import java.awt.geom.Rectangle2D ; 23 import java.io.IOException ; 24 import java.util.Iterator ; 25 import java.util.LinkedList ; 26 import java.util.List ; 27 import java.util.StringTokenizer ; 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 ; 40 import org.w3c.dom.Node ; 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 53 public abstract class SVGUtilities implements SVGConstants, ErrorConstants { 54 55 58 protected SVGUtilities() {} 59 60 64 67 public static Node getImportedChild(Node n) { 68 return CSSEngine.getImportedChild(n); 69 } 70 71 76 public static Element getParentElement(Element elt) { 77 return CSSEngine.getParentElement(elt); 78 } 79 80 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 100 public static float convertSVGNumber(String s) { 101 return Float.parseFloat(s); 102 } 103 104 108 public static int convertSVGInteger(String s) { 109 return Integer.parseInt(s); 110 } 111 112 118 public static float convertRatio(String 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 136 public static String getDescription(SVGElement elt) { 137 String result = ""; 138 boolean preserve = false; 139 Node n = elt.getFirstChild(); 140 if (n != null && n.getNodeType() == Node.ELEMENT_NODE) { 141 String 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 166 public static boolean matchUserAgent(Element elt, UserAgent ua) { 167 test: if (elt.hasAttributeNS(null, SVG_SYSTEM_LANGUAGE_ATTRIBUTE)) { 168 String sl = elt.getAttributeNS(null, 170 SVG_SYSTEM_LANGUAGE_ATTRIBUTE); 171 if (sl.length() == 0) return false; 173 StringTokenizer st = new StringTokenizer (sl, ", "); 174 while (st.hasMoreTokens()) { 175 String 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 String rf = elt.getAttributeNS(null, 185 SVG_REQUIRED_FEATURES_ATTRIBUTE); 186 if (rf.length() == 0) return false; 188 StringTokenizer st = new StringTokenizer (rf, " "); 189 while (st.hasMoreTokens()) { 190 String s = st.nextToken(); 191 if (!ua.hasFeature(s)) { 192 return false; 193 } 194 } 195 } 196 if (elt.hasAttributeNS(null, SVG_REQUIRED_EXTENSIONS_ATTRIBUTE)) { 197 String re = elt.getAttributeNS(null, 199 SVG_REQUIRED_EXTENSIONS_ATTRIBUTE); 200 if (re.length() == 0) return false; 202 StringTokenizer st = new StringTokenizer (re, " "); 203 while (st.hasMoreTokens()) { 204 String s = st.nextToken(); 205 if (!ua.supportExtension(s)) { 206 return false; 207 } 208 } 209 } 210 return true; 211 } 212 213 220 protected static boolean matchUserLanguage(String s, 221 String userLanguages) { 222 StringTokenizer st = new StringTokenizer (userLanguages, ", "); 223 while (st.hasMoreTokens()) { 224 String 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 246 public static String getChainableAttributeNS(Element element, 247 String namespaceURI, 248 String attrName, 249 BridgeContext ctx) { 250 251 DocumentLoader loader = ctx.getDocumentLoader(); 252 Element e = element; 253 List refs = new LinkedList (); 254 for (;;) { 255 String v = e.getAttributeNS(namespaceURI, attrName); 256 if (v.length() > 0) { return v; 258 } 259 String uriStr = XLinkSupport.getXLinkHref(e); 260 if (uriStr.length() == 0) { return ""; 262 } 263 SVGDocument svgDoc = (SVGDocument)e.getOwnerDocument(); 264 String 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 [] {uriStr}); 270 271 Iterator 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 [] {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 ex) { 284 throw new BridgeException(e, ERR_URI_IO, 285 new Object [] {uriStr}); 286 } catch(SecurityException ex) { 287 throw new BridgeException(e, ERR_URI_UNSECURE, 288 new Object [] {uriStr}); 289 } 290 } 291 } 292 293 297 308 public static Point2D convertPoint(String xStr, 309 String xAttr, 310 String yStr, 311 String 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 (); } 331 return new Point2D.Float (x, y); 332 } 333 334 343 public static float convertLength(String length, 344 String 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 (); } 356 } 357 358 362 371 public static Rectangle2D convertMaskRegion(Element maskElement, 372 Element maskedElement, 373 GraphicsNode maskedNode, 374 BridgeContext ctx) { 375 376 String xStr = maskElement.getAttributeNS(null, SVG_X_ATTRIBUTE); 378 if (xStr.length() == 0) { 379 xStr = SVG_MASK_X_DEFAULT_VALUE; 380 } 381 String yStr = maskElement.getAttributeNS(null, SVG_Y_ATTRIBUTE); 383 if (yStr.length() == 0) { 384 yStr = SVG_MASK_Y_DEFAULT_VALUE; 385 } 386 String wStr = maskElement.getAttributeNS(null, SVG_WIDTH_ATTRIBUTE); 388 if (wStr.length() == 0) { 389 wStr = SVG_MASK_WIDTH_DEFAULT_VALUE; 390 } 391 String hStr = maskElement.getAttributeNS(null, SVG_HEIGHT_ATTRIBUTE); 393 if (hStr.length() == 0) { 394 hStr = SVG_MASK_HEIGHT_DEFAULT_VALUE; 395 } 396 short unitsType; 398 String 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 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 424 433 public static Rectangle2D convertPatternRegion(Element patternElement, 434 Element paintedElement, 435 GraphicsNode paintedNode, 436 BridgeContext ctx) { 437 438 String xStr = getChainableAttributeNS 440 (patternElement, null, SVG_X_ATTRIBUTE, ctx); 441 if (xStr.length() == 0) { 442 xStr = SVG_PATTERN_X_DEFAULT_VALUE; 443 } 444 String yStr = getChainableAttributeNS 446 (patternElement, null, SVG_Y_ATTRIBUTE, ctx); 447 if (yStr.length() == 0) { 448 yStr = SVG_PATTERN_Y_DEFAULT_VALUE; 449 } 450 String 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 [] {SVG_WIDTH_ATTRIBUTE}); 456 } 457 String 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 [] {SVG_HEIGHT_ATTRIBUTE}); 463 } 464 short unitsType; 466 String 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 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 492 499 public static 500 float [] convertFilterRes(Element filterElement, BridgeContext ctx) { 501 502 float [] filterRes = new float[2]; 503 String s = getChainableAttributeNS 504 (filterElement, null, SVG_FILTER_RES_ATTRIBUTE, ctx); 505 Float [] 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 [] {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 [] {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 [] {SVG_FILTER_RES_ATTRIBUTE, s}); 532 } 533 return filterRes; 534 } 535 536 541 public static Float [] 542 convertSVGNumberOptionalNumber(Element elem, 543 String attrName, 544 String attrValue) { 545 546 Float [] ret = new Float [2]; 547 if (attrValue.length() == 0) 548 return ret; 549 550 try { 551 StringTokenizer tokens = new StringTokenizer (attrValue, " "); 552 ret[0] = new Float (Float.parseFloat(tokens.nextToken())); 553 if (tokens.hasMoreTokens()) { 554 ret[1] = new Float (Float.parseFloat(tokens.nextToken())); 555 } 556 557 if (tokens.hasMoreTokens()) { 558 throw new BridgeException 559 (elem, ERR_ATTRIBUTE_VALUE_MALFORMED, 560 new Object [] {attrName, attrValue}); 561 } 562 } catch (NumberFormatException ex) { 563 throw new BridgeException 564 (elem, ERR_ATTRIBUTE_VALUE_MALFORMED, 565 new Object [] {attrName, attrValue, ex}); 566 } 567 return ret; 568 } 569 570 571 580 public static 581 Rectangle2D convertFilterChainRegion(Element filterElement, 582 Element filteredElement, 583 GraphicsNode filteredNode, 584 BridgeContext ctx) { 585 586 String xStr = getChainableAttributeNS 588 (filterElement, null, SVG_X_ATTRIBUTE, ctx); 589 if (xStr.length() == 0) { 590 xStr = SVG_FILTER_X_DEFAULT_VALUE; 591 } 592 String yStr = getChainableAttributeNS 594 (filterElement, null, SVG_Y_ATTRIBUTE, ctx); 595 if (yStr.length() == 0) { 596 yStr = SVG_FILTER_Y_DEFAULT_VALUE; 597 } 598 String wStr = getChainableAttributeNS 600 (filterElement, null, SVG_WIDTH_ATTRIBUTE, ctx); 601 if (wStr.length() == 0) { 602 wStr = SVG_FILTER_WIDTH_DEFAULT_VALUE; 603 } 604 String hStr = getChainableAttributeNS 606 (filterElement, null, SVG_HEIGHT_ATTRIBUTE, ctx); 607 if (hStr.length() == 0) { 608 hStr = SVG_FILTER_HEIGHT_DEFAULT_VALUE; 609 } 610 short unitsType; 612 String 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 UnitProcessor.Context uctx 624 = UnitProcessor.createContext(ctx, filteredElement); 625 626 Rectangle2D region = convertRegion(xStr, 627 yStr, 628 wStr, 629 hStr, 630 unitsType, 631 filteredNode, 632 uctx); 633 units = getChainableAttributeNS 637 (filterElement, null, 638 SVG12Constants.SVG_FILTER_MARGINS_UNITS_ATTRIBUTE, ctx); 639 if (units.length() == 0) { 640 unitsType = USER_SPACE_ON_USE; 642 } else { 643 unitsType = parseCoordinateSystem 644 (filterElement, 645 SVG12Constants.SVG_FILTER_MARGINS_UNITS_ATTRIBUTE, units); 646 } 647 648 String dxStr = filterElement.getAttributeNS(null, 650 SVG12Constants.SVG_MX_ATRIBUTE); 651 if (dxStr.length() == 0) { 652 dxStr = SVG12Constants.SVG_FILTER_MX_DEFAULT_VALUE; 653 } 654 String dyStr = filterElement.getAttributeNS(null, SVG12Constants.SVG_MY_ATRIBUTE); 656 if (dyStr.length() == 0) { 657 dyStr = SVG12Constants.SVG_FILTER_MY_DEFAULT_VALUE; 658 } 659 String dwStr = filterElement.getAttributeNS(null, SVG12Constants.SVG_MW_ATRIBUTE); 661 if (dwStr.length() == 0) { 662 dwStr = SVG12Constants.SVG_FILTER_MW_DEFAULT_VALUE; 663 } 664 String 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 693 protected static Rectangle2D extendRegion(String dxStr, 694 String dyStr, 695 String dwStr, 696 String dhStr, 697 short unitsType, 698 GraphicsNode filteredNode, 699 Rectangle2D region, 700  
|