1 18 package org.apache.batik.bridge; 19 20 import java.awt.AlphaComposite ; 21 import java.awt.Color ; 22 import java.awt.Composite ; 23 import java.awt.Paint ; 24 import java.awt.RenderingHints ; 25 import java.awt.Shape ; 26 import java.awt.Stroke ; 27 import java.awt.font.TextAttribute ; 28 import java.awt.geom.AffineTransform ; 29 import java.awt.geom.GeneralPath ; 30 import java.awt.geom.Point2D ; 31 import java.awt.geom.Rectangle2D ; 32 import java.text.AttributedCharacterIterator ; 33 import java.text.AttributedString ; 34 import java.text.AttributedCharacterIterator.Attribute; 35 import java.util.ArrayList ; 36 import java.util.HashMap ; 37 import java.util.HashSet ; 38 import java.util.Iterator ; 39 import java.util.List ; 40 import java.util.Map ; 41 import java.util.Set ; 42 import java.util.StringTokenizer ; 43 import java.util.Vector ; 44 import java.util.WeakHashMap ; 45 46 import org.apache.batik.css.engine.CSSEngineEvent; 47 import org.apache.batik.css.engine.CSSStylableElement; 48 import org.apache.batik.css.engine.SVGCSSEngine; 49 import org.apache.batik.css.engine.StyleMap; 50 import org.apache.batik.css.engine.value.ListValue; 51 import org.apache.batik.css.engine.value.Value; 52 import org.apache.batik.dom.svg.SVGContext; 53 import org.apache.batik.dom.svg.SVGOMElement; 54 import org.apache.batik.dom.svg.SVGTextContent; 55 import org.apache.batik.dom.util.XLinkSupport; 56 import org.apache.batik.dom.util.XMLSupport; 57 import org.apache.batik.gvt.GraphicsNode; 58 import org.apache.batik.gvt.TextNode; 59 import org.apache.batik.gvt.font.GVTFontFamily; 60 import org.apache.batik.gvt.font.GVTGlyphMetrics; 61 import org.apache.batik.gvt.font.GVTGlyphVector; 62 import org.apache.batik.gvt.renderer.StrokingTextPainter; 63 import org.apache.batik.gvt.text.GVTAttributedCharacterIterator; 64 import org.apache.batik.gvt.text.Mark; 65 import org.apache.batik.gvt.text.TextHit; 66 import org.apache.batik.gvt.text.TextPaintInfo; 67 import org.apache.batik.gvt.text.TextPath; 68 import org.apache.batik.gvt.text.TextSpanLayout; 69 import org.w3c.dom.Element ; 70 import org.w3c.dom.Node ; 71 import org.w3c.dom.css.CSSPrimitiveValue; 72 import org.w3c.dom.css.CSSValue; 73 import org.w3c.dom.events.Event ; 74 import org.w3c.dom.events.EventListener ; 75 import org.w3c.dom.events.EventTarget ; 76 import org.w3c.dom.events.MutationEvent ; 77 78 85 public class SVGTextElementBridge extends AbstractGraphicsNodeBridge 86 implements SVGTextContent { 87 88 protected final static Integer ZERO = new Integer (0); 89 90 public static final 91 AttributedCharacterIterator.Attribute TEXT_COMPOUND_DELIMITER = 92 GVTAttributedCharacterIterator.TextAttribute.TEXT_COMPOUND_DELIMITER; 93 94 public static final AttributedCharacterIterator.Attribute PAINT_INFO = 95 GVTAttributedCharacterIterator.TextAttribute.PAINT_INFO; 96 97 public static final 98 AttributedCharacterIterator.Attribute ALT_GLYPH_HANDLER = 99 GVTAttributedCharacterIterator.TextAttribute.ALT_GLYPH_HANDLER; 100 101 102 protected AttributedString laidoutText; 103 104 protected WeakHashMap elemTPI = new WeakHashMap (); 107 108 protected boolean usingComplexSVGFont = false; 115 116 119 public SVGTextElementBridge() {} 120 121 124 public String getLocalName() { 125 return SVG_TEXT_TAG; 126 } 127 128 131 public Bridge getInstance() { 132 return new SVGTextElementBridge(); 133 } 134 135 142 public GraphicsNode createGraphicsNode(BridgeContext ctx, Element e) { 143 TextNode node = (TextNode)super.createGraphicsNode(ctx, e); 144 if (node == null) 145 return null; 146 147 if (ctx.getTextPainter() != null) 149 node.setTextPainter(ctx.getTextPainter()); 150 151 RenderingHints hints = null; 153 hints = CSSUtilities.convertColorRendering(e, hints); 154 hints = CSSUtilities.convertTextRendering (e, hints); 155 if (hints != null) 156 node.setRenderingHints(hints); 157 158 node.setLocation(getLocation(ctx, e)); 159 160 return node; 161 } 162 163 167 protected GraphicsNode instantiateGraphicsNode() { 168 return new TextNode(); 169 } 170 171 178 protected Point2D getLocation(BridgeContext ctx, Element e) { 179 UnitProcessor.Context uctx = UnitProcessor.createContext(ctx, e); 180 181 String s = e.getAttributeNS(null, SVG_X_ATTRIBUTE); 183 float x = 0; 184 if (s.length() != 0) { 185 StringTokenizer st = new StringTokenizer (s, ", ", false); 186 x = UnitProcessor.svgHorizontalCoordinateToUserSpace 187 (st.nextToken(), SVG_X_ATTRIBUTE, uctx); 188 } 189 190 s = e.getAttributeNS(null, SVG_Y_ATTRIBUTE); 192 float y = 0; 193 if (s.length() != 0) { 194 StringTokenizer st = new StringTokenizer (s, ", ", false); 195 y = UnitProcessor.svgVerticalCoordinateToUserSpace 196 (st.nextToken(), SVG_Y_ATTRIBUTE, uctx); 197 } 198 199 return new Point2D.Float (x, y); 200 } 201 202 protected boolean isTextElement(Element e) { 203 if (!SVG_NAMESPACE_URI.equals(e.getNamespaceURI())) 204 return false; 205 String nodeName = e.getLocalName(); 206 return (nodeName.equals(SVG_TEXT_TAG) || 207 nodeName.equals(SVG_TSPAN_TAG) || 208 nodeName.equals(SVG_ALT_GLYPH_TAG) || 209 nodeName.equals(SVG_A_TAG) || 210 nodeName.equals(SVG_TEXT_PATH_TAG) || 211 nodeName.equals(SVG_TREF_TAG)); 212 } 213 214 protected boolean isTextChild(Element e) { 215 if (!SVG_NAMESPACE_URI.equals(e.getNamespaceURI())) 216 return false; 217 String nodeName = e.getLocalName(); 218 return (nodeName.equals(SVG_TSPAN_TAG) || 219 nodeName.equals(SVG_ALT_GLYPH_TAG) || 220 nodeName.equals(SVG_A_TAG) || 221 nodeName.equals(SVG_TEXT_PATH_TAG) || 222 nodeName.equals(SVG_TREF_TAG)); 223 } 224 225 233 public void buildGraphicsNode(BridgeContext ctx, 234 Element e, 235 GraphicsNode node) { 236 e.normalize(); 237 computeLaidoutText(ctx, e, node); 238 239 node.setComposite(CSSUtilities.convertOpacity(e)); 244 node.setFilter(CSSUtilities.convertFilter(e, node, ctx)); 246 node.setMask(CSSUtilities.convertMask(e, node, ctx)); 248 node.setClip(CSSUtilities.convertClipPath(e, node, ctx)); 250 node.setPointerEventType(CSSUtilities.convertPointerEvents(e)); 252 253 initializeDynamicSupport(ctx, e, node); 254 } 255 256 259 public boolean isComposite() { 260 return false; 261 } 262 263 265 268 protected DOMChildNodeRemovedEventListener childNodeRemovedEventListener = 269 new DOMChildNodeRemovedEventListener(); 270 271 274 protected class DOMChildNodeRemovedEventListener implements EventListener { 275 276 279 public void handleEvent(Event evt) { 280 handleDOMChildNodeRemovedEvent((MutationEvent )evt); 281 } 282 } 283 284 287 protected DOMSubtreeModifiedEventListener subtreeModifiedEventListener = 288 new DOMSubtreeModifiedEventListener(); 289 290 293 protected class DOMSubtreeModifiedEventListener implements EventListener { 294 295 298 public void handleEvent(Event evt) { 299 handleDOMSubtreeModifiedEvent((MutationEvent )evt); 300 } 301 } 302 303 305 310 protected void initializeDynamicSupport(BridgeContext ctx, 311 Element e, 312 GraphicsNode node) { 313 super.initializeDynamicSupport(ctx,e,node); 314 315 if (!ctx.isDynamic()) 316 return; 318 319 EventTarget evtTarget = (EventTarget )e; 320 321 evtTarget.addEventListener 324 ("DOMNodeRemoved", childNodeRemovedEventListener, true); 325 ctx.storeEventListener 326 (evtTarget, "DOMNodeRemoved", childNodeRemovedEventListener, true); 327 328 evtTarget.addEventListener 331 ("DOMSubtreeModified", subtreeModifiedEventListener, false); 332 ctx.storeEventListener 333 (evtTarget, "DOMSubtreeModified", subtreeModifiedEventListener, false); 334 335 Node child = e.getFirstChild(); 338 while (child != null) { 339 if (child.getNodeType() == Node.ELEMENT_NODE) { 340 addContextToChild(ctx,(Element)child); 341 } 342 child = child.getNextSibling(); 343 } 344 } 345 346 358 protected void addContextToChild(BridgeContext ctx,Element e) { 359 if (SVG_NAMESPACE_URI.equals(e.getNamespaceURI())) { 360 if (e.getLocalName().equals(SVG_TSPAN_TAG)) { 361 ((SVGOMElement)e).setSVGContext 362 (new TspanBridge(ctx, this, e)); 363 } else if (e.getLocalName().equals(SVG_TEXT_PATH_TAG)) { 364 ((SVGOMElement)e).setSVGContext 365 (new TextPathBridge(ctx, this, e)); 366 } else if (e.getLocalName().equals(SVG_TREF_TAG)) { 367 ((SVGOMElement)e).setSVGContext 368 (new TRefBridge(ctx, this, e)); 369 } 370 } 371 372 Node child = e.getFirstChild(); 373 while (child != null) { 374 if (child.getNodeType() == Node.ELEMENT_NODE) { 375 addContextToChild(ctx, (Element)child); 376 } 377 child = child.getNextSibling(); 378 } 379 } 380 381 384 public void handleDOMNodeInsertedEvent(MutationEvent evt) { 385 Node childNode = (Node)evt.getTarget(); 386 387 switch( childNode.getNodeType() ){ 391 case Node.TEXT_NODE: 392 case Node.CDATA_SECTION_NODE: 393 laidoutText = null; 394 break; 395 case Node.ELEMENT_NODE: { 396 Element childElement = (Element)childNode; 397 if (isTextChild(childElement)) { 398 addContextToChild( ctx, childElement); 399 laidoutText = null; 400 } 401 } 402 break; 403 default: 404 } 405 if (laidoutText == null) { 406 computeLaidoutText(ctx, e, node); 407 } 408 } 409 410 413 public void handleDOMNodeRemovedEvent(MutationEvent evt) { 414 EventTarget evtTarget = evt.getTarget(); 415 evtTarget.removeEventListener("DOMNodeRemoved", 416 childNodeRemovedEventListener, 417 true); 418 evtTarget.removeEventListener("DOMSubtreeModified", 419 subtreeModifiedEventListener, 420 false); 421 super.handleDOMNodeRemovedEvent(evt); 422 } 423 424 427 public void handleDOMChildNodeRemovedEvent(MutationEvent evt) { 428 Node childNode = (Node)evt.getTarget(); 429 430 switch (childNode.getNodeType()) { 434 case Node.TEXT_NODE: 435 case Node.CDATA_SECTION_NODE: 436 if (isParentDisplayed( childNode)) { 438 laidoutText = null; 439 } 440 break; 441 case Node.ELEMENT_NODE: 442 if (isTextChild((Element)childNode)) { 443 laidoutText = null; 444 } 445 break; 446 default: 447 } 448 } 451 452 455 public void handleDOMSubtreeModifiedEvent(MutationEvent evt){ 456 if (laidoutText == null) { 459 computeLaidoutText(ctx, e, node); 460 } 461 } 462 463 467 public void handleDOMCharacterDataModified(MutationEvent evt){ 468 Node childNode = (Node)evt.getTarget(); 469 if (isParentDisplayed(childNode)) { 471 laidoutText = null; 472 } 473 } 474 475 485 protected boolean isParentDisplayed(Node childNode) { 486 Node parentNode = childNode.getParentNode(); 487 return isTextElement((Element)parentNode); 488 } 489 490 498 protected void computeLaidoutText(BridgeContext ctx, 499 Element e, 500 GraphicsNode node) { 501 AttributedString as = buildAttributedString(ctx, e); 502 addGlyphPositionAttributes(as, e, ctx); 503 if (ctx.isDynamic()) { 504 laidoutText = new AttributedString (as.getIterator()); 505 } 506 TextNode tn = (TextNode)node; 507 elemTPI.clear(); 508 addNullPaintAttributes(as, e, ctx); 511 512 tn.setAttributedCharacterIterator(as.getIterator()); 514 515 TextPaintInfo pi = new TextPaintInfo(); 519 setBaseTextPaintInfo(pi, e, node, ctx); 520 setDecorationTextPaintInfo(pi, e); 522 addPaintAttributes(as, e, tn, pi, ctx); 524 525 if (usingComplexSVGFont) { 526 tn.setAttributedCharacterIterator(as.getIterator()); 528 } 529 } 530 531 536 protected void addNullPaintAttributes(AttributedString as, 537 Element element, 538 BridgeContext ctx) { 539 if ((!SVGUtilities.matchUserAgent(element, ctx.getUserAgent())) || 541 (!CSSUtilities.convertDisplay(element))) { 542 return; 543 } 544 545 AttributedCharacterIterator aci = as.getIterator(); 546 547 int firstChar = getElementStartIndex(aci, element); 549 if (firstChar == -1) 550 return; 552 int lastChar = getElementEndIndex(aci, element); 553 TextPaintInfo pi = new TextPaintInfo(); 554 pi.visible = true; 556 pi.fillPaint = Color.black; 557 558 as.addAttribute(PAINT_INFO, pi, firstChar, lastChar+1); 559 elemTPI.put(element, pi); 560 561 addChildNullPaintAttributes(as, element, ctx); 562 } 563 564 protected void addChildNullPaintAttributes(AttributedString as, 565 Element element, 566 BridgeContext ctx) { 567 for (Node child = element.getFirstChild(); 569 child != null; 570 child = child.getNextSibling()) { 571 if (child.getNodeType() != Node.ELEMENT_NODE) 572 continue; 573 574 Element childElement = (Element)child; 575 if (isTextChild(childElement)) { 576 addNullPaintAttributes(as, childElement, ctx); 577 } 578 } 579 } 580 581 586 private boolean hasNewACI; 587 588 591 private Element cssProceedElement; 592 593 596 public void handleDOMAttrModifiedEvent(MutationEvent evt) { 597 String attrName = evt.getAttrName(); 598 if (attrName.equals(SVG_X_ATTRIBUTE) || 599 attrName.equals(SVG_Y_ATTRIBUTE) || 600 attrName.equals(SVG_DX_ATTRIBUTE) || 601 attrName.equals(SVG_DY_ATTRIBUTE) || 602 attrName.equals(SVG_ROTATE_ATTRIBUTE) ){ 603 604 if ( attrName.equals(SVG_X_ATTRIBUTE) || 605 attrName.equals(SVG_Y_ATTRIBUTE)){ 606 ((TextNode)node).setLocation(getLocation(ctx, e)); 607 } 608 609 computeLaidoutText(ctx, e, node); 610 } 611 else{ 612 super.handleDOMAttrModifiedEvent(evt); 613 } 614 } 615 616 621 public void handleCSSEngineEvent(CSSEngineEvent evt) { 622 hasNewACI = false; 623 int [] properties = evt.getProperties(); 624 for (int i=0; i < properties.length; ++i) { 626 switch(properties[i]) { 627 case SVGCSSEngine.BASELINE_SHIFT_INDEX: 628 case SVGCSSEngine.DIRECTION_INDEX: 629 case SVGCSSEngine.DISPLAY_INDEX: 630 case SVGCSSEngine.FONT_FAMILY_INDEX: 631 case SVGCSSEngine.FONT_SIZE_INDEX: 632 case SVGCSSEngine.FONT_STRETCH_INDEX: 633 case SVGCSSEngine.FONT_STYLE_INDEX: 634 case SVGCSSEngine.FONT_WEIGHT_INDEX: 635 case SVGCSSEngine.GLYPH_ORIENTATION_HORIZONTAL_INDEX: 636 case SVGCSSEngine.GLYPH_ORIENTATION_VERTICAL_INDEX: 637 case SVGCSSEngine.KERNING_INDEX: 638 case SVGCSSEngine.LETTER_SPACING_INDEX: 639 case SVGCSSEngine.TEXT_ANCHOR_INDEX: 640 case SVGCSSEngine.UNICODE_BIDI_INDEX: 641 case SVGCSSEngine.WORD_SPACING_INDEX: 642 case SVGCSSEngine.WRITING_MODE_INDEX: { 643 if (!hasNewACI) { 644 hasNewACI = true; 645 computeLaidoutText(ctx, e, node); 646 } 647 break; 648 } 649 } 650 } 651 cssProceedElement = evt.getElement(); 655 super.handleCSSEngineEvent(evt); 657 cssProceedElement = null; 658 } 659 660 663 protected void handleCSSPropertyChanged(int property) { 664 switch(property) { 665 case SVGCSSEngine.FILL_INDEX: 666 case SVGCSSEngine.FILL_OPACITY_INDEX: 667 case SVGCSSEngine.STROKE_INDEX: 668 case SVGCSSEngine.STROKE_OPACITY_INDEX: 669 case SVGCSSEngine.STROKE_WIDTH_INDEX: 670 case SVGCSSEngine.STROKE_LINECAP_INDEX: 671 case SVGCSSEngine.STROKE_LINEJOIN_INDEX: 672 case SVGCSSEngine.STROKE_MITERLIMIT_INDEX: 673 case SVGCSSEngine.STROKE_DASHARRAY_INDEX: 674 case SVGCSSEngine.STROKE_DASHOFFSET_INDEX: 675 case SVGCSSEngine.TEXT_DECORATION_INDEX: 676 rebuildACI(); 677 break; 678 679 case SVGCSSEngine.VISIBILITY_INDEX: 680 rebuildACI(); 681 super.handleCSSPropertyChanged(property); 682 break; 683 case SVGCSSEngine.TEXT_RENDERING_INDEX: { 684 RenderingHints hints = node.getRenderingHints(); 685 hints = CSSUtilities.convertTextRendering(e, hints); 686 if (hints != null) { 687 node.setRenderingHints(hints); 688 } 689 break; 690 } 691 case SVGCSSEngine.COLOR_RENDERING_INDEX: { 692 RenderingHints hints = node.getRenderingHints(); 693 hints = CSSUtilities.convertColorRendering(e, hints); 694 if (hints != null) { 695 node.setRenderingHints(hints); 696 } 697 break; 698 } 699 default: 700 super.handleCSSPropertyChanged(property); 701 } 702 } 703 704 protected void rebuildACI() { 705 if (hasNewACI) 706 return; 707 708 TextNode tn = (TextNode)node; 709 710 TextPaintInfo pi, oldPI; 711 if ( cssProceedElement == e ){ 712 pi = new TextPaintInfo(); 713 setBaseTextPaintInfo(pi, e, node, ctx); 714 setDecorationTextPaintInfo(pi, e); 715 oldPI = (TextPaintInfo)elemTPI.get(e); 716 } else { 717 TextPaintInfo parentPI; 722 parentPI = getParentTextPaintInfo 723 (tn.getAttributedCharacterIterator(), cssProceedElement); 724 pi = getTextPaintInfo(cssProceedElement, tn, parentPI, ctx); 725 oldPI = (TextPaintInfo)elemTPI.get(cssProceedElement); 726 } 727 if (oldPI == null) return; 728 729 tn.swapTextPaintInfo(pi, oldPI); 730 if (usingComplexSVGFont) 731 tn.setAttributedCharacterIterator 733 (tn.getAttributedCharacterIterator()); 734 } 735 736 int getElementStartIndex 737 (AttributedCharacterIterator aci, Element element) { 738 for (int i = 0; i < aci.getEndIndex();) { 740 aci.setIndex(i); 741 Element delimeter; 742 delimeter = (Element)aci.getAttribute(TEXT_COMPOUND_DELIMITER); 743 if (delimeter == element || nodeAncestorOf(element, delimeter)) 744 return i; 745 i = aci.getRunLimit(TEXT_COMPOUND_DELIMITER); 746 } 747 return -1; 748 } 749 750 int getElementEndIndex 751 (AttributedCharacterIterator aci, Element element) { 752 for (int i = aci.getEndIndex()-1; i >= 0;) { 754 aci.setIndex(i); 755 Element delimeter; 756 delimeter = (Element)aci.getAttribute(TEXT_COMPOUND_DELIMITER); 757 if (delimeter == element || nodeAncestorOf(element, delimeter)) 758 return i; 759 i = aci.getRunStart(TEXT_COMPOUND_DELIMITER)-1; 760 } 761 return -1; 762 } 763 764 769 776 protected AttributedString buildAttributedString(BridgeContext ctx, 777 Element element) { 778 779 AttributedStringBuffer asb = new AttributedStringBuffer(); 780 fillAttributedStringBuffer(ctx, element, true, null, null, asb); 781 return asb.toAttributedString(); 782 } 783 784 785 791 protected int endLimit; 792 793 796 protected void fillAttributedStringBuffer(BridgeContext ctx, 797 Element element, 798 boolean top, 799 TextPath textPath, 800 Integer bidiLevel, 801 AttributedStringBuffer asb) { 802 if ((!SVGUtilities.matchUserAgent(element, ctx.getUserAgent())) || 805 (!CSSUtilities.convertDisplay(element))) { 806 return; 807 } 808 809 String s = XMLSupport.getXMLSpace(element); 810 boolean preserve = s.equals(SVG_PRESERVE_VALUE); 811 boolean prevEndsWithSpace; 812 Element nodeElement = element; 813 814 if (top) 815 endLimit = 0; 816 if (preserve) 817 endLimit = asb.length(); 818 819 Map map = getAttributeMap(ctx, element, textPath, bidiLevel); 820 Object o = map.get(TextAttribute.BIDI_EMBEDDING); 821 Integer subBidiLevel = bidiLevel; 822 if (o != null) 823 subBidiLevel = ((Integer )o); 824 825 for (Node n = element.getFirstChild(); 826 n != null; 827 n = n.getNextSibling()) { 828 829 if (preserve) { 830 prevEndsWithSpace = false; 831 } else { 832 if (asb.length() == 0) 833 prevEndsWithSpace = true; 834 else 835 prevEndsWithSpace = (asb.getLastChar() == ' '); 836 } 837 838 switch (n.getNodeType()) { 839 case Node.ELEMENT_NODE: 840 if (!SVG_NAMESPACE_URI.equals(n.getNamespaceURI())) 841 break; 842 843 nodeElement = (Element)n; 844 845 String ln = n.getLocalName(); 846 847 if (ln.equals(SVG_TSPAN_TAG) || 848 ln.equals(SVG_ALT_GLYPH_TAG)) { 849 fillAttributedStringBuffer(ctx, 850 nodeElement, 851 false, 852 textPath, 853 subBidiLevel, 854 asb); 855 } else if (ln.equals(SVG_TEXT_PATH_TAG)) { 856 SVGTextPathElementBridge textPathBridge 857 = (SVGTextPathElementBridge)ctx.getBridge(nodeElement); 858 TextPath newTextPath 859 = textPathBridge.createTextPath(ctx, nodeElement); 860 if (newTextPath != null) { 861 fillAttributedStringBuffer(ctx, 862 nodeElement, 863 false, 864 newTextPath, 865 subBidiLevel, 866 asb); 867 } 868 } else if (ln.equals(SVG_TREF_TAG)) { 869 String uriStr = XLinkSupport.getXLinkHref((Element)n); 870 Element ref = ctx.getReferencedElement((Element)n, uriStr); 871 s = TextUtilities.getElementContent(ref); 872 s = normalizeString(s, preserve, prevEndsWithSpace); 873 if (s != null) { 874 Map m = getAttributeMap(ctx, nodeElement, 875 textPath, bidiLevel); 876 asb.append(s, m); 877 } 878 } else if (ln.equals(SVG_A_TAG)) { 879 EventTarget target = (EventTarget )nodeElement; 880 UserAgent ua = ctx.getUserAgent(); 881 EventListener l = new SVGAElementBridge.AnchorListener(ua); 882 target.addEventListener(SVG_EVENT_CLICK, l, false); 883 ctx.storeEventListener(target, SVG_EVENT_CLICK, l, false); 884 885 fillAttributedStringBuffer(ctx, 886 nodeElement, 887 false, 888 textPath, 889 subBidiLevel, 890 asb); 891 } 892 break; 893 case Node.TEXT_NODE: 894 case Node.CDATA_SECTION_NODE: 895 s = n.getNodeValue(); 896 s = normalizeString(s, preserve, prevEndsWithSpace); 897 asb.append(s, map); 898 if (preserve) 899 endLimit = asb.length(); 900 } 901 } 902 903 if (top) { 904 while ((endLimit < asb.length()) && (asb.getLastChar() == ' ')) 905 asb.stripLast(); 906 } 907 } 908 909 912 protected String normalizeString(String s, 913 boolean preserve, 914 boolean stripfirst) { 915 StringBuffer sb = new StringBuffer (s.length()); 916 if (preserve) { 917 for (int i = 0; i < s.length(); i++) { 918 char c = s.charAt(i); 919 switch (c) { 920 case 10: 921 case 13: 922 case '\t': 923 sb.append(' '); 924 break; 925 default: 926 sb.append(c); 927 } 928 } 929 return sb.toString(); 930 } 931 932 int idx = 0; 933 if (stripfirst) { 934 loop: while (idx < s.length()) { 935 switch (s.charAt(idx)) { 936 default: 937 break loop; 938 case 10: 939 case 13: 940 case ' ': 941 case '\t': 942 idx++; 943 } 944 } 945 } 946 947 boolean space = false; 948 for (int i = idx; i < s.length(); i++) { 949 char c = s.charAt(i); 950 switch (c) { 951 case 10: 952 case 13: 953 break; 954 case ' ': 955 case '\t': 956 if (!space) { 957 sb.append(' '); 958 space = true; 959 } 960 break; 961 default: 962 sb.append(c); 963 space = false; 964 } 965 } 966 967 return sb.toString(); 968 } 969 970 973 protected static class AttributedStringBuffer { 974 975 978 protected List strings; 979 980 983 protected List attributes; 984 985 988 protected int count; 989 990 993 protected int length; 994 995 998 public AttributedStringBuffer() { 999 strings = new ArrayList (); 1000 attributes = new ArrayList (); 1001 count = 0; 1002 length = 0; 1003 } 1004 1005 1008 public boolean isEmpty() { 1009 return count == 0; 1010 } 1011 1012 1015 public int length() { 1016 return length; 1017 } 1018 1019 1022 public void append(String s, Map m) { 1023 if (s.length() == 0) return; 1024 strings.add(s); 1025 attributes.add(m); 1026 count++; 1027 length += s.length(); 1028 } 1029 1030 1033 public int getLastChar() { 1034 if (count == 0) { 1035 return -1; 1036 } 1037 String s = (String )strings.get(count - 1); 1038 return s.charAt(s.length() - 1); 1039 } 1040 1041 1044 public void stripFirst() { 1045 String s = (String )strings.get(0); 1046 if (s.charAt(s.length() - 1) != ' ') 1047 return; 1048 1049 length--; 1050 1051 if (s.length() == 1) { 1052 attributes.remove(0); 1053 strings.remove(0); 1054 count--; 1055 return; 1056 } 1057 1058 strings.set(0, s.substring(1)); 1059 } 1060 1061 1064 public void stripLast() { 1065 String s = (String )strings.get(count - 1); 1066 if (s.charAt(s.length() - 1) != ' ') 1067 return; 1068 1069 length--; 1070 1071 if (s.length() == 1) { 1072 attributes.remove(--count); 1073 strings.remove(count); 1074 return; 1075 } 1076 1077 strings.set(count-1, s.substring(0, s.length() - 1)); 1078 } 1079 1080 1084 public AttributedString toAttributedString() { 1085 switch (count) { 1086 case 0: 1087 return new AttributedString (" "); 1088 case 1: 1089 return new AttributedString ((String )strings.get(0), 1090 (Map )attributes.get(0)); 1091 } 1092 1093 StringBuffer sb = new StringBuffer (); 1094 Iterator it = strings.iterator(); 1095 while (it.hasNext()) { 1096 sb.append((String )it.next()); 1097 } 1098 1099 AttributedString result = new AttributedString (sb.toString()); 1100 1101 1103 Iterator sit = strings.iterator(); 1104 Iterator ait = attributes.iterator(); 1105 int idx = 0; 1106 while (sit.hasNext()) { 1107 String s = (String )sit.next(); 1108 int nidx = idx + s.length(); 1109 Map m = (Map )ait.next(); 1110 Iterator kit = m.keySet().iterator(); 1111 Iterator vit = m.values().iterator(); 1112 while (kit.hasNext()) { 1113 Attribute attr = (Attribute )kit.next(); 1114 Object val = vit.next(); 1115 result.addAttribute(attr, val, idx, nidx); 1116 } 1117 idx = nidx; 1118 } 1119 1120 return result; 1121 } 1122 1123 public String toString() { 1124 switch (count) { 1125 case 0: 1126 return ""; 1127 case 1: 1128 return (String )strings.get(0); 1129 } 1130 1131 StringBuffer sb = new StringBuffer (); 1132 Iterator it = strings.iterator(); 1133 while (it.hasNext()) { 1134 sb.append((String )it.next()); 1135 } 1136 return sb.toString(); 1137 } 1138 } 1139 1140 1143 protected boolean nodeAncestorOf(Node node1, Node node2) { 1144 if (node2 == null || node1 == null) { 1145 return false; 1146 } 1147 Node parent = node2.getParentNode(); 1148 while (parent != null && parent != node1) { 1149 parent = parent.getParentNode(); 1150 } 1151 return (parent == node1); 1152 } 1153 1154 1155 1158 protected void addGlyphPositionAttributes(AttributedString as, 1159 Element element, 1160 BridgeContext ctx) { 1161 1162 if ((!SVGUtilities.matchUserAgent(element, ctx.getUserAgent())) || 1164 (!CSSUtilities.convertDisplay(element))) { 1165 return; 1166 } 1167 if (element.getLocalName().equals(SVG_TEXT_PATH_TAG)) { 1168 addChildGlyphPositionAttributes(as, element, ctx); 1170 return; 1171 } 1172 1173 AttributedCharacterIterator aci = as.getIterator(); 1174 1175 int firstChar = getElementStartIndex(aci, element); 1177 if (firstChar == -1) return; 1179 1180 int lastChar = getElementEndIndex(aci, element); 1181 1182 String xAtt = element.getAttributeNS(null, SVG_X_ATTRIBUTE); 1184 String yAtt = element.getAttributeNS(null, SVG_Y_ATTRIBUTE); 1185 String dxAtt = element.getAttributeNS(null, SVG_DX_ATTRIBUTE); 1186 String dyAtt = element.getAttributeNS(null, SVG_DY_ATTRIBUTE); 1187 String rotateAtt = element.getAttributeNS(null, SVG_ROTATE_ATTRIBUTE); 1188 1189 ArrayList al; 1190 int len; 1191 1192 if (xAtt.length() != 0) { 1194 al = TextUtilities.svgHorizontalCoordinateArrayToUserSpace 1195 (element, SVG_X_ATTRIBUTE, xAtt, ctx); 1196 len = al.size(); 1197 for (int i = 0; i < len; i++) { 1198 if (firstChar + i <= lastChar) { 1199 as.addAttribute 1200 (GVTAttributedCharacterIterator.TextAttribute.X, 1201 al.get(i), firstChar+i, firstChar+i+1); 1202 } 1203 } 1204 } 1205 1206 1207 if (yAtt.length() != 0) { 1209 al = TextUtilities.svgVerticalCoordinateArrayToUserSpace 1210 (element, SVG_Y_ATTRIBUTE, yAtt, ctx); 1211 len = al.size(); 1212 1213 for (int i = 0; i < len; i++) { 1214 if (firstChar+i <= lastChar) { 1215 as.addAttribute 1216 (GVTAttributedCharacterIterator.TextAttribute.Y, 1217 al.get(i), firstChar+i, firstChar+i+1); 1218 } 1219 } 1220 } 1221 1222 if (dxAtt.length() != 0) { 1224 al = TextUtilities.svgHorizontalCoordinateArrayToUserSpace 1225 (element, SVG_DX_ATTRIBUTE, dxAtt, ctx); 1226 len = al.size(); 1227 1228 for (int i = 0; i < len; i++) { 1229 if (firstChar+i <= lastChar) { 1230 as.addAttribute 1231 (GVTAttributedCharacterIterator.TextAttribute.DX, 1232 al.get(i), firstChar+i, firstChar+i+1); 1233 } 1234 } 1235 } 1236 1237 if (dyAtt.length() != 0) { 1239 al = TextUtilities.svgVerticalCoordinateArrayToUserSpace 1240 (element, SVG_DY_ATTRIBUTE, dyAtt, ctx); 1241 len = al.size(); 1242 1243 for (int i = 0; i < len; i++) { 1244 if (firstChar+i <= lastChar) { 1245 as.addAttribute 1246 (GVTAttributedCharacterIterator.TextAttribute.DY, 1247 al.get(i), firstChar+i, firstChar+i+1); 1248 } 1249 } 1250 } 1251 1252 if (rotateAtt.length() != 0) { 1254 al = TextUtilities.svgRotateArrayToFloats 1255 (element, SVG_ROTATE_ATTRIBUTE, rotateAtt, ctx); 1256 len = al.size(); 1257 1258 if (len == 1) { as.addAttribute 1261 (GVTAttributedCharacterIterator.TextAttribute.ROTATION, 1262 al.get(0), firstChar, lastChar + 1); 1263 1264 } else { for (int i = 0; i < len; i++) { 1267 if (firstChar+i <= lastChar) { 1268 as.addAttribute 1269 (GVTAttributedCharacterIterator. 1270 TextAttribute.ROTATION, 1271 al.get(i), firstChar+i, firstChar+i+1); 1272 } 1273 } 1274 } 1275 } 1276 1277 addChildGlyphPositionAttributes(as, element, ctx); 1278 } 1279 1280 protected void addChildGlyphPositionAttributes(AttributedString as, 1281 Element element, 1282 BridgeContext ctx) { 1283 for (Node child = element.getFirstChild(); 1285 child != null; 1286 child = child.getNextSibling()) { 1287 if (child.getNodeType() != Node.ELEMENT_NODE) continue; 1288 1289 Element childElement = (Element)child; 1290 if (isTextChild(childElement)) { 1291 addGlyphPositionAttributes(as, childElement, ctx); 1292 } 1293 } 1294 } 1295 1296 1299 protected void addPaintAttributes(AttributedString as, 1300 Element element, 1301 TextNode node, 1302 TextPaintInfo pi, 1303 BridgeContext ctx) { 1304 if ((!SVGUtilities.matchUserAgent(element, ctx.getUserAgent())) || 1306 (!CSSUtilities.convertDisplay(element))) { 1307 return; 1308 } 1309 Object o = elemTPI.get(element); 1310 if (o == null) return; 1311 node.swapTextPaintInfo(pi, (TextPaintInfo)o); 1312 addChildPaintAttributes(as, element, node, pi, ctx); 1313 } 1314 1315 protected void addChildPaintAttributes(AttributedString as, 1316 Element element, 1317 TextNode node, 1318 TextPaintInfo parentPI, 1319 BridgeContext ctx) { 1320 for (Node child = element.getFirstChild(); 1322 child != null; 1323 child = child.getNextSibling()) { 1324 if (child.getNodeType() != Node.ELEMENT_NODE) { 1325 continue; 1326 } 1327 Element childElement = (Element)child; 1328 if (isTextChild(childElement)) { 1329 TextPaintInfo pi = getTextPaintInfo(childElement, node, 1330 parentPI, ctx); 1331 addPaintAttributes(as, childElement, node, pi, ctx); 1332 } 1333 } 1334 } 1335 1336 protected Map getFontProperties(BridgeContext ctx, Element element, 1337 Map map) { 1338 if (map == null) map = new HashMap (4); 1339 1340 map.put(TEXT_COMPOUND_DELIMITER, element); 1342 1343 map.put(TextAttribute.SIZE, TextUtilities.convertFontSize(element)); 1345 1346 map.put(TextAttribute.WIDTH, TextUtilities.convertFontStretch(element)); 1348 1349 map.put(TextAttribute.WEIGHT, TextUtilities.convertFontWeight(element)); 1351 1352 map.put(TextAttribute.POSTURE, TextUtilities.convertFontStyle(element)); 1354 1355 return map; 1356 } 1357 1358 1361 protected Map getAttributeMap(BridgeContext ctx, 1362 Element element, 1363 TextPath textPath, 1364 Integer bidiLevel) { 1365 UnitProcessor.Context uctx = UnitProcessor.createContext(ctx, element); 1366 1367 Map result = new HashMap (); 1368 String s; 1369 float f; 1370 1371 if (SVG_NAMESPACE_URI.equals(element.getNamespaceURI()) && 1372 element.getLocalName().equals(SVG_ALT_GLYPH_TAG)) { 1373 result.put(ALT_GLYPH_HANDLER, 1374 new SVGAltGlyphHandler(ctx, element)); 1375 } 1376 1377 if (textPath != null) { 1378 result.put(GVTAttributedCharacterIterator.TextAttribute.TEXTPATH, 1379 textPath); 1380 } 1381 1382 TextNode.Anchor a = TextUtilities.convertTextAnchor(element); 1384 result.put(GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE, 1385 a); 1386 1387 getFontProperties(ctx, element, result); 1389 1390 List fontFamilyList = getFontFamilyList(element, ctx); 1392 result.put 1393 (GVTAttributedCharacterIterator.TextAttribute.GVT_FONT_FAMILIES, 1394 fontFamilyList); 1395 1396 Object bs = TextUtilities.convertBaselineShift(element); 1398 if (bs != null) { 1399 result.put(GVTAttributedCharacterIterator. 1400 TextAttribute.BASELINE_SHIFT, bs); 1401 } 1402 1403 Value val = CSSUtilities.getComputedStyle 1405 (element, SVGCSSEngine.UNICODE_BIDI_INDEX); 1406 s = val.getStringValue(); 1407 if (s.charAt(0) == 'n') { 1408 if (bidiLevel != null) 1409 result.put(TextAttribute.BIDI_EMBEDDING, bidiLevel); 1410 } else { 1411 1412 1422 val = CSSUtilities.getComputedStyle 1423 (element, SVGCSSEngine.DIRECTION_INDEX); 1424 String rs = val.getStringValue(); 1425 int cbidi = 0; 1426 if (bidiLevel != null) cbidi = bidiLevel.intValue(); 1427 1428 if (cbidi < 0) cbidi = -cbidi; 1431 1432 switch (rs.charAt(0)) { 1433 case 'l': 1434 result.put(TextAttribute.RUN_DIRECTION, 1435 TextAttribute.RUN_DIRECTION_LTR); 1436 if ((cbidi & 0x1) == 1) cbidi++; else cbidi+=2; break; 1439 case 'r': 1440 result.put(TextAttribute.RUN_DIRECTION, 1441 TextAttribute.RUN_DIRECTION_RTL); 1442 if ((cbidi & 0x1) == 1) cbidi+=2; else cbidi++; break; 1445 } 1446 1447 switch (s.charAt(0)) { 1448 case 'b': cbidi = -cbidi; break; 1451 } 1452 1453 result.put(TextAttribute.BIDI_EMBEDDING, new Integer (cbidi)); 1454 } 1455 1456 1458 val = CSSUtilities.getComputedStyle 1459 (element, SVGCSSEngine.WRITING_MODE_INDEX); 1460 s = val.getStringValue(); 1461 switch (s.charAt(0)) { 1462 case 'l': 1463 result.put(GVTAttributedCharacterIterator. 1464 TextAttribute.WRITING_MODE, 1465 GVTAttributedCharacterIterator. 1466 TextAttribute.WRITING_MODE_LTR); 1467 break; 1468 case 'r': 1469 result.put(GVTAttributedCharacterIterator. 1470 TextAttribute.WRITING_MODE, 1471 GVTAttributedCharacterIterator. 1472 TextAttribute.WRITING_MODE_RTL); 1473 break; 1474 case 't': 1475 result.put(GVTAttributedCharacterIterator. 1476 TextAttribute.WRITING_MODE, 1477 GVTAttributedCharacterIterator. 1478 TextAttribute.WRITING_MODE_TTB); 1479 break; 1480 } 1481 1482 1484 val = CSSUtilities.getComputedStyle 1485 (element, SVGCSSEngine.GLYPH_ORIENTATION_VERTICAL_INDEX); 1486 switch (val.getPrimitiveType()) { 1487 case CSSPrimitiveValue.CSS_IDENT: result.put(GVTAttributedCharacterIterator. 1489 TextAttribute.VERTICAL_ORIENTATION, 1490 GVTAttributedCharacterIterator. 1491 TextAttribute.ORIENTATION_AUTO); 1492 break; 1493 case CSSPrimitiveValue.CSS_DEG: 1494 result.put(GVTAttributedCharacterIterator. 1495 TextAttribute.VERTICAL_ORIENTATION, 1496 GVTAttributedCharacterIterator. 1497 TextAttribute.ORIENTATION_ANGLE); 1498 result.put(GVTAttributedCharacterIterator. 1499 TextAttribute.VERTICAL_ORIENTATION_ANGLE, 1500 new Float (val.getFloatValue())); 1501 break; 1502 case CSSPrimitiveValue.CSS_RAD: 1503 result.put(GVTAttributedCharacterIterator. 1504 TextAttribute.VERTICAL_ORIENTATION, 1505 GVTAttributedCharacterIterator. 1506 TextAttribute.ORIENTATION_ANGLE); 1507 result.put(GVTAttributedCharacterIterator. 1508 TextAttribute.VERTICAL_ORIENTATION_ANGLE, 1509 new Float (val.getFloatValue() * 180 / Math.PI)); 1510 break; 1511 case CSSPrimitiveValue.CSS_GRAD: 1512 result.put(GVTAttributedCharacterIterator. 1513 TextAttribute.VERTICAL_ORIENTATION, 1514 GVTAttributedCharacterIterator. 1515 TextAttribute.ORIENTATION_ANGLE); 1516 result.put(GVTAttributedCharacterIterator. 1517 TextAttribute.VERTICAL_ORIENTATION_ANGLE, 1518 new Float (val.getFloatValue() * 9 / 5)); 1519 break; 1520 default: 1521 throw new InternalError (); 1523 } 1524 1525 1527 val = CSSUtilities.getComputedStyle 1528 (element, SVGCSSEngine.GLYPH_ORIENTATION_HORIZONTAL_INDEX); 1529 switch (val.getPrimitiveType()) { 1530 case CSSPrimitiveValue.CSS_DEG: 1531 result.put(GVTAttributedCharacterIterator. 1532 TextAttribute.HORIZONTAL_ORIENTATION_ANGLE, 1533 new Float (val.getFloatValue())); 1534 break; 1535 case CSSPrimitiveValue.CSS_RAD: 1536 result.put(GVTAttributedCharacterIterator. 1537 TextAttribute.HORIZONTAL_ORIENTATION_ANGLE, 1538 new Float (val.getFloatValue() * 180 / Math.PI)); 1539 break; 1540 case CSSPrimitiveValue.CSS_GRAD: 1541 result.put(GVTAttributedCharacterIterator. 1542 TextAttribute.HORIZONTAL_ORIENTATION_ANGLE, 1543 new Float (val.getFloatValue() * 9 / 5)); 1544 break; 1545 default: 1546 throw new InternalError (); 1548 } 1549 1550 1552 Float sp = TextUtilities.convertLetterSpacing(element); 1554 if (sp != null) { 1555 result.put(GVTAttributedCharacterIterator. 1556 TextAttribute.LETTER_SPACING, 1557 sp); 1558 result.put(GVTAttributedCharacterIterator. 1559 TextAttribute.CUSTOM_SPACING, 1560 Boolean.TRUE); 1561 } 1562 1563 sp = TextUtilities.convertWordSpacing(element); 1565 if (sp != null) { 1566 result.put(GVTAttributedCharacterIterator. 1567 TextAttribute.WORD_SPACING, 1568 sp); 1569 result.put(GVTAttributedCharacterIterator. 1570 TextAttribute.CUSTOM_SPACING, 1571 Boolean.TRUE); 1572 } 1573 1574 sp = TextUtilities.convertKerning(element); 1576 if (sp != null) { 1577 result.put(GVTAttributedCharacterIterator.TextAttribute.KERNING, 1578 sp); 1579 result.put(GVTAttributedCharacterIterator. 1580 TextAttribute.CUSTOM_SPACING, 1581 Boolean.TRUE); 1582 } 1583 1584 s = element.getAttributeNS(null, SVG_TEXT_LENGTH_ATTRIBUTE); 1586 if (s.length() != 0) { 1587 f = UnitProcessor.svgOtherLengthToUserSpace 1588 (s, SVG_TEXT_LENGTH_ATTRIBUTE, uctx); 1589 result.put(GVTAttributedCharacterIterator.TextAttribute.BBOX_WIDTH, 1590 new Float (f)); 1591 1592 s = element.getAttributeNS(null, SVG_LENGTH_ADJUST_ATTRIBUTE); 1594 1595 if (s.length() < 10) { 1596 result.put(GVTAttributedCharacterIterator. 1597 TextAttribute.LENGTH_ADJUST, 1598 GVTAttributedCharacterIterator. 1599 TextAttribute.ADJUST_SPACING); 1600 result.put(GVTAttributedCharacterIterator. 1601 TextAttribute.CUSTOM_SPACING, 1602 Boolean.TRUE); 1603 } else { 1604 result.put(GVTAttributedCharacterIterator. 1605 TextAttribute.LENGTH_ADJUST, 1606 GVTAttributedCharacterIterator. 1607 TextAttribute.ADJUST_ALL); 1608 } 1609 } 1610 1611 return result; 1612 } 1613 1614 1615 protected List getFontFamilyList(Element element, BridgeContext ctx) { 1616 Value v = CSSUtilities.getComputedStyle 1618 (element, SVGCSSEngine.FONT_WEIGHT_INDEX); 1619 String fontWeightString = v.getCssText(); 1620 1621 String fontStyleString = CSSUtilities.getComputedStyle 1623 (element, SVGCSSEngine.FONT_STYLE_INDEX).getStringValue(); 1624 1625 Value val = CSSUtilities.getComputedStyle 1626 (element, SVGCSSEngine.FONT_FAMILY_INDEX); 1627 List fontFamilyList = new ArrayList (); 1629 int len = val.getLength(); 1630 for (int i = 0; i < len; i++) { 1631 Value it = val.item(i); 1632 String fontFamilyName = it.getStringValue(); 1633 GVTFontFamily fontFamily 1634 = SVGFontUtilities.getFontFamily(element, ctx, fontFamilyName, 1635 fontWeightString, 1636 fontStyleString); 1637 if (fontFamily instanceof SVGFontFamily) { 1638 SVGFontFamily svgFF = (SVGFontFamily)fontFamily; 1639 if (svgFF.isComplex()) { 1640 usingComplexSVGFont = true; 1641 } 1642 } 1643 fontFamilyList.add(fontFamily); 1644 } 1645 return fontFamilyList; 1646 } 1647 1648 1657 protected TextPaintInfo getParentTextPaintInfo 1658 (AttributedCharacterIterator aci, Element child) { 1659 Element parent = null; 1660 1661 int firstChar = -1; 1663 for (int i = 0; i < aci.getEndIndex();) { 1664 aci.setIndex(i); 1665 Element delimeter; 1666 delimeter = (Element)aci.getAttribute(TEXT_COMPOUND_DELIMITER); 1667 if (nodeAncestorOf(delimeter,child) && 1668 ((parent == null) || nodeAncestorOf(parent, delimeter))) { 1669 parent = delimeter; 1670 firstChar = i; 1671 } 1672 if (delimeter == child || nodeAncestorOf(child, delimeter)) { 1673 break; 1674 } 1675 i = aci.getRunLimit(TEXT_COMPOUND_DELIMITER); 1676 } 1677 1678 if ( parent == null) 1679 return new TextPaintInfo(); 1681 aci.setIndex(firstChar); 1682 return (TextPaintInfo)aci.getAttribute(PAINT_INFO); 1683 } 1684 1685 1690 protected TextPaintInfo getTextPaintInfo(Element element, 1691 GraphicsNode node, 1692 TextPaintInfo parent, 1693 BridgeContext ctx) { 1694 Value val = CSSUtilities.getComputedStyle 1696 (element, SVGCSSEngine.TEXT_DECORATION_INDEX); 1697 1698 TextPaintInfo pi = new TextPaintInfo(parent); 1699 1700 StyleMap sm = ((CSSStylableElement)element).getComputedStyleMap(null); 1702 if ((sm.isNullCascaded(SVGCSSEngine.TEXT_DECORATION_INDEX)) && 1703 (sm.isNullCascaded(SVGCSSEngine.FILL_INDEX)) && 1704 (sm.isNullCascaded(SVGCSSEngine.STROKE_INDEX)) && 1705 (sm.isNullCascaded(SVGCSSEngine.STROKE_WIDTH_INDEX)) && 1706 (sm.isNullCascaded(SVGCSSEngine.OPACITY_INDEX))) { 1707 return pi; 1709 } 1710 1711 setBaseTextPaintInfo(pi, element, node, ctx); 1712 1713 if (!sm.isNullCascaded(SVGCSSEngine.TEXT_DECORATION_INDEX)) 1714 setDecorationTextPaintInfo(pi, element); 1715 1716 return pi; 1717 } 1718 1719 public void setBaseTextPaintInfo(TextPaintInfo pi, Element element, 1720 GraphicsNode node, BridgeContext ctx) { 1721 if (!element.getLocalName().equals(SVG_TEXT_TAG)) 1722 pi.composite = CSSUtilities.convertOpacity (element); 1723 else 1724 pi.composite = AlphaComposite.SrcOver; 1725 1726 pi.visible = CSSUtilities.convertVisibility(element); 1727 pi.fillPaint = PaintServer.convertFillPaint (element, node, ctx); 1728 pi.strokePaint = PaintServer.convertStrokePaint(element, node, ctx); 1729 pi.strokeStroke = PaintServer.convertStroke (element); 1730 } 1731 1732 public void setDecorationTextPaintInfo(TextPaintInfo pi, Element element) { 1733 Value val = CSSUtilities.getComputedStyle 1734 (element, SVGCSSEngine.TEXT_DECORATION_INDEX); 1735 1736 switch (val.getCssValueType()) { 1737 case CSSValue.CSS_VALUE_LIST: 1738 ListValue lst = (ListValue)val; 1739 1740 int len = lst.getLength(); 1741 for (int i = 0; i < len; i++) { 1742 Value v = lst.item(i); 1743 String s = v.getStringValue(); 1744 switch (s.charAt(0)) { 1745 case 'u': 1746 if (pi.fillPaint != null) { 1747 pi.underlinePaint = pi.fillPaint; 1748 } 1749 if (pi.strokePaint != null) { 1750 pi.underlineStrokePaint = pi.strokePaint; 1751 } 1752 if (pi.strokeStroke != null) { 1753 pi.underlineStroke = pi.strokeStroke; 1754 } 1755 break; 1756 case 'o': 1757 if (pi.fillPaint != null) { 1758 pi.overlinePaint = pi.fillPaint; 1759 } 1760 if (pi.strokePaint != null) { 1761 pi.overlineStrokePaint = pi.strokePaint; 1762 } 1763 if (pi.strokeStroke != null) { 1764 pi.overlineStroke = pi.strokeStroke; 1765 } 1766 break; 1767 case 'l': 1768 if (pi.fillPaint != null) { 1769 pi.strikethroughPaint = pi.fillPaint; 1770 } 1771 if (pi.strokePaint != null) { 1772 pi.strikethroughStrokePaint = pi.strokePaint; 1773 } 1774 if (pi.strokeStroke != null) { 1775 pi.strikethroughStroke = pi.strokeStroke; 1776 } 1777 break; 1778 } 1779 } 1780 break; 1781 1782 default: pi.underlinePaint = null; 1784 pi.underlineStrokePaint = null; 1785 pi.underlineStroke = null; 1786 1787 pi.overlinePaint = null; 1788 pi.overlineStrokePaint = null; 1789 pi.overlineStroke = null; 1790 1791 pi.strikethroughPaint = null; 1792 pi.strikethroughStrokePaint = null; 1793 pi.strikethroughStroke = null; 1794 break; 1795 } 1796 } 1797 1798 1802 public abstract class AbstractTextChildSVGContext 1803 implements SVGContext { 1804 1805 1806 protected BridgeContext ctx; 1807 1808 1809 protected SVGTextElementBridge textBridge; 1810 1811 1812 protected Element e; 1813 1814 1819 public AbstractTextChildSVGContext(BridgeContext ctx, 1820 SVGTextElementBridge parent, 1821 Element e) { 1822 this.ctx = ctx; 1823 this.textBridge = parent; 1824 this.e = e; 1825 } 1826 1827 public SVGTextElementBridge getTextBridge() { return textBridge; } 1828 1829 1832 public float getPixelUnitToMillimeter() { 1833 return ctx.getUserAgent().getPixelUnitToMillimeter(); 1834 } 1835 1836 1841 public float getPixelToMM() { 1842 return getPixelUnitToMillimeter(); 1843 1844 } 1845 1851 public Rectangle2D getBBox() { 1852 return null; 1855 } 1856 1857 1862 public AffineTransform getCTM() { 1863 return null; 1866 } 1867 1868 1872 public AffineTransform getGlobalTransform() { 1873 return null; 1875 } 1876 1877 1881 public AffineTransform getScreenTransform() { 1882 return null; 1884 } 1885 1886 1890 public void setScreenTransform(AffineTransform at) { 1891 return; 1893 } 1894 1895 1899 public float getViewportWidth() { 1900 return ctx.getBlockWidth(e); 1901 } 1902 1903 1907 public float getViewportHeight() { 1908 return ctx.getBlockHeight(e); 1909 } 1910 1911 1914 public float getFontSize() { 1915 return CSSUtilities.getComputedStyle 1916 (e, SVGCSSEngine.FONT_SIZE_INDEX).getFloatValue(); 1917 } 1918 } 1919 1920 1930 protected abstract class AbstractTextChildBridgeUpdateHandler 1931 extends AbstractTextChildSVGContext implements BridgeUpdateHandler { 1932 1933 1936 public AbstractTextChildBridgeUpdateHandler 1937 (BridgeContext ctx, 1938 SVGTextElementBridge parent, 1939 Element e) { 1940 1941 super(ctx,parent,e); 1942 } 1943 1946 public void handleDOMAttrModifiedEvent(MutationEvent evt) { 1947 } 1949 1950 1953 public void handleDOMNodeInsertedEvent(MutationEvent evt) { 1954 textBridge.handleDOMNodeInsertedEvent(evt); 1955 } 1956 1957 1960 public void handleDOMNodeRemovedEvent(MutationEvent evt) { 1961 dispose(); 1963 } 1964 1965 1969 public void handleDOMCharacterDataModified(MutationEvent evt) { 1970 textBridge.handleDOMCharacterDataModified(evt); 1971 } 1972 1973 1976 public void handleCSSEngineEvent(CSSEngineEvent evt) { 1977 textBridge.handleCSSEngineEvent(evt); 1978 } 1979 1980 1983 public void dispose(){ 1984 ((SVGOMElement)e).setSVGContext(null); 1985 elemTPI.remove(e); 1986 } 1987 } 1988 1989 protected class AbstractTextChildTextContent 1990 extends AbstractTextChildBridgeUpdateHandler 1991 implements SVGTextContent{ 1992 1993 1996 public AbstractTextChildTextContent 1997 (BridgeContext ctx, 1998 SVGTextElementBridge parent, 1999 Element e) { 2000 2001 super(ctx,parent,e); 2002 } 2003 2004 2006 public int getNumberOfChars(){ 2007 return textBridge.getNumberOfChars(e); 2008 } 2009 2010 public Rectangle2D getExtentOfChar(int charnum ){ 2011 return textBridge.getExtentOfChar(e,charnum); 2012 } 2013 2014 public Point2D getStartPositionOfChar(int charnum){ 2015 return textBridge.getStartPositionOfChar(e,charnum); 2016 } 2017 2018 public Point2D getEndPositionOfChar(int charnum){ 2019 return textBridge.getEndPositionOfChar(e,charnum); 2020 } 2021 2022 public void selectSubString(int charnum, int nchars){ 2023 textBridge.selectSubString(e,charnum,nchars); 2024 } 2025 2026 public float getRotationOfChar(int charnum){ 2027 return textBridge.getRotationOfChar(e,charnum); 2028 } 2029 2030 public float getComputedTextLength(){ 2031 return textBridge.getComputedTextLength(e); 2032 } 2033 2034 public float getSubStringLength(int charnum, int nchars){ 2035 return textBridge.getSubStringLength(e,charnum,nchars); 2036 } 2037 2038 public int getCharNumAtPosition(float x , float y){ 2039 return textBridge.getCharNumAtPosition(e,x,y); 2040 } 2041 } 2042 2043 2046 protected class TRefBridge 2047 extends AbstractTextChildTextContent { 2048 2049 public TRefBridge(BridgeContext ctx, 2050 SVGTextElementBridge parent, 2051 Element e) { 2052 super(ctx,parent,e); 2053 } 2054 2055 2059 public void handleDOMAttrModifiedEvent(MutationEvent evt){ 2060 String attrName = evt.getAttrName(); 2061 if (attrName.equals(SVG_X_ATTRIBUTE) || 2062 attrName.equals(SVG_Y_ATTRIBUTE) || 2063 attrName.equals(SVG_DX_ATTRIBUTE) || 2064 attrName.equals(SVG_DY_ATTRIBUTE) || 2065 attrName.equals(SVG_ROTATE_ATTRIBUTE)) { 2066 textBridge.computeLaidoutText(ctx, textBridge.e, 2068 textBridge.node); 2069 } 2070 } 2071 } 2072 2073 2076 protected class TextPathBridge 2077 extends AbstractTextChildTextContent{ 2078 2079 public TextPathBridge(BridgeContext ctx, 2080 SVGTextElementBridge parent, 2081 Element e){ 2082 super(ctx,parent,e); 2083 } 2084 } 2085 2086 2089 protected class TspanBridge 2090 extends AbstractTextChildTextContent { 2091 2092 public TspanBridge(BridgeContext ctx, 2093 SVGTextElementBridge parent, 2094 Element e){ 2095 super(ctx,parent,e); 2096 } 2097 2098 2102 public void handleDOMAttrModifiedEvent(MutationEvent evt){ 2103 String attrName = evt.getAttrName(); 2104 if (attrName.equals(SVG_X_ATTRIBUTE) || 2105 attrName.equals(SVG_Y_ATTRIBUTE) || 2106 attrName.equals(SVG_DX_ATTRIBUTE) || 2107 attrName.equals(SVG_DY_ATTRIBUTE) || 2108 attrName.equals(SVG_ROTATE_ATTRIBUTE)) { 2109 textBridge.computeLaidoutText(ctx, textBridge.e, 2111 textBridge.node); 2112 } 2113 } 2114 } 2115 2116 public int getNumberOfChars(){ 2118 return getNumberOfChars(e); 2119 } 2120 2121 public Rectangle2D getExtentOfChar(int charnum ){ 2122 return getExtentOfChar(e,charnum); 2123 } 2124 2125 public Point2D getStartPositionOfChar(int charnum){ 2126 return getStartPositionOfChar(e,charnum); 2127 } 2128 2129 public Point2D getEndPositionOfChar(int charnum){ 2130 return getEndPositionOfChar(e,charnum); 2131 } 2132 2133 public void selectSubString(int charnum, int nchars){ 2134 selectSubString(e,charnum,nchars); 2135 } 2136 2137 public float getRotationOfChar(int charnum){ 2138 return getRotationOfChar(e,charnum); 2139 } 2140 2141 public float getComputedTextLength(){ 2142 return getComputedTextLength(e); 2143 } 2144 2145 public float getSubStringLength(int charnum, int nchars){ 2146 return getSubStringLength(e,charnum,nchars); 2147 } 2148 2149 public int getCharNumAtPosition(float x , float y){ 2150 return getCharNumAtPosition(e,x,y); 2151 } 2152 2153 2157 protected int getNumberOfChars(Element element){ 2158 2159 AttributedCharacterIterator aci = ((TextNode)node).getAttributedCharacterIterator(); 2160 2161 int firstChar = getElementStartIndex(aci,element); 2164 2165 if (firstChar == -1) 2166 return 0; 2168 int lastChar = getElementEndIndex(aci,element); 2169 2170 return( lastChar - firstChar + 1 ); 2171 } 2172 2173 2174 2178 protected Rectangle2D getExtentOfChar(Element element,int charnum ){ 2179 2180 AttributedCharacterIterator aci; 2181 aci = ((TextNode)node).getAttributedCharacterIterator(); 2182 2183 int firstChar = getElementStartIndex(aci,element); 2184 2185 if ( firstChar == -1 ) 2186 return null; 2187 2188 List list = getTextRuns((TextNode)node); 2190 2191 CharacterInformation info; 2193 info = getCharacterInformation(list, firstChar,charnum, aci); 2194 2195 if ( info == null ) 2196 return null; 2197 2198 GVTGlyphVector it = info.layout.getGlyphVector(); 2201 2202 Shape b = null; 2203 2204 if ( info.glyphIndexStart == info.glyphIndexEnd ){ 2205 if (it.isGlyphVisible(info.glyphIndexStart)) 2206 b = it.getGlyphOutline(info.glyphIndexStart); 2207 } else { 2208 GeneralPath path = null; 2209 for( int k = info.glyphIndexStart ; 2210 k <= info.glyphIndexEnd; 2211 k++){ 2212 if (it.isGlyphVisible(k)) { 2213 if (path == null) 2214 path = new GeneralPath (it.getGlyphOutline(k)); 2215 else 2216 path.append(it.getGlyphOutline(k),false); 2217 } 2218 } 2219 b = path; 2220 } 2221 2222 if (b == null) 2223 return null; 2224 2225 return b.getBounds2D(); 2227 } 2228 2229 2230 2234 protected Point2D getStartPositionOfChar(Element element,int charnum){ 2235 2236 AttributedCharacterIterator aci = ((TextNode)node).getAttributedCharacterIterator(); 2237 2238 int firstChar = getElementStartIndex(aci,element); 2239 2240 if ( firstChar == -1 ) 2241 return null; 2242 2243 List list = getTextRuns((TextNode)node); 2245 2246 CharacterInformation info; 2248 info = getCharacterInformation(list, firstChar,charnum, aci); 2249 2250 if ( info == null ) 2251 return null; 2252 2253 return getStartPoint( info ); 2254 } 2255 2256 protected Point2D getStartPoint(CharacterInformation info){ 2257 2258 GVTGlyphVector it = info.layout.getGlyphVector(); 2259 if (!it.isGlyphVisible(info.glyphIndexStart)) 2260 return null; 2261 2262 Point2D b = it.getGlyphPosition(info.glyphIndexStart); 2263 2264 AffineTransform glyphTransform; 2265 glyphTransform = it.getGlyphTransform(info.glyphIndexStart); 2266 2267 2268 Point2D.Float result = new Point2D.Float (0, 0); 2270 if ( glyphTransform != null ) 2271 glyphTransform.transform(result,result); 2273 2274 result.x += b.getX(); 2275 result.y += b.getY(); 2276 return result; 2277 } 2278 2279 2283 protected Point2D getEndPositionOfChar(Element element,int charnum ){ 2284 2285 AttributedCharacterIterator aci; 2286 aci = ((TextNode)node).getAttributedCharacterIterator(); 2287 2288 int firstChar = getElementStartIndex(aci,element); 2289 2290 if ( firstChar == -1 ) 2291 return null; 2292 2293 List list = getTextRuns((TextNode)node); 2295 2296 CharacterInformation info; 2298 info = getCharacterInformation(list, firstChar,charnum, aci); 2299 2300 if ( info == null ) 2301 return null; 2302 return getEndPoint(info); 2303 } 2304 2305 protected Point2D getEndPoint(CharacterInformation info){ 2306 2307 GVTGlyphVector it = info.layout.getGlyphVector(); 2308 if (!it.isGlyphVisible(info.glyphIndexEnd)) 2309 return null; 2310 2311 Point2D b = it.getGlyphPosition(info.glyphIndexEnd); 2312 2313 AffineTransform glyphTransform; 2314 glyphTransform = it.getGlyphTransform(info.glyphIndexEnd); 2315 2316 GVTGlyphMetrics metrics = it.getGlyphMetrics(info.glyphIndexEnd); 2317 2318 2319 Point2D.Float result = new Point2D.Float 2320 (metrics.getHorizontalAdvance(), 0); 2321 2322 if ( glyphTransform != null ) 2323 glyphTransform.transform(result,result); 2324 2325 result.x += b.getX(); 2326 result.y += b.getY(); 2327 return result; 2328 } 2329 2330 2334 protected float getRotationOfChar(Element element, int charnum){ 2335 2336 AttributedCharacterIterator aci; 2337 aci = ((TextNode)node).getAttributedCharacterIterator(); 2338 int firstChar = getElementStartIndex(aci,element); 2340 2341 if ( firstChar == -1 ) 2342 return 0; 2343 2344 List list = getTextRuns((TextNode)node); 2346 2347 CharacterInformation info; 2349 info = getCharacterInformation(list, firstChar,charnum, aci); 2350 2351 double angle = 0.0; 2352 int nbGlyphs = 0; 2353 2354 if ( info != null ){ 2355 GVTGlyphVector it = info.layout.getGlyphVector(); 2356 2357 for( int k = info.glyphIndexStart ; 2358 k <= info.glyphIndexEnd ; 2359 k++ ){ 2360 if (!it.isGlyphVisible(k)) continue; 2361 2362 nbGlyphs++; 2363 2364 AffineTransform glyphTransform = it.getGlyphTransform(k); 2366 if ( glyphTransform == null ) continue; 2367 2368 double glyphAngle = 0.0; 2369 double cosTheta = glyphTransform.getScaleX(); 2370 double sinTheta = glyphTransform.getShearX(); 2371 2372 if ( cosTheta == 0.0 ){ 2374 if ( sinTheta > 0 ) glyphAngle = Math.PI; 2375 else glyphAngle = -Math.PI; 2376 } else { 2377 glyphAngle = Math.atan(sinTheta/cosTheta); 2378 if ( cosTheta < 0 ) 2379 glyphAngle += Math.PI; 2380 } 2381 2384 glyphAngle = (Math.toDegrees( - glyphAngle ) ) % 360.0; 2385 2386 angle += glyphAngle - info.getComputedOrientationAngle(); 2388 } 2389 } 2390 if (nbGlyphs == 0) return 0; 2391 return (float)(angle / nbGlyphs ); 2392 } 2393 2394 2398 protected float getComputedTextLength(Element e) { 2399 return getSubStringLength(e,0,getNumberOfChars(e)); 2400 } 2401 2402 2406 protected float getSubStringLength(Element element, 2407 int charnum, 2408 int nchars){ 2409 if (nchars == 0) { 2410 return 0; 2411 } 2412 2413 float length = 0; 2414 2415 AttributedCharacterIterator aci; 2416 aci = ((TextNode)node).getAttributedCharacterIterator(); 2417 TextNode textNode = (TextNode)node; 2418 2419 int firstChar = getElementStartIndex(aci,element); 2420 2421 if ( firstChar == -1 ) 2422 return -1; 2423 2424 List list = getTextRuns(textNode); 2425 2426 CharacterInformation currentInfo; 2427 currentInfo = getCharacterInformation(list, firstChar,charnum,aci); 2428 CharacterInformation lastCharacterInRunInfo = null; 2429 int chIndex = currentInfo.characterIndex+1; 2430 GVTGlyphVector vector = currentInfo.layout.getGlyphVector(); 2431 float [] advs = currentInfo.layout.getGlyphAdvances(); 2432 boolean [] glyphTrack = new boolean[advs.length]; 2433 for( int k = charnum +1; k < charnum +nchars ; k++) { 2434 if (currentInfo.layout.isOnATextPath() ){ 2435 for (int gi = currentInfo.glyphIndexStart; 2436 gi <= currentInfo.glyphIndexEnd; gi++) { 2437 if ((vector.isGlyphVisible(gi)) && !glyphTrack[gi]) 2438 length += advs[gi+1]-advs[gi]; 2439 glyphTrack[gi] = true; 2440 } 2441 CharacterInformation newInfo; 2442 newInfo = getCharacterInformation(list, firstChar, k, aci); 2443 if (newInfo.layout != currentInfo.layout) { 2444 vector = newInfo.layout.getGlyphVector(); 2445 advs = newInfo.layout.getGlyphAdvances(); 2446 glyphTrack = new boolean[advs.length]; 2447 chIndex = currentInfo.characterIndex+1; 2448 } 2449 currentInfo = newInfo; 2450 } else { 2451 if ( currentInfo.layout.hasCharacterIndex(chIndex) ){ 2453 chIndex++; 2454 continue; 2455 } 2456 2457 lastCharacterInRunInfo = getCharacterInformation 2458 (list,firstChar,k-1,aci); 2459 2460 length += distanceFirstLastCharacterInRun 2463 (currentInfo,lastCharacterInRunInfo); 2464 2465 currentInfo = getCharacterInformation(list,firstChar,k,aci); 2466 chIndex = currentInfo.characterIndex+1; 2467 vector = currentInfo.layout.getGlyphVector(); 2468 advs = currentInfo.layout.getGlyphAdvances(); 2469 glyphTrack = new boolean[advs.length]; 2470 lastCharacterInRunInfo = null; 2471 } 2472 } 2473 2474 if (currentInfo.layout.isOnATextPath() ){ 2475 for (int gi = currentInfo.glyphIndexStart; 2476 gi <= currentInfo.glyphIndexEnd; gi++) { 2477 if ((vector.isGlyphVisible(gi)) && !glyphTrack[gi]) 2478 length += advs[gi+1]-advs[gi]; 2479 glyphTrack[gi] = true; 2480 } 2481 } else { 2482 if ( lastCharacterInRunInfo == null ){ 2483 lastCharacterInRunInfo = getCharacterInformation 2484 (list,firstChar,charnum+nchars-1,aci); 2485 } 2486 length += distanceFirstLastCharacterInRun 2489 (currentInfo,lastCharacterInRunInfo); 2490 } 2491 2492 return length; 2493 } 2494 2495 protected float distanceFirstLastCharacterInRun 2496 (CharacterInformation first, CharacterInformation last){ 2497 2498 float [] advs = first.layout.getGlyphAdvances(); 2499 2500 int firstStart = first.glyphIndexStart; 2501 int firstEnd = first.glyphIndexEnd; 2502 int lastStart = last.glyphIndexStart; 2503 int lastEnd = last.glyphIndexEnd; 2504 2505 int start = (firstStart<lastStart)?firstStart:lastStart; 2506 int end = (firstEnd<lastEnd)?lastEnd:firstEnd; 2507 return advs[end+1] - advs[start]; 2508 } 2509 2510 protected float distanceBetweenRun 2511 (CharacterInformation last, CharacterInformation first){ 2512 2513 float distance; 2514 Point2D startPoint; 2515 Point2D endPoint; 2516 CharacterInformation info = new CharacterInformation(); 2517 2518 2520 info.layout = last.layout; 2521 info.glyphIndexEnd = last.layout.getGlyphCount()-1; 2522 2523 startPoint = getEndPoint(info); 2524 2525 info.layout = first.layout; 2527 info.glyphIndexStart = 0; 2528 2529 endPoint = getStartPoint(info); 2530 2531 if( first.isVertical() ){ 2532 distance = (float)(endPoint.getY() - startPoint.getY()); 2533 } 2534 else{ 2535 distance = (float)(endPoint.getX() - startPoint.getX()); 2536 } 2537 2538 return distance; 2539 } 2540 2541 2542 2548 protected void selectSubString(Element element, int charnum, int nchars) { 2549 AttributedCharacterIterator aci; 2550 aci = ((TextNode)node).getAttributedCharacterIterator(); 2551 TextNode textNode = (TextNode)node; 2552 2553 int firstChar = getElementStartIndex(aci,element); 2554 2555 if ( firstChar == -1 ) 2556 return; 2557 2558 List list = getTextRuns(textNode); 2559 2560 int lastChar = getElementEndIndex(aci,element); 2561 2562 CharacterInformation firstInfo, lastInfo; 2563 firstInfo = getCharacterInformation(list, firstChar,charnum,aci); 2564 lastInfo = getCharacterInformation(list, firstChar,charnum+nchars-1,aci); 2565 2566 Mark firstMark, lastMark; 2567 firstMark = textNode.getMarkerForChar(firstInfo.characterIndex,true); 2568 2569 if ( lastInfo != null && lastInfo.characterIndex <= lastChar ){ 2570 lastMark = textNode.getMarkerForChar(lastInfo.characterIndex,false); 2571 } 2572 else{ 2573 lastMark = textNode.getMarkerForChar(lastChar,false); 2574 } 2575 2576 ctx.getUserAgent().setTextSelection(firstMark,lastMark); 2577 } 2578 2579 protected int getCharNumAtPosition(Element e, float x, float y){ 2580 2581 TextNode textNode = (TextNode)node; 2582 2583 List list = getTextRuns(textNode); 2585 2586 TextHit hit = null; 2589 2590 for( int i = list.size()-1 ; i>= 0 && hit == null; i-- ){ 2591 2592 hit = ((StrokingTextPainter.TextRun)list.get(i)).getLayout().hitTestChar(x,y); 2593 } 2594 2595 if ( hit == null ){ 2596 return -1; 2597 } 2598 2599 AttributedCharacterIterator aci = ((TextNode)node).getAttributedCharacterIterator(); 2600 2601 int first = getElementStartIndex( aci, e ); 2603 int last = getElementEndIndex( aci, e ); 2604 2605 int hitIndex = hit.getCharIndex(); 2606 2607 if ( hitIndex >= first && hitIndex <= last ){ 2608 2609 return hitIndex - first; 2610 } 2611 else{ 2612 return -1; 2613 } 2614 } 2615 2616 2620 protected List getTextRuns(TextNode node){ 2621 if ( node.getTextRuns() == null ){ 2623 node.getPrimitiveBounds(); 2626 } 2627 return node.getTextRuns(); 2629 } 2630 2631 2647 protected CharacterInformation getCharacterInformation 2648 (List list,int startIndex, int charnum, 2649 AttributedCharacterIterator aci) 2650 { 2651 CharacterInformation info = new CharacterInformation(); 2652 info.characterIndex = startIndex+charnum; 2653 2654 for (int i = 0; i < list.size(); i++) { 2655 StrokingTextPainter.TextRun run = 2656 (StrokingTextPainter.TextRun)list.get(i); 2657 2658 if ( run.getLayout().hasCharacterIndex(info.characterIndex) ){ 2659 info.layout = run.getLayout(); 2660 2661 aci.setIndex(info.characterIndex); 2662 2663 if (aci.getAttribute(ALT_GLYPH_HANDLER) != null){ 2665 info.glyphIndexStart = 0; 2666 info.glyphIndexEnd = info.layout.getGlyphCount()-1; 2667 } else { 2668 info.glyphIndexStart = info.layout.getGlyphIndex 2669 (info.characterIndex); 2670 2671 if ( info.glyphIndexStart == -1 ){ 2674 info.glyphIndexStart = 0; 2675 info.glyphIndexEnd = info.layout.getGlyphCount()-1; 2676 } 2677 else{ 2678 info.glyphIndexEnd = info.glyphIndexStart; 2679 } 2680 } 2681 return info; 2682 } 2683 } 2684 return null; 2685 } 2686 2687 2691 protected static class CharacterInformation{ 2692 TextSpanLayout layout; 2694 int glyphIndexStart; 2696 2697 int glyphIndexEnd; 2698 2699 int characterIndex; 2701 2702 public boolean isVertical(){ 2704 return layout.isVertical(); 2705 } 2706 public double getComputedOrientationAngle(){ 2708 return layout.getComputedOrientationAngle(characterIndex); 2709 } 2710 } 2711 2712 2713 public Set getTextIntersectionSet(AffineTransform at, 2714 Rectangle2D rect) { 2715 TextNode tn = (TextNode)node; 2716 List list = tn.getTextRuns(); 2717 Set elems = new HashSet (); 2718 for (int i = 0 ; i < list.size(); i++) { 2719 StrokingTextPainter.TextRun run; 2720 run = (StrokingTextPainter.TextRun)list.get(i); 2721 TextSpanLayout layout = run.getLayout(); 2722 AttributedCharacterIterator aci = run.getACI(); 2723 aci.first(); 2724 Element elem = (Element)aci.getAttribute(TEXT_COMPOUND_DELIMITER); 2725 2726 if (elem == null) continue; 2727 if (elems.contains(elem)) continue; 2728 if (!isTextSensitive(elem)) continue; 2729 2730 Rectangle2D glBounds = layout.getBounds2D(); 2731 if (glBounds != null) 2732 glBounds = at.createTransformedShape(glBounds).getBounds2D(); 2733 2734 if (!rect.intersects(glBounds)) 2735 continue; 2736 2737 GVTGlyphVector gv = layout.getGlyphVector(); 2738 for (int g = 0; g < gv.getNumGlyphs(); g++) { 2739 Shape gBounds = gv.getGlyphLogicalBounds(g); 2740 if (gBounds != null) 2741 gBounds = at.createTransformedShape 2742 (gBounds).getBounds2D(); 2743 2744 if (gBounds.intersects(rect)) { 2745 elems.add(elem); 2746 break; 2747 } 2748 } 2749 } 2750 return elems; 2751 } 2752 2753 public Set getTextEnclosureSet(AffineTransform at, 2754 Rectangle2D rect) { 2755 TextNode tn = (TextNode)node; 2756 2757 Set elems = new HashSet (); 2758 Set reject = new HashSet (); 2759 List list = tn.getTextRuns(); 2760 for (int i = 0 ; i < list.size(); i++) { 2761 StrokingTextPainter.TextRun run; 2762 run = (StrokingTextPainter.TextRun)list.get(i); 2763 TextSpanLayout layout = run.getLayout(); 2764 AttributedCharacterIterator aci = run.getACI(); 2765 aci.first(); 2766 Element elem = (Element)aci.getAttribute(TEXT_COMPOUND_DELIMITER); 2767 2768 if (elem == null) continue; 2769 if (reject.contains(elem)) continue; 2770 if (!isTextSensitive(elem)) { 2771 reject.add(elem); 2772 continue; 2773 } 2774 2775 Rectangle2D glBounds = layout.getBounds2D(); 2776 if (glBounds != null) 2777 glBounds = at.createTransformedShape 2778 (glBounds).getBounds2D(); 2779 2780 if (rect.contains(glBounds)) { 2781 elems.add(elem); 2782 } else { 2783 reject.add(elem); 2784 elems.remove(elem); 2785 } 2786 } 2787 2788 return elems; 2789 } 2790 2791 public static boolean getTextIntersection(BridgeContext ctx, 2792 Element elem, 2793 AffineTransform ati, 2794 Rectangle2D rect, 2795 boolean checkSensitivity) { 2796 SVGContext svgCtx = null; 2797 if (elem instanceof SVGOMElement) 2798 svgCtx = ((SVGOMElement)elem).getSVGContext(); 2799 if (svgCtx == null) return false; 2800 2801 SVGTextElementBridge txtBridge = null; 2802 if (svgCtx instanceof SVGTextElementBridge) 2803 txtBridge = (SVGTextElementBridge)svgCtx; 2804 else if (svgCtx instanceof AbstractTextChildSVGContext) { 2805 AbstractTextChildSVGContext childCtx; 2806 childCtx = (AbstractTextChildSVGContext)svgCtx; 2807 txtBridge = childCtx.getTextBridge(); 2808 } 2809 if (txtBridge == null) return false; 2810 2811 TextNode tn = (TextNode)txtBridge.node; 2812 Element txtElem = txtBridge.e; 2813 2814 AffineTransform at = tn.getGlobalTransform(); 2815 at.preConcatenate(ati); 2816 2817 Rectangle2D tnRect; 2818 tnRect = tn.getBounds(); 2819 tnRect = at.createTransformedShape(tnRect).getBounds2D(); 2820 if (!rect.intersects(tnRect)) return false; 2821 2822 List list = tn.getTextRuns(); 2823 for (int i = 0 ; i < list.size(); i++) { 2824 StrokingTextPainter.TextRun run; 2825 run = (StrokingTextPainter.TextRun)list.get(i); 2826 TextSpanLayout layout = run.getLayout(); 2827 AttributedCharacterIterator aci = run.getACI(); 2828 aci.first(); 2829 Element runElem = (Element)aci.getAttribute 2830 (TEXT_COMPOUND_DELIMITER); 2831 if (runElem == null) continue; 2832 2833 if (checkSensitivity && !isTextSensitive(runElem)) continue; 2835 2836 Element p = runElem; 2837 while ((p != null) && (p != txtElem) && (p != elem)) { 2838 p = (Element)p.getParentNode(); 2839 } 2840 if (p != elem) continue; 2841 2842 Rectangle2D glBounds = layout.getBounds2D(); 2844 if (glBounds == null) continue; 2845 glBounds = at.createTransformedShape(glBounds).getBounds2D(); 2846 if (!rect.intersects(glBounds)) continue; 2847 2848 GVTGlyphVector gv = layout.getGlyphVector(); 2849 for (int g = 0; g < gv.getNumGlyphs(); g++) { 2850 Shape gBounds = gv.getGlyphLogicalBounds(g); 2851 if (gBounds != null) 2852 gBounds = at.createTransformedShape 2853 (gBounds).getBounds2D(); 2854 2855 if (gBounds.intersects(rect)) 2856 return true; 2857 } 2858 } 2859 return false; 2860 } 2861 2862 public static Rectangle2D getTextBounds(BridgeContext ctx, Element elem, 2863 boolean checkSensitivity) { 2864 SVGContext svgCtx = null; 2865 if (elem instanceof SVGOMElement) 2866 svgCtx = ((SVGOMElement)elem).getSVGContext(); 2867 if (svgCtx == null) return null; 2868 2869 SVGTextElementBridge txtBridge = null; 2870 if (svgCtx instanceof SVGTextElementBridge) 2871 txtBridge = (SVGTextElementBridge)svgCtx; 2872 else if (svgCtx instanceof AbstractTextChildSVGContext) { 2873 AbstractTextChildSVGContext childCtx; 2874 childCtx = (AbstractTextChildSVGContext)svgCtx; 2875 txtBridge = childCtx.getTextBridge(); 2876 } 2877 if (txtBridge == null) return null; 2878 2879 TextNode tn = (TextNode)txtBridge.node; 2880 Element txtElem = txtBridge.e; 2881 2882 Rectangle2D ret = null; 2883 List list = tn.getTextRuns(); 2884 if (list == null) return null; 2885 for (int i = 0 ; i < list.size(); i++) { 2886 StrokingTextPainter.TextRun run; 2887 run = (StrokingTextPainter.TextRun)list.get(i); 2888 TextSpanLayout layout = run.getLayout(); 2889 AttributedCharacterIterator aci = run.getACI(); 2890 aci.first(); 2891 Element runElem = (Element)aci.getAttribute 2892 (TEXT_COMPOUND_DELIMITER); 2893 if (runElem == null) continue; 2894 2895 if (checkSensitivity && !isTextSensitive(runElem)) continue; 2897 2898 Element p = runElem; 2899 while ((p != null) && (p != txtElem) && (p != elem)) { 2900 p = (Element)p.getParentNode(); 2901 } 2902 if (p != elem) continue; 2903 2904 Rectangle2D glBounds = layout.getBounds2D(); 2906 if (glBounds != null) { 2907 if (ret == null) ret = (Rectangle2D )glBounds.clone(); 2908 else ret.add(glBounds); 2909 } 2910 } 2911 return ret; 2912 } 2913 2914 2915 public static boolean isTextSensitive(Element e) { 2916 int ptrEvts = CSSUtilities.convertPointerEvents(e); 2917 switch (ptrEvts) { 2918 case GraphicsNode.VISIBLE_PAINTED: 2919 case GraphicsNode.VISIBLE_FILL: 2920 case GraphicsNode.VISIBLE_STROKE: 2921 case GraphicsNode.VISIBLE: 2922 return CSSUtilities.convertVisibility(e); 2923 case GraphicsNode.PAINTED: 2924 case GraphicsNode.FILL: 2925 case GraphicsNode.STROKE: 2926 case GraphicsNode.ALL: 2927 return true; 2928 case GraphicsNode.NONE: 2929 default: 2930 return false; 2931 } 2932 } 2933} 2934 | Popular Tags |