| 1 18 package org.apache.batik.swing.svg; 19 20 import java.awt.Cursor ; 21 import java.awt.Dimension ; 22 import java.awt.EventQueue ; 23 import java.awt.Point ; 24 import java.awt.Rectangle ; 25 import java.awt.Shape ; 26 import java.awt.event.ComponentAdapter ; 27 import java.awt.event.ComponentEvent ; 28 import java.awt.event.KeyEvent ; 29 import java.awt.event.MouseEvent ; 30 import java.awt.geom.AffineTransform ; 31 import java.awt.geom.Dimension2D ; 32 import java.awt.geom.NoninvertibleTransformException ; 33 import java.awt.geom.Point2D ; 34 import java.util.ArrayList ; 35 import java.util.HashMap ; 36 import java.util.HashSet ; 37 import java.util.Iterator ; 38 import java.util.LinkedList ; 39 import java.util.List ; 40 import java.util.Map ; 41 import java.util.Set ; 42 43 import javax.swing.JOptionPane ; 44 45 import org.apache.batik.bridge.BridgeContext; 46 import org.apache.batik.bridge.BridgeException; 47 import org.apache.batik.bridge.BridgeExtension; 48 import org.apache.batik.bridge.DefaultScriptSecurity; 49 import org.apache.batik.bridge.DocumentLoader; 50 import org.apache.batik.bridge.ExternalResourceSecurity; 51 import org.apache.batik.bridge.RelaxedExternalResourceSecurity; 52 import org.apache.batik.bridge.ScriptSecurity; 53 import org.apache.batik.bridge.UpdateManager; 54 import org.apache.batik.bridge.UpdateManagerAdapter; 55 import org.apache.batik.bridge.UpdateManagerEvent; 56 import org.apache.batik.bridge.UpdateManagerListener; 57 import org.apache.batik.bridge.UserAgent; 58 import org.apache.batik.bridge.ViewBox; 59 import org.apache.batik.dom.svg.SVGDOMImplementation; 60 import org.apache.batik.dom.util.DOMUtilities; 61 import org.apache.batik.dom.util.XLinkSupport; 62 import org.apache.batik.ext.awt.image.spi.ImageTagRegistry; 63 import org.apache.batik.gvt.CanvasGraphicsNode; 64 import org.apache.batik.gvt.CompositeGraphicsNode; 65 import org.apache.batik.gvt.GraphicsNode; 66 import org.apache.batik.gvt.event.EventDispatcher; 67 import org.apache.batik.gvt.text.Mark; 68 import org.apache.batik.gvt.renderer.ImageRenderer; 69 import org.apache.batik.swing.gvt.GVTTreeRenderer; 70 import org.apache.batik.swing.gvt.GVTTreeRendererEvent; 71 import org.apache.batik.swing.gvt.GVTTreeRendererAdapter; 72 import org.apache.batik.swing.gvt.JGVTComponent; 73 import org.apache.batik.swing.gvt.JGVTComponentListener; 74 import org.apache.batik.util.ParsedURL; 75 import org.apache.batik.util.RunnableQueue; 76 import org.apache.batik.util.SVGConstants; 77 import org.apache.batik.util.XMLResourceDescriptor; 78 import org.w3c.dom.Document ; 79 import org.w3c.dom.DOMImplementation ; 80 import org.w3c.dom.Element ; 81 import org.w3c.dom.svg.SVGAElement; 82 import org.w3c.dom.svg.SVGDocument; 83 import org.w3c.dom.svg.SVGSVGElement; 84 85 196 public class JSVGComponent extends JGVTComponent { 197 198 202 public final static int AUTODETECT = 0; 203 204 211 public final static int ALWAYS_DYNAMIC = 1; 212 213 221 public final static int ALWAYS_STATIC = 2; 222 223 230 public final static int ALWAYS_INTERACTIVE = 3; 231 232 235 protected SVGDocumentLoader documentLoader; 236 237 240 protected SVGDocumentLoader nextDocumentLoader; 241 242 245 protected DocumentLoader loader; 246 247 250 protected GVTTreeBuilder gvtTreeBuilder; 251 252 255 protected GVTTreeBuilder nextGVTTreeBuilder; 256 257 260 protected SVGLoadEventDispatcher svgLoadEventDispatcher; 261 262 265 protected UpdateManager updateManager; 266 267 270 protected UpdateManager nextUpdateManager; 271 272 275 protected SVGDocument svgDocument; 276 277 280 protected List svgDocumentLoaderListeners = new LinkedList (); 281 282 285 protected List gvtTreeBuilderListeners = new LinkedList (); 286 287 290 protected List svgLoadEventDispatcherListeners = new LinkedList (); 291 292 295 protected List linkActivationListeners = new LinkedList (); 296 297 300 protected List updateManagerListeners = new LinkedList (); 301 302 305 protected UserAgent userAgent; 306 307 310 protected SVGUserAgent svgUserAgent; 311 312 315 protected BridgeContext bridgeContext; 316 317 320 protected String fragmentIdentifier; 321 322 325 protected boolean isDynamicDocument; 326 327 330 protected boolean isInteractiveDocument; 331 332 335 protected int documentState; 336 337 protected Dimension prevComponentSize; 338 339 protected Runnable afterStopRunnable = null; 340 341 protected SVGUpdateOverlay updateOverlay; 343 protected boolean recenterOnResize = true; 344 345 protected AffineTransform viewingTransform = null; 346 347 350 public JSVGComponent() { 351 this(null, false, false); 352 } 353 354 361 public JSVGComponent(SVGUserAgent ua, boolean eventsEnabled, 362 boolean selectableText) { 363 super(eventsEnabled, selectableText); 364 365 svgUserAgent = ua; 366 367 userAgent = new BridgeUserAgentWrapper(createUserAgent()); 368 369 addSVGDocumentLoaderListener((SVGListener)listener); 370 addGVTTreeBuilderListener((SVGListener)listener); 371 addSVGLoadEventDispatcherListener((SVGListener)listener); 372 if (updateOverlay != null) 373 getOverlays().add(updateOverlay); 374 } 375 376 public void dispose() { 377 setSVGDocument(null); 378 } 379 380 386 public boolean getRecenterOnResize() { 387 return recenterOnResize; 388 } 389 392 public void setRecenterOnResize(boolean recenterOnResize) { 393 this.recenterOnResize = recenterOnResize; 394 } 395 396 400 public boolean isDynamic() { 401 return isDynamicDocument; 402 } 403 404 408 public boolean isInteractive() { 409 return isInteractiveDocument; 410 } 411 412 418 public void setDocumentState(int state) { 419 documentState = state; 420 } 421 422 438 public UpdateManager getUpdateManager() { 439 if (svgLoadEventDispatcher != null) { 440 return svgLoadEventDispatcher.getUpdateManager(); 441 } 442 if (nextUpdateManager != null) { 443 return nextUpdateManager; 444 } 445 return updateManager; 446 } 447 448 451 public void resumeProcessing() { 452 if (updateManager != null) { 453 updateManager.resume(); 454 } 455 } 456 457 460 public void suspendProcessing() { 461 if (updateManager != null) { 462 updateManager.suspend(); 463 } 464 } 465 466 469 public void stopProcessing() { 470 nextDocumentLoader = null; 471 nextGVTTreeBuilder = null; 472 473 if (documentLoader != null) { 474 documentLoader.halt(); 475 } 476 if (gvtTreeBuilder != null) { 477 gvtTreeBuilder.halt(); 478 } 479 if (svgLoadEventDispatcher != null) { 480 svgLoadEventDispatcher.halt(); 481 } 482 if (nextUpdateManager != null) { 483 nextUpdateManager.interrupt(); 484 nextUpdateManager = null; 485 } 486 if (updateManager != null) { 487 updateManager.interrupt(); 488 } 489 super.stopProcessing(); 490 } 491 492 499 public void loadSVGDocument(String url) { 500 String oldURI = null; 501 if (svgDocument != null) { 502 oldURI = svgDocument.getURL(); 503 } 504 final ParsedURL newURI = new ParsedURL(oldURI, url); 505 506 stopThenRun(new Runnable () { 507 public void run() { 508 String url = newURI.toString(); 509 fragmentIdentifier = newURI.getRef(); 510 511 loader = new DocumentLoader(userAgent); 512 nextDocumentLoader = new SVGDocumentLoader(url, loader); 513 nextDocumentLoader.setPriority(Thread.MIN_PRIORITY); 514 515 Iterator it = svgDocumentLoaderListeners.iterator(); 516 while (it.hasNext()) { 517 nextDocumentLoader.addSVGDocumentLoaderListener 518 ((SVGDocumentLoaderListener)it.next()); 519 } 520 startDocumentLoader(); 521 } 522 }); 523 } 524 525 528 private void startDocumentLoader() { 529 documentLoader = nextDocumentLoader; 530 nextDocumentLoader = null; 531 documentLoader.start(); 532 } 533 534 550 public void setDocument(Document doc) { 551 if ((doc != null) && 552 !(doc.getImplementation() instanceof SVGDOMImplementation)) { 553 DOMImplementation impl; 554 impl = SVGDOMImplementation.getDOMImplementation(); 555 Document d = DOMUtilities.deepCloneDocument(doc, impl); 556 doc = d; 557 } 558 setSVGDocument((SVGDocument)doc); 559 } 560 561 578 public void setSVGDocument(SVGDocument doc) { 579 if ((doc != null) && 580 !(doc.getImplementation() instanceof SVGDOMImplementation)) { 581 DOMImplementation impl; 582 impl = SVGDOMImplementation.getDOMImplementation(); 583 Document d = DOMUtilities.deepCloneDocument(doc, impl); 584 doc = (SVGDocument)d; 585 } 586 587 final SVGDocument svgdoc = doc; 588 stopThenRun(new Runnable () { 589 public void run() { 590 installSVGDocument(svgdoc); 591 } 592 }); 593 } 594 595 600 protected void stopThenRun(final Runnable r) { 601 if (afterStopRunnable != null) { 602 afterStopRunnable = r; 605 return; 606 } 607 afterStopRunnable = r; 608 609 stopProcessing(); 610 611 if ((documentLoader == null) && 612 (gvtTreeBuilder == null) && 613 (gvtTreeRenderer == null) && 614 (svgLoadEventDispatcher == null) && 615 (nextUpdateManager == null) && 616 (updateManager == null)) { 617 Runnable asr = afterStopRunnable; 618 afterStopRunnable = null; 619 asr.run(); 620 } 621 } 622 623 628 protected void installSVGDocument(SVGDocument doc) { 629 svgDocument = doc; 630 631 if (bridgeContext != null) { 632 bridgeContext.dispose(); 633 bridgeContext = null; 634 } 635 636 releaseRenderingReferences(); 637 638 if (doc == null) { 639 isDynamicDocument = false; 640 isInteractiveDocument = false; 641 disableInteractions = true; 642 initialTransform = new AffineTransform (); 643 setRenderingTransform(initialTransform, false); 644 Rectangle vRect = getRenderRect(); 645 repaint(vRect.x, vRect.y, 646 vRect.width, vRect.height); 647 return; 648 } 649 650 bridgeContext = createBridgeContext(); 651 652 switch (documentState) { 653 case ALWAYS_STATIC: 654 isDynamicDocument = false; 655 isInteractiveDocument = false; 656 break; 657 case ALWAYS_INTERACTIVE: 658 isDynamicDocument = false; 659 isInteractiveDocument = true; 660 break; 661 case ALWAYS_DYNAMIC: 662 isDynamicDocument = true; 663 isInteractiveDocument = true; 664 break; 665 case AUTODETECT: 666 isDynamicDocument = bridgeContext.isDynamicDocument(doc); 667 if (isDynamicDocument) 668 isInteractiveDocument = true; 669 else 670 isInteractiveDocument = 671 bridgeContext.isInteractiveDocument(doc); 672 } 673 674 if (isInteractiveDocument) { 675 if (isDynamicDocument) 676 bridgeContext.setDynamicState(BridgeContext.DYNAMIC); 677 else 678 bridgeContext.setDynamicState(BridgeContext.INTERACTIVE); 679 } 680 681 Element root = doc.getDocumentElement(); 682 String znp = root.getAttributeNS 683 (null, SVGConstants.SVG_ZOOM_AND_PAN_ATTRIBUTE); 684 685 setDisableInteractions(!znp.equals(SVGConstants.SVG_MAGNIFY_VALUE)); 686 687 nextGVTTreeBuilder = new GVTTreeBuilder(doc, bridgeContext); 688 nextGVTTreeBuilder.setPriority(Thread.MIN_PRIORITY); 689 690 Iterator it = gvtTreeBuilderListeners.iterator(); 691 while (it.hasNext()) { 692 nextGVTTreeBuilder.addGVTTreeBuilderListener 693 ((GVTTreeBuilderListener)it.next()); 694 } 695 696 initializeEventHandling(); 697 698 if (gvtTreeBuilder == null && 699 documentLoader == null && 700 gvtTreeRenderer == null && 701 svgLoadEventDispatcher == null && 702 updateManager == null) { 703 startGVTTreeBuilder(); 704 } 705 } 706 707 710 protected void startGVTTreeBuilder() { 711 gvtTreeBuilder = nextGVTTreeBuilder; 712 nextGVTTreeBuilder = null; 713 gvtTreeBuilder.start(); 714 } 715 716 719 public SVGDocument getSVGDocument() { 720 return svgDocument; 721 } 722 723 726 public Dimension2D getSVGDocumentSize() { 727 return bridgeContext.getDocumentSize(); 728 } 729 730 733 public String getFragmentIdentifier() { 734 return fragmentIdentifier; 735 } 736 737 740 public void setFragmentIdentifier(String fi) { 741 fragmentIdentifier = fi; 742 if (computeRenderingTransform()) 743 scheduleGVTRendering(); 744 } 745 746 749 public void flushImageCache() { 750 ImageTagRegistry reg = ImageTagRegistry.getRegistry(); 751 reg.flushCache(); 752 } 753 754 public void setGraphicsNode(GraphicsNode gn, boolean createDispatcher) { 755 Dimension2D dim = bridgeContext.getDocumentSize(); 756 Dimension mySz = new Dimension ((int)dim.getWidth(), 757 (int)dim.getHeight()); 758 JSVGComponent.this.setMySize(mySz); 759 SVGSVGElement elt = svgDocument.getRootElement(); 760 prevComponentSize = getSize(); 761 AffineTransform at = calculateViewingTransform 762 (fragmentIdentifier, elt); 763 CanvasGraphicsNode cgn = getCanvasGraphicsNode(gn); 764 cgn.setViewingTransform(at); 765 viewingTransform = null; 766 initialTransform = new AffineTransform (); 767 setRenderingTransform(initialTransform, false); 768 jsvgComponentListener.updateMatrix(initialTransform); 769 addJGVTComponentListener(jsvgComponentListener); 770 addComponentListener(jsvgComponentListener); 771 super.setGraphicsNode(gn, createDispatcher); 772 } 773 774 777 protected BridgeContext createBridgeContext() { 778 if (loader == null) { 779 loader = new DocumentLoader(userAgent); 780 } 781 BridgeContext result = new BridgeContext(userAgent, loader); 782 return result; 783 } 784 785 788 protected void startSVGLoadEventDispatcher(GraphicsNode root) { 789 UpdateManager um = new UpdateManager(bridgeContext, 790 root, 791 svgDocument); 792 svgLoadEventDispatcher = 793 new SVGLoadEventDispatcher(root, 794 svgDocument, 795 bridgeContext, 796 um); 797 Iterator it = svgLoadEventDispatcherListeners.iterator(); 798 while (it.hasNext()) { 799 svgLoadEventDispatcher.addSVGLoadEventDispatcherListener 800 ((SVGLoadEventDispatcherListener)it.next()); 801 } 802 803 svgLoadEventDispatcher.start(); 804 } 805 806 809 protected ImageRenderer createImageRenderer() { 810 if (isDynamicDocument) { 811 return rendererFactory.createDynamicImageRenderer(); 812 } else { 813 return rendererFactory.createStaticImageRenderer(); 814 } 815 } 816 817 public CanvasGraphicsNode getCanvasGraphicsNode() { 818 return getCanvasGraphicsNode(gvtRoot); 819 820 } 821 822 protected CanvasGraphicsNode getCanvasGraphicsNode(GraphicsNode gn) { 823 if (!(gn instanceof CompositeGraphicsNode)) 824 return null; 825 CompositeGraphicsNode cgn = (CompositeGraphicsNode)gn; 826 List children = cgn.getChildren(); 827 if (children.size() == 0) 828 return null; 829 gn = (GraphicsNode)children.get(0); 830 if (!(gn instanceof CanvasGraphicsNode)) 831 return null; 832 return (CanvasGraphicsNode)gn; 833 } 834 835 public AffineTransform getViewingTransform() { 836 AffineTransform vt; 837 synchronized (this) { 838 vt = viewingTransform; 839 if (vt == null) { 840 CanvasGraphicsNode cgn = getCanvasGraphicsNode(); 841 if (cgn != null) 842 vt = cgn.getViewingTransform(); 843 } 844 } 845 return vt; 846 } 847 848 851 public AffineTransform getViewBoxTransform() { 852 AffineTransform at = getRenderingTransform(); 853 if (at == null) at = new AffineTransform (); 854 else at = new AffineTransform (at); 855 AffineTransform vt = getViewingTransform(); 856 if (vt != null) { 857 at.concatenate(vt); 858 } 859 return at; 860 } 861 862 866 protected boolean computeRenderingTransform() { 867 if ((svgDocument == null) || (gvtRoot == null)) 868 return false; 869 870 boolean ret = updateRenderingTransform(); 871 initialTransform = new AffineTransform (); 872 if (!initialTransform.equals(getRenderingTransform())) { 873 setRenderingTransform(initialTransform, false); 874 ret = true; 875 } 876 return ret; 877 } 878 879 protected AffineTransform calculateViewingTransform 880 (String fragIdent, SVGSVGElement svgElt) { 881 Dimension d = getSize(); 882 if (d.width < 1) d.width = 1; 883 if (d.height < 1) d.height = 1; 884 return ViewBox.getViewTransform 885 (fragIdent, svgElt, d.width, d.height); 886 } 887 888 892 protected boolean updateRenderingTransform() { 893 if ((svgDocument == null) || (gvtRoot == null)) 894 return false; 895 896 try { 897 SVGSVGElement elt = svgDocument.getRootElement(); 898 Dimension d = getSize(); 899 Dimension |