1 18 package org.apache.batik.bridge; 19 20 import java.awt.Cursor ; 21 import java.awt.Dimension ; 22 import java.awt.Image ; 23 import java.awt.Point ; 24 import java.awt.Rectangle ; 25 import java.awt.Toolkit ; 26 import java.awt.geom.AffineTransform ; 27 import java.awt.geom.Point2D ; 28 import java.awt.image.BufferedImage ; 29 import java.awt.image.ColorModel ; 30 import java.awt.image.Raster ; 31 import java.awt.image.RenderedImage ; 32 import java.awt.image.SampleModel ; 33 import java.awt.image.WritableRaster ; 34 import java.util.Hashtable ; 35 import java.util.Map ; 36 37 import org.apache.batik.css.engine.SVGCSSEngine; 38 import org.apache.batik.css.engine.value.Value; 39 import org.apache.batik.dom.svg.XMLBaseSupport; 40 import org.apache.batik.dom.util.XLinkSupport; 41 import org.apache.batik.ext.awt.image.PadMode; 42 import org.apache.batik.ext.awt.image.renderable.AffineRable8Bit; 43 import org.apache.batik.ext.awt.image.renderable.Filter; 44 import org.apache.batik.ext.awt.image.renderable.PadRable8Bit; 45 import org.apache.batik.ext.awt.image.spi.ImageTagRegistry; 46 import org.apache.batik.gvt.GraphicsNode; 47 import org.apache.batik.util.ParsedURL; 48 import org.apache.batik.util.SVGConstants; 49 import org.apache.batik.util.SoftReferenceCache; 50 import org.w3c.dom.Element ; 51 import org.w3c.dom.Node ; 52 import org.w3c.dom.css.CSSPrimitiveValue; 53 import org.w3c.dom.css.CSSValue; 54 import org.w3c.dom.svg.SVGDocument; 55 import org.w3c.dom.svg.SVGPreserveAspectRatio; 56 57 58 65 public class CursorManager implements SVGConstants, ErrorConstants { 66 69 protected static Map cursorMap; 70 71 74 public static final Cursor DEFAULT_CURSOR 75 = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR); 76 77 80 public static final Cursor ANCHOR_CURSOR 81 = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); 82 83 86 public static final Cursor TEXT_CURSOR 87 = Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR); 88 89 92 public static final int DEFAULT_PREFERRED_WIDTH = 32; 93 public static final int DEFAULT_PREFERRED_HEIGHT = 32; 94 95 98 static { 99 cursorMap = new Hashtable (); 100 cursorMap.put(SVG_CROSSHAIR_VALUE, 101 Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); 102 cursorMap.put(SVG_DEFAULT_VALUE, 103 Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 104 cursorMap.put(SVG_POINTER_VALUE, 105 Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 106 cursorMap.put(SVG_MOVE_VALUE, 107 Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); 108 cursorMap.put(SVG_E_RESIZE_VALUE, 109 Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); 110 cursorMap.put(SVG_NE_RESIZE_VALUE, 111 Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR)); 112 cursorMap.put(SVG_NW_RESIZE_VALUE, 113 Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR)); 114 cursorMap.put(SVG_N_RESIZE_VALUE, 115 Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR)); 116 cursorMap.put(SVG_SE_RESIZE_VALUE, 117 Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR)); 118 cursorMap.put(SVG_SW_RESIZE_VALUE, 119 Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR)); 120 cursorMap.put(SVG_S_RESIZE_VALUE, 121 Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR)); 122 cursorMap.put(SVG_W_RESIZE_VALUE, 123 Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR)); 124 cursorMap.put(SVG_TEXT_VALUE, 125 Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); 126 cursorMap.put(SVG_WAIT_VALUE, 127 Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); 128 cursorMap.put(SVG_HELP_VALUE, 129 Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 130 131 } 132 133 136 protected BridgeContext ctx; 137 138 141 protected CursorCache cursorCache = new CursorCache(); 142 143 148 public CursorManager(BridgeContext ctx) { 149 this.ctx = ctx; 150 } 151 152 157 public static Cursor getPredefinedCursor(String cursorName){ 158 return (Cursor )cursorMap.get(cursorName); 159 } 160 161 162 163 168 public Cursor convertCursor(Element e) { 169 Value cursorValue = CSSUtilities.getComputedStyle 170 (e, SVGCSSEngine.CURSOR_INDEX); 171 172 String cursorStr = SVGConstants.SVG_AUTO_VALUE; 173 174 if (cursorValue != null) { 175 if (cursorValue.getCssValueType() == CSSValue.CSS_PRIMITIVE_VALUE 176 && 177 cursorValue.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { 178 cursorStr = cursorValue.getStringValue(); 181 return convertBuiltInCursor(e, cursorStr); 182 } else if (cursorValue.getCssValueType() == 183 CSSValue.CSS_VALUE_LIST) { 184 int nValues = cursorValue.getLength(); 185 if (nValues == 1) { 186 cursorValue = cursorValue.item(0); 187 if (cursorValue.getPrimitiveType() == 188 CSSPrimitiveValue.CSS_IDENT) { 189 cursorStr = cursorValue.getStringValue(); 190 return convertBuiltInCursor(e, cursorStr); 191 } 192 } else if (nValues > 1) { 193 return convertSVGCursor(e, cursorValue); 198 } 199 } 200 } 201 202 return convertBuiltInCursor(e, cursorStr); 203 } 204 205 public Cursor convertBuiltInCursor(Element e, String cursorStr) { 206 Cursor cursor = null; 207 208 if (cursorStr.charAt(0) == 'a') { 212 String nameSpaceURI = e.getNamespaceURI(); 242 if (SVGConstants.SVG_NAMESPACE_URI.equals(nameSpaceURI)) { 243 String tag = e.getLocalName(); 244 if (SVGConstants.SVG_A_TAG.equals(tag)) { 245 cursor = CursorManager.ANCHOR_CURSOR; 246 } else if (SVGConstants.SVG_TEXT_TAG.equals(tag) || 247 SVGConstants.SVG_TSPAN_TAG.equals(tag) || 248 SVGConstants.SVG_TREF_TAG.equals(tag) ) { 249 cursor = CursorManager.TEXT_CURSOR; 250 } else if (SVGConstants.SVG_IMAGE_TAG.equals(tag)) { 251 return null; 253 } else { 254 cursor = CursorManager.DEFAULT_CURSOR; 255 } 256 } else { 257 cursor = CursorManager.DEFAULT_CURSOR; 258 } 259 } else { 260 cursor = CursorManager.getPredefinedCursor(cursorStr); 262 } 263 264 return cursor; 265 } 266 267 268 274 public Cursor convertSVGCursor(Element e, Value l) { 275 int nValues = l.getLength(); 276 Element cursorElement = null; 277 for (int i=0; i<nValues-1; i++) { 278 Value cursorValue = l.item(i); 279 if (cursorValue.getPrimitiveType() == CSSPrimitiveValue.CSS_URI) { 280 String uri = cursorValue.getStringValue(); 281 282 try { 286 cursorElement = ctx.getReferencedElement(e, uri); 287 } catch (BridgeException be) { 288 if (!ERR_URI_BAD_TARGET.equals(be.getCode())) { 292 throw be; 293 } 294 } 295 296 if (cursorElement != null) { 297 String cursorNS = cursorElement.getNamespaceURI(); 299 if (SVGConstants.SVG_NAMESPACE_URI.equals(cursorNS) && 300 SVGConstants.SVG_CURSOR_TAG.equals 301 (cursorElement.getLocalName())) { 302 Cursor c = convertSVGCursorElement(cursorElement); 303 if (c != null) { 304 return c; 305 } 306 } 307 } 308 } 309 } 310 311 Value cursorValue = l.item(nValues-1); 316 String cursorStr = SVGConstants.SVG_AUTO_VALUE; 317 if (cursorValue.getPrimitiveType() == CSSPrimitiveValue.CSS_IDENT) { 318 cursorStr = cursorValue.getStringValue(); 319 } 320 321 return convertBuiltInCursor(e, cursorStr); 322 } 323 324 327 public Cursor convertSVGCursorElement(Element cursorElement) { 328 String uriStr = XLinkSupport.getXLinkHref(cursorElement); 331 if (uriStr.length() == 0) { 332 throw new BridgeException(cursorElement, ERR_ATTRIBUTE_MISSING, 333 new Object [] {"xlink:href"}); 334 } 335 336 String baseURI = XMLBaseSupport.getCascadedXMLBase(cursorElement); 337 ParsedURL purl; 338 if (baseURI == null) { 339 purl = new ParsedURL(uriStr); 340 } else { 341 purl = new ParsedURL(baseURI, uriStr); 342 } 343 344 UnitProcessor.Context uctx 348 = UnitProcessor.createContext(ctx, cursorElement); 349 350 String s = cursorElement.getAttributeNS(null, SVG_X_ATTRIBUTE); 351 float x = 0; 352 if (s.length() != 0) { 353 x = UnitProcessor.svgHorizontalCoordinateToUserSpace 354 (s, SVG_X_ATTRIBUTE, uctx); 355 } 356 357 s = cursorElement.getAttributeNS(null, SVG_Y_ATTRIBUTE); 358 float y = 0; 359 if (s.length() != 0) { 360 y = UnitProcessor.svgVerticalCoordinateToUserSpace 361 (s, SVG_Y_ATTRIBUTE, uctx); 362 } 363 364 CursorDescriptor desc = new CursorDescriptor(purl, x, y); 365 366 Cursor cachedCursor = cursorCache.getCursor(desc); 370 371 if (cachedCursor != null) { 372 return cachedCursor; 373 } 374 375 Point2D.Float hotSpot = new Point2D.Float (x, y); 380 Filter f = cursorHrefToFilter(cursorElement, 381 purl, 382 hotSpot); 383 if (f == null) { 384 cursorCache.clearCursor(desc); 385 return null; 386 } 387 388 Rectangle cursorSize = f.getBounds2D().getBounds(); 391 RenderedImage ri = f.createScaledRendering(cursorSize.width, 392 cursorSize.height, 393 null); 394 Image img = null; 395 396 if (ri instanceof Image ) { 397 img = (Image )ri; 398 } else { 399 img = renderedImageToImage(ri); 400 } 401 402 hotSpot.x = hotSpot.x < 0 ? 0 : hotSpot.x; 405 hotSpot.y = hotSpot.y < 0 ? 0 : hotSpot.y; 406 hotSpot.x = hotSpot.x > (cursorSize.width-1) ? cursorSize.width - 1 : hotSpot.x; 407 hotSpot.y = hotSpot.y > (cursorSize.height-1) ? cursorSize.height - 1: hotSpot.y; 408 409 Cursor c = Toolkit.getDefaultToolkit() 413 .createCustomCursor(img, 414 new Point (Math.round(hotSpot.x), 415 Math.round(hotSpot.y)), 416 purl.toString()); 417 418 cursorCache.putCursor(desc, c); 419 return c; 420 } 421 422 426 protected Filter cursorHrefToFilter(Element cursorElement, 427 ParsedURL purl, 428 Point2D hotSpot) { 429 430 AffineRable8Bit f = null; 431 String uriStr = purl.toString(); 432 Dimension cursorSize = null; 433 434 DocumentLoader loader = ctx.getDocumentLoader(); 436 SVGDocument svgDoc = (SVGDocument)cursorElement.getOwnerDocument(); 437 URIResolver resolver = new URIResolver(svgDoc, loader); 438 try { 439 Element rootElement = null; 440 Node n = resolver.getNode(uriStr, cursorElement); 441 if (n.getNodeType() == Node.DOCUMENT_NODE) { 442 SVGDocument doc = (SVGDocument)n; 443 ctx.initializeDocument(doc); 445 rootElement = doc.getRootElement(); 446 } else { 447 throw new BridgeException 448 (cursorElement, ERR_URI_IMAGE_INVALID, 449 new Object [] {uriStr}); 450 } 451 GraphicsNode node = ctx.getGVTBuilder().build(ctx, rootElement); 452 453 float width = DEFAULT_PREFERRED_WIDTH; 459 float height = DEFAULT_PREFERRED_HEIGHT; 460 UnitProcessor.Context uctx 461 = UnitProcessor.createContext(ctx, rootElement); 462 463 String s = rootElement.getAttribute(SVG_WIDTH_ATTRIBUTE); 464 if (s.length() != 0) { 465 width = UnitProcessor.svgHorizontalLengthToUserSpace 466 (s, SVG_WIDTH_ATTRIBUTE, uctx); 467 } 468 469 s = rootElement.getAttribute(SVG_HEIGHT_ATTRIBUTE); 470 if (s.length() != 0) { 471 height = UnitProcessor.svgVerticalLengthToUserSpace 472 (s, SVG_HEIGHT_ATTRIBUTE, uctx); 473 } 474 475 cursorSize 476 = Toolkit.getDefaultToolkit().getBestCursorSize 477 (Math.round(width), Math.round(height)); 478 479 AffineTransform at 481 = ViewBox.getPreserveAspectRatioTransform(rootElement, 482 cursorSize.width, 483 cursorSize.height); 484 Filter filter = node.getGraphicsNodeRable(true); 485 f = new AffineRable8Bit(filter, at); 486 } catch (BridgeException ex) { 487 throw ex; 488 } catch (SecurityException ex) { 489 throw new BridgeException(cursorElement, ERR_URI_UNSECURE, 490 new Object [] {uriStr}); 491 } catch (Exception ex) { 492 493 } 494 495 496 if (f == null) { 499 ImageTagRegistry reg = ImageTagRegistry.getRegistry(); 500 Filter filter = reg.readURL(purl); 501 if (filter == null) { 502 return null; 503 } 504 505 if (filter.getProperty 507 (SVGBrokenLinkProvider.SVG_BROKEN_LINK_DOCUMENT_PROPERTY) != null) { 508 return null; 509 } 510 511 Rectangle preferredSize = filter.getBounds2D().getBounds(); 512 cursorSize = Toolkit.getDefaultToolkit().getBestCursorSize 513 (preferredSize.width, preferredSize.height); 514 515 if (preferredSize != null && preferredSize.width >0 516 && preferredSize.height > 0 ) { 517 AffineTransform at = new AffineTransform (); 518 if (preferredSize.width > cursorSize.width 519 || 520 preferredSize.height > cursorSize.height) { 521 at = ViewBox.getPreserveAspectRatioTransform 522 (new float[] {0, 0, preferredSize.width, preferredSize.height}, 523 SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMINYMIN, 524 true, 525 cursorSize.width, 526 cursorSize.height); 527 } 528 f = new AffineRable8Bit(filter, at); 529 } else { 530 return null; 532 } 533 } 534 535 536 AffineTransform at = f.getAffine(); 540 at.transform(hotSpot, hotSpot); 541 542 Rectangle cursorViewport 546 = new Rectangle (0, 0, cursorSize.width, cursorSize.height); 547 548 PadRable8Bit cursorImage 549 = new PadRable8Bit(f, cursorViewport, 550 PadMode.ZERO_PAD); 551 552 return cursorImage; 553 554 } 555 556 557 560 protected Image renderedImageToImage(RenderedImage ri) { 561 int x = ri.getMinX(); 562 int y = ri.getMinY(); 563 SampleModel sm = ri.getSampleModel(); 564 ColorModel cm = ri.getColorModel(); 565 WritableRaster wr = Raster.createWritableRaster(sm, new Point (x,y)); 566 ri.copyData(wr); 567 568 return new BufferedImage (cm, wr, cm.isAlphaPremultiplied(), null); 569 } 570 571 576 static class CursorDescriptor { 577 ParsedURL purl; 578 float x; 579 float y; 580 String desc; 581 582 public CursorDescriptor(ParsedURL purl, 583 float x, float y) { 584 if (purl == null) { 585 throw new IllegalArgumentException (); 586 } 587 588 this.purl = purl; 589 this.x = x; 590 this.y = y; 591 592 this.desc = this.getClass().getName() + 594 "\n\t:[" + this.purl + "]\n\t:[" + x + "]:[" + y + "]"; 595 } 596 597 public boolean equals(Object obj) { 598 if (obj == null 599 || 600 !(obj instanceof CursorDescriptor)) { 601 return false; 602 } 603 604 CursorDescriptor desc = (CursorDescriptor)obj; 605 boolean isEqual = 606 this.purl.equals(desc.purl) 607 && 608 this.x == desc.x 609 && 610 this.y == desc.y; 611 612 return isEqual; 613 } 614 615 public String toString() { 616 return this.desc; 617 } 618 619 public int hashCode() { 620 return desc.hashCode(); 621 } 622 } 623 624 629 static class CursorCache extends SoftReferenceCache { 630 public CursorCache() { 631 } 632 633 public Cursor getCursor(CursorDescriptor desc) { 634 return (Cursor )requestImpl(desc); 635 } 636 637 public void putCursor(CursorDescriptor desc, 638 Cursor cursor) { 639 putImpl(desc, cursor); 640 } 641 642 public void clearCursor(CursorDescriptor desc) { 643 clearImpl(desc); 644 } 645 } 646 647 648 649 } 650 | Popular Tags |