1 18 19 package org.apache.batik.bridge.svg12; 20 21 import java.awt.Shape ; 22 import java.awt.font.TextAttribute ; 23 import java.awt.geom.AffineTransform ; 24 import java.awt.geom.Point2D ; 25 import java.awt.geom.Rectangle2D ; 26 import java.text.AttributedCharacterIterator ; 27 import java.text.AttributedString ; 28 import java.util.ArrayList ; 29 import java.util.Iterator ; 30 import java.util.List ; 31 import java.util.LinkedList ; 32 import java.util.Map ; 33 34 import org.w3c.dom.Element ; 35 import org.w3c.dom.Node ; 36 import org.w3c.dom.events.EventTarget ; 37 38 import org.apache.batik.bridge.Bridge; 39 import org.apache.batik.bridge.BridgeException; 40 import org.apache.batik.bridge.BridgeContext; 41 import org.apache.batik.bridge.CSSUtilities; 42 import org.apache.batik.bridge.GraphicsNodeBridge; 43 import org.apache.batik.bridge.GVTBuilder; 44 import org.apache.batik.bridge.SVGTextElementBridge; 45 import org.apache.batik.bridge.SVGUtilities; 46 import org.apache.batik.bridge.TextUtilities; 47 import org.apache.batik.bridge.UnitProcessor; 48 import org.apache.batik.bridge.UserAgent; 49 import org.apache.batik.bridge.SVGAElementBridge; 50 51 import org.apache.batik.css.engine.CSSEngine; 52 import org.apache.batik.css.engine.SVGCSSEngine; 53 import org.apache.batik.css.engine.value.ComputedValue; 54 import org.apache.batik.css.engine.value.svg12.SVG12ValueConstants; 55 import org.apache.batik.css.engine.value.svg12.LineHeightValue; 56 import org.apache.batik.css.engine.value.Value; 57 import org.apache.batik.css.engine.value.ValueConstants; 58 59 import org.apache.batik.dom.util.XMLSupport; 60 import org.apache.batik.dom.util.XLinkSupport; 61 62 import org.apache.batik.gvt.GraphicsNode; 63 import org.apache.batik.gvt.TextNode; 64 import org.apache.batik.gvt.flow.BlockInfo; 65 import org.apache.batik.gvt.flow.FlowTextNode; 66 import org.apache.batik.gvt.flow.RegionInfo; 67 import org.apache.batik.gvt.flow.TextLineBreaks; 68 69 import org.apache.batik.gvt.text.GVTAttributedCharacterIterator; 70 import org.apache.batik.gvt.text.TextPaintInfo; 71 import org.apache.batik.gvt.text.TextPath; 72 73 import org.apache.batik.util.SVG12Constants; 74 import org.apache.batik.util.SVG12CSSConstants; 75 76 82 public class SVGFlowRootElementBridge extends SVGTextElementBridge { 83 84 public static final AttributedCharacterIterator.Attribute FLOW_PARAGRAPH 85 = GVTAttributedCharacterIterator.TextAttribute.FLOW_PARAGRAPH; 86 87 public static final AttributedCharacterIterator.Attribute 88 FLOW_EMPTY_PARAGRAPH 89 = GVTAttributedCharacterIterator.TextAttribute.FLOW_EMPTY_PARAGRAPH; 90 91 public static final AttributedCharacterIterator.Attribute FLOW_LINE_BREAK 92 = GVTAttributedCharacterIterator.TextAttribute.FLOW_LINE_BREAK; 93 94 public static final AttributedCharacterIterator.Attribute FLOW_REGIONS 95 = GVTAttributedCharacterIterator.TextAttribute.FLOW_REGIONS; 96 97 public static final AttributedCharacterIterator.Attribute LINE_HEIGHT 98 = GVTAttributedCharacterIterator.TextAttribute.LINE_HEIGHT; 99 100 103 public SVGFlowRootElementBridge() {} 104 105 108 public String getNamespaceURI() { 109 return SVG12Constants.SVG_NAMESPACE_URI; 110 } 111 112 115 public String getLocalName() { 116 return SVG12Constants.SVG_FLOW_ROOT_TAG; 117 } 118 119 122 public Bridge getInstance() { 123 return new SVGFlowRootElementBridge(); 124 } 125 126 129 public boolean isComposite() { 130 return false; 131 } 132 133 protected GraphicsNode instantiateGraphicsNode() { 134 return new FlowTextNode(); 135 } 136 137 145 protected Point2D getLocation(BridgeContext ctx, Element e) { 146 return new Point2D.Float (0,0); 147 } 148 149 protected boolean isTextElement(Element e) { 150 if (!SVG_NAMESPACE_URI.equals(e.getNamespaceURI())) 151 return false; 152 String nodeName = e.getLocalName(); 153 return (nodeName.equals(SVG12Constants.SVG_FLOW_DIV_TAG) || 154 nodeName.equals(SVG12Constants.SVG_FLOW_LINE_TAG) || 155 nodeName.equals(SVG12Constants.SVG_FLOW_PARA_TAG) || 156 nodeName.equals(SVG12Constants.SVG_FLOW_REGION_BREAK_TAG) || 157 nodeName.equals(SVG12Constants.SVG_FLOW_SPAN_TAG)); 158 } 159 160 protected boolean isTextChild(Element e) { 161 if (!SVG_NAMESPACE_URI.equals(e.getNamespaceURI())) 162 return false; 163 String nodeName = e.getLocalName(); 164 return (nodeName.equals(SVG12Constants.SVG_A_TAG) || 165 nodeName.equals(SVG12Constants.SVG_FLOW_LINE_TAG) || 166 nodeName.equals(SVG12Constants.SVG_FLOW_PARA_TAG) || 167 nodeName.equals(SVG12Constants.SVG_FLOW_REGION_BREAK_TAG) || 168 nodeName.equals(SVG12Constants.SVG_FLOW_SPAN_TAG)); 169 } 170 171 protected void computeLaidoutText(BridgeContext ctx, 172 Element e, 173 GraphicsNode node) { 174 super.computeLaidoutText(ctx, getFlowDivElement(e), node); 175 } 176 177 184 protected AttributedString buildAttributedString(BridgeContext ctx, 185 Element element) { 186 List rgns = getRegions(ctx, element); 187 AttributedString ret = getFlowDiv(ctx, element); 188 ret.addAttribute(FLOW_REGIONS, rgns, 0, 1); 189 TextLineBreaks.findLineBrk(ret); 190 return ret; 192 } 193 194 protected void dumpACIWord(AttributedString as) { 195 String chars = ""; 196 String brkStr = ""; 197 AttributedCharacterIterator aci = as.getIterator(); 198 AttributedCharacterIterator.Attribute WORD_LIMIT = 199 TextLineBreaks.WORD_LIMIT; 200 201 for (char ch = aci.current(); 202 ch!=AttributedCharacterIterator.DONE; 203 ch = aci.next()) { 204 chars += ch + " "; 205 int w = ((Integer )aci.getAttribute(WORD_LIMIT)).intValue(); 206 if (w >=10) 207 brkStr += ""+w+" "; 208 else 209 brkStr += ""+w+" "; 210 } 211 System.out.println(chars); 212 System.out.println(brkStr); 213 } 214 215 protected Element getFlowDivElement(Element elem) { 216 String eNS = elem.getNamespaceURI(); 217 if (!eNS.equals(SVG_NAMESPACE_URI)) return null; 218 219 String nodeName = elem.getLocalName(); 220 if (nodeName.equals(SVG12Constants.SVG_FLOW_DIV_TAG)) return elem; 221 222 if (!nodeName.equals(SVG12Constants.SVG_FLOW_ROOT_TAG)) return null; 223 224 for (Node n = elem.getFirstChild(); 225 n != null; n = n.getNextSibling()) { 226 if (n.getNodeType() != Node.ELEMENT_NODE) continue; 227 228 String nNS = n.getNamespaceURI(); 229 if (!SVG_NAMESPACE_URI.equals(nNS)) continue; 230 231 Element e = (Element )n; 232 String ln = e.getLocalName(); 233 if (ln.equals(SVG12Constants.SVG_FLOW_DIV_TAG)) 234 return e; 235 } 236 return null; 237 } 238 239 protected AttributedString getFlowDiv(BridgeContext ctx, Element element) { 240 Element flowDiv = getFlowDivElement(element); 241 if (flowDiv == null) return null; 242 243 return gatherFlowPara(ctx, flowDiv); 244 } 245 246 protected AttributedString gatherFlowPara 247 (BridgeContext ctx, Element div) { 248 AttributedStringBuffer asb = new AttributedStringBuffer(); 249 List paraEnds = new ArrayList (); 250 List paraElems = new ArrayList (); 251 List lnLocs = new ArrayList (); 252 for (Node n = div.getFirstChild(); 253 n != null; n = n.getNextSibling()) { 254 if (n.getNodeType() != Node.ELEMENT_NODE) continue; 255 if (!getNamespaceURI().equals(n.getNamespaceURI())) continue; 256 Element e = (Element )n; 257 258 String ln = e.getLocalName(); 259 if (ln.equals(SVG12Constants.SVG_FLOW_PARA_TAG)) { 260 fillAttributedStringBuffer(ctx, e, true, null, asb, lnLocs); 261 262 paraElems.add(e); 263 paraEnds.add(new Integer (asb.length())); 264 } else if (ln.equals(SVG12Constants.SVG_FLOW_REGION_BREAK_TAG)) { 265 fillAttributedStringBuffer(ctx, e, true, null, asb, lnLocs); 266 267 paraElems.add(e); 268 paraEnds.add(new Integer (asb.length())); 269 } 270 } 271 272 AttributedString ret = asb.toAttributedString(); 275 276 int prevLN = 0; 279 Iterator lnIter = lnLocs.iterator(); 280 while (lnIter.hasNext()) { 281 int nextLN = ((Integer )lnIter.next()).intValue(); 282 if (nextLN == prevLN) continue; 283 284 ret.addAttribute(FLOW_LINE_BREAK, 286 new Object (), 287 prevLN, nextLN); 288 prevLN = nextLN; 289 } 290 291 int start=0; 292 int end; 293 List emptyPara = null; 294 for (int i=0; i<paraElems.size(); i++, start=end) { 295 Element elem = (Element )paraElems.get(i); 296 end = ((Integer )paraEnds.get(i)).intValue(); 297 if (start == end) { 298 if (emptyPara == null) 299 emptyPara = new LinkedList (); 300 emptyPara.add(makeBlockInfo(ctx, elem)); 301 continue; 302 } 303 ret.addAttribute(FLOW_PARAGRAPH, makeBlockInfo(ctx, elem), 305 start, end); 306 if (emptyPara != null) { 307 ret.addAttribute(FLOW_EMPTY_PARAGRAPH, emptyPara, start, end); 308 emptyPara = null; 309 } 310 } 311 312 return ret; 313 } 314 315 protected List getRegions(BridgeContext ctx, Element element) { 316 element = (Element )element.getParentNode(); 318 List ret = new LinkedList (); 319 for (Node n = element.getFirstChild(); 320 n != null; n = n.getNextSibling()) { 321 322 if (n.getNodeType() != Node.ELEMENT_NODE) continue; 323 if (!SVG12Constants.SVG_NAMESPACE_URI.equals(n.getNamespaceURI())) 324 continue; 325 326 Element e = (Element )n; 327 String ln = e.getLocalName(); 328 if (!SVG12Constants.SVG_FLOW_REGION_TAG.equals(ln)) continue; 329 330 float verticalAlignment = 0.0f; 332 333 gatherRegionInfo(ctx, e, verticalAlignment, ret); 334 } 335 336 return ret; 337 } 338 339 protected void gatherRegionInfo(BridgeContext ctx, Element rgn, 340 float verticalAlign, List regions) { 341 342 GVTBuilder builder = ctx.getGVTBuilder(); 343 for (Node n = rgn.getFirstChild(); 344 n != null; n = n.getNextSibling()) { 345 346 if (n.getNodeType() != Node.ELEMENT_NODE) continue; 347 if (!getNamespaceURI().equals(n.getNamespaceURI())) continue; 348 Element e = (Element )n; 349 350 GraphicsNode gn = builder.build(ctx, e) ; 351 if (gn == null) continue; 352 353 Shape s = gn.getOutline(); 354 if (s == null) continue; 355 AffineTransform at = gn.getTransform(); 356 if (at != null) 357 s = at.createTransformedShape(s); 358 regions.add(new RegionInfo(s, verticalAlign)); 359 } 360 } 361 362 protected int startLen; 363 364 367 protected void fillAttributedStringBuffer(BridgeContext ctx, 368 Element element, 369 boolean top, 370 Integer bidiLevel, 371 AttributedStringBuffer asb, 372 List lnLocs) { 373 if ((!SVGUtilities.matchUserAgent(element, ctx.getUserAgent())) || 376 (!CSSUtilities.convertDisplay(element))) { 377 return; 378 } 379 380 String s = XMLSupport.getXMLSpace(element); 381 boolean preserve = s.equals(SVG_PRESERVE_VALUE); 382 boolean prevEndsWithSpace; 383 Element nodeElement = element; 384 385 if (top) 386 endLimit = startLen = asb.length(); 387 388 if (preserve) 389 endLimit = startLen; 390 391 Map map = getAttributeMap(ctx, element, null, bidiLevel); 392 Object o = map.get(TextAttribute.BIDI_EMBEDDING); 393 Integer subBidiLevel = bidiLevel; 394 if (o != null) 395 subBidiLevel = (Integer )o; 396 397 int lineBreak = -1; 398 if (lnLocs.size() != 0) 399 lineBreak = ((Integer )lnLocs.get(lnLocs.size()-1)).intValue(); 400 401 for (Node n = element.getFirstChild(); 402 n != null; 403 n = n.getNextSibling()) { 404 405 if (preserve) { 406 prevEndsWithSpace = false; 407 } else { 408 int len = asb.length(); 409 if (len == startLen) 410 prevEndsWithSpace = true; 411 else { 412 prevEndsWithSpace = (asb.getLastChar() == ' '); 413 int idx = lnLocs.size()-1; 414 if (!prevEndsWithSpace && (idx >= 0)) { 415 Integer i = (Integer )lnLocs.get(idx); 416 if (i.intValue() == len) 417 prevEndsWithSpace = true; 418 } 419 } 420 } 421 422 switch (n.getNodeType()) { 423 case Node.ELEMENT_NODE: 424 if (!SVG_NAMESPACE_URI.equals(n.getNamespaceURI())) 426 break; 427 428 nodeElement = (Element )n; 429 430 String ln = n.getLocalName(); 431 432 if (ln.equals(SVG12Constants.SVG_FLOW_LINE_TAG)) { 433 fillAttributedStringBuffer(ctx, nodeElement, 434 false, subBidiLevel, 435 asb, lnLocs); 436 lineBreak = asb.length(); 439 lnLocs.add(new Integer (lineBreak)); 440 } else if (ln.equals(SVG12Constants.SVG_FLOW_SPAN_TAG) || 441 ln.equals(SVG12Constants.SVG_ALT_GLYPH_TAG)) { 442 fillAttributedStringBuffer(ctx, nodeElement, 443 false, subBidiLevel, 444 asb, lnLocs); 445 } else if (ln.equals(SVG_A_TAG)) { 446 if (ctx.isInteractive()) { 447 EventTarget target = (EventTarget )nodeElement; 448 UserAgent ua = ctx.getUserAgent(); 449 target.addEventListener 450 (SVG_EVENT_CLICK, 451 new SVGAElementBridge.AnchorListener(ua), 452 false); 453 454 target.addEventListener 455 (SVG_EVENT_MOUSEOVER, 456 new SVGAElementBridge.CursorMouseOverListener(ua), 457 false); 458 459 target.addEventListener 460 (SVG_EVENT_MOUSEOUT, 461 new SVGAElementBridge.CursorMouseOutListener(ua), 462 false); 463 } 464 fillAttributedStringBuffer(ctx, 465 nodeElement, 466 false, subBidiLevel, 467 asb, lnLocs); 468 } else if (ln.equals(SVG_TREF_TAG)) { 469 String uriStr = XLinkSupport.getXLinkHref((Element )n); 470 Element ref = ctx.getReferencedElement((Element )n, uriStr); 471 s = TextUtilities.getElementContent(ref); 472 s = normalizeString(s, preserve, prevEndsWithSpace); 473 if (s != null) { 474 Map m = getAttributeMap(ctx, nodeElement, null, 475 bidiLevel); 476 asb.append(s, m); 477 } 478 } 479 break; 480 481 case Node.TEXT_NODE: 482 case Node.CDATA_SECTION_NODE: 483 s = n.getNodeValue(); 484 s = normalizeString(s, preserve, prevEndsWithSpace); 485 asb.append(s, map); 486 if (preserve) 487 endLimit = asb.length(); 488 } 489 } 490 491 if (top) { 492 while ((endLimit < asb.length()) && (asb.getLastChar() == ' ')) { 493 int idx = lnLocs.size()-1; 494 int len = asb.length(); 495 if (idx >= 0) { 496 Integer i = (Integer )lnLocs.get(idx); 497 if (i.intValue() >= len) { 498 i = new Integer (len-1); 499 lnLocs.set(idx, i); 500 idx--; 501 while (idx >= 0) { 502 i = (Integer )lnLocs.get(idx); 503 if (i.intValue() < len-1) 504 break; 505 lnLocs.remove(idx); 506 idx--; 507 } 508 } 509 } 510 asb.stripLast(); 511 } 512 } 513 } 514 515 518 protected Map getAttributeMap(BridgeContext ctx, 519 Element element, 520 TextPath textPath, 521 Integer bidiLevel) { 522 Map result = super.getAttributeMap(ctx, element, textPath, bidiLevel); 523 524 float fontSize = TextUtilities.convertFontSize(element).floatValue(); 525 float lineHeight = getLineHeight(ctx, element, fontSize); 526 result.put(LINE_HEIGHT, new Float (lineHeight)); 527 528 return result; 529 } 530 531 protected final static 532 GVTAttributedCharacterIterator.TextAttribute TEXTPATH = 533 GVTAttributedCharacterIterator.TextAttribute.TEXTPATH; 534 535 protected final static 536 GVTAttributedCharacterIterator.TextAttribute ANCHOR_TYPE = 537 GVTAttributedCharacterIterator.TextAttribute.ANCHOR_TYPE; 538 539 protected final static 540 GVTAttributedCharacterIterator.TextAttribute LETTER_SPACING = 541 GVTAttributedCharacterIterator.TextAttribute.LETTER_SPACING; 542 543 protected final static 544 GVTAttributedCharacterIterator.TextAttribute WORD_SPACING = 545 GVTAttributedCharacterIterator.TextAttribute.WORD_SPACING; 546 547 protected final static 548 GVTAttributedCharacterIterator.TextAttribute KERNING = 549 GVTAttributedCharacterIterator.TextAttribute.KERNING; 550 551 protected void checkMap(Map attrs) { 552 if (attrs.containsKey(TEXTPATH)) { 553 return; } 555 556 if (attrs.containsKey(ANCHOR_TYPE)) { 557 return; } 559 560 if (attrs.containsKey(LETTER_SPACING)) { 561 return; } 563 564 if (attrs.containsKey(WORD_SPACING)) { 565 return; } 567 568 if (attrs.containsKey(KERNING)) { 569 return; } 571 } 572 573 int marginTopIndex = -1; 574 int marginRightIndex = -1; 575 int marginBottomIndex = -1; 576 int marginLeftIndex = -1; 577 int indentIndex = -1; 578 int textAlignIndex = -1; 579 int lineHeightIndex = -1; 580 581 protected void initCSSPropertyIndexes(Element e) { 582 CSSEngine eng = CSSUtilities.getCSSEngine(e); 583 marginTopIndex = eng.getPropertyIndex(SVG12CSSConstants.CSS_MARGIN_TOP_PROPERTY); 584 marginRightIndex = eng.getPropertyIndex(SVG12CSSConstants.CSS_MARGIN_RIGHT_PROPERTY); 585 marginBottomIndex = eng.getPropertyIndex(SVG12CSSConstants.CSS_MARGIN_BOTTOM_PROPERTY); 586 marginLeftIndex = eng.getPropertyIndex(SVG12CSSConstants.CSS_MARGIN_LEFT_PROPERTY); 587 indentIndex = eng.getPropertyIndex(SVG12CSSConstants.CSS_INDENT_PROPERTY); 588 textAlignIndex = eng.getPropertyIndex(SVG12CSSConstants.CSS_TEXT_ALIGN_PROPERTY); 589 lineHeightIndex = eng.getPropertyIndex(SVG12CSSConstants.CSS_LINE_HEIGHT_PROPERTY); 590 } 591 592 public BlockInfo makeBlockInfo(BridgeContext ctx, Element element) { 593 if (marginTopIndex == -1) initCSSPropertyIndexes(element); 594 595 Value v; 596 v = CSSUtilities.getComputedStyle(element, marginTopIndex); 597 float top = v.getFloatValue(); 598 599 v = CSSUtilities.getComputedStyle(element, marginRightIndex); 600 float right = v.getFloatValue(); 601 602 v = CSSUtilities.getComputedStyle(element, marginBottomIndex); 603 float bottom = v.getFloatValue(); 604 605 v = CSSUtilities.getComputedStyle(element, marginLeftIndex); 606 float left = v.getFloatValue(); 607 608 v = CSSUtilities.getComputedStyle(element, indentIndex); 609 float indent = v.getFloatValue(); 610 611 v = CSSUtilities.getComputedStyle(element, textAlignIndex); 612 if (v == ValueConstants.INHERIT_VALUE) { 613 v = CSSUtilities.getComputedStyle(element, 614 SVGCSSEngine.DIRECTION_INDEX); 615 if (v == ValueConstants.LTR_VALUE) 616 v = SVG12ValueConstants.START_VALUE; 617 else 618 v = SVG12ValueConstants.END_VALUE; 619 } 620 int textAlign; 621 if (v == SVG12ValueConstants.START_VALUE) 622 textAlign = BlockInfo.ALIGN_START; 623 else if (v == SVG12ValueConstants.MIDDLE_VALUE) 624 textAlign = BlockInfo.ALIGN_MIDDLE; 625 else if (v == SVG12ValueConstants.END_VALUE) 626 textAlign = BlockInfo.ALIGN_END; 627 else 628 textAlign = BlockInfo.ALIGN_FULL; 629 630 Map fontAttrs = getFontProperties(ctx, element, null); 631 Float fs = (Float )fontAttrs.get(TextAttribute.SIZE); 632 float fontSize = fs.floatValue(); 633 float lineHeight = getLineHeight(ctx, element, fontSize); 634 List fontFamilyList = getFontFamilyList(element, ctx); 635 636 String ln = element.getLocalName(); 637 boolean rgnBr; 638 rgnBr = ln.equals(SVG12Constants.SVG_FLOW_REGION_BREAK_TAG); 639 return new BlockInfo(top, right, bottom, left, indent, textAlign, 640 lineHeight, fontFamilyList, fontAttrs, 641 rgnBr); 642 } 643 644 protected float getLineHeight(BridgeContext ctx, Element element, 645 float fontSize) { 646 if (lineHeightIndex == -1) initCSSPropertyIndexes(element); 647 648 Value v = CSSUtilities.getComputedStyle(element, lineHeightIndex); 649 if ((v == ValueConstants.INHERIT_VALUE) || 650 (v == SVG12ValueConstants.NORMAL_VALUE)) { 651 return fontSize*1.1f; 652 } 653 654 float lineHeight = v.getFloatValue(); 655 if (v instanceof ComputedValue) 656 v = ((ComputedValue)v).getComputedValue(); 657 if ((v instanceof LineHeightValue) && 658 ((LineHeightValue)v).getFontSizeRelative()) 659 lineHeight *= fontSize; 660 return lineHeight; 661 } 662 } 663 | Popular Tags |