1 51 package org.apache.fop.layout; 52 53 import java.util.ArrayList ; 55 import java.util.StringTokenizer ; 56 import java.awt.Rectangle ; 57 58 import org.apache.fop.datatypes.IDReferences; 60 import org.apache.fop.fo.properties.Hyphenate; 61 import org.apache.fop.fo.properties.LeaderAlignment; 62 import org.apache.fop.fo.properties.LeaderPattern; 63 import org.apache.fop.fo.properties.TextAlign; 64 import org.apache.fop.fo.properties.VerticalAlign; 65 import org.apache.fop.fo.properties.WhiteSpaceCollapse; 66 import org.apache.fop.fo.properties.WrapOption; 67 import org.apache.fop.layout.hyphenation.Hyphenation; 68 import org.apache.fop.layout.hyphenation.Hyphenator; 69 import org.apache.fop.layout.inline.*; 70 import org.apache.fop.messaging.MessageHandler; 71 import org.apache.fop.render.Renderer; 72 73 public class LineArea extends Area { 74 75 protected int lineHeight; 76 protected int halfLeading; 77 protected int nominalFontSize; 78 protected int nominalGlyphHeight; 79 80 protected int allocationHeight; 81 protected int startIndent; 82 protected int endIndent; 83 84 private int placementOffset; 85 private int textAlign; 86 87 private FontState currentFontState; private float red, green, blue; 90 private int wrapOption; 91 private int whiteSpaceCollapse; 92 private int vAlign; 93 94 95 private HyphenationProps hyphProps; 96 97 101 private int finalWidth = 0; 102 103 106 protected int embeddedLinkStart = 0; 107 108 109 111 112 protected static final int NOTHING = 0; 113 protected static final int WHITESPACE = 1; 114 protected static final int TEXT = 2; 115 protected static final int MULTIBYTECHAR = 3; 116 117 118 private int prev = NOTHING; 119 120 121 private int spaceWidth = 0; 122 123 128 private ArrayList pendingAreas = new ArrayList (); 129 130 131 private int pendingWidth = 0; 132 133 134 protected boolean prevUlState = false; 135 protected boolean prevOlState = false; 136 protected boolean prevLTState = false; 137 138 private boolean aligned = false; 141 private boolean hasPageNumbers = false; 142 143 public class Leader { 144 int leaderPattern; 145 int leaderLengthMinimum; 146 int leaderLengthOptimum; 147 int leaderLengthMaximum; 148 int ruleStyle; 149 int ruleThickness; 150 int leaderPatternWidth; 151 int leaderAlignment; 152 FontState fontState; 153 float red; 154 float green; 155 float blue; 156 int placementOffset; 157 int position; 158 159 Leader(int leaderPattern, int leaderLengthMinimum, 160 int leaderLengthOptimum, int leaderLengthMaximum, 161 int ruleStyle, int ruleThickness, 162 int leaderPatternWidth, int leaderAlignment, 163 FontState fontState, 164 float red, float green, float blue, 165 int placementOffset, 166 int position) { 167 this.leaderPattern=leaderPattern; 168 this.leaderLengthMinimum=leaderLengthMinimum; 169 this.leaderLengthOptimum=leaderLengthOptimum; 170 this.leaderLengthMaximum=leaderLengthMaximum; 171 this.ruleStyle=ruleStyle; 172 this.ruleThickness=ruleThickness; 173 this.leaderPatternWidth=leaderPatternWidth; 174 this.leaderAlignment=leaderAlignment; 175 this.fontState=fontState; 176 this.red=red; 177 this.green=green; 178 this.blue=blue; 179 this.placementOffset=placementOffset; 180 this.position = position; 181 } 182 void expand() { 183 char dot = '.'; 184 int dotWidth = fontState.getCharWidth(dot); 185 char space = ' '; 186 int spaceWidth = fontState.getCharWidth(space); 187 int idx=children.indexOf(this); 188 children.remove(this); 189 switch (leaderPattern) { 190 case LeaderPattern.SPACE: 191 InlineSpace spaceArea = new InlineSpace(leaderLengthOptimum 192 , false); 193 children.add(idx,spaceArea); 194 break; 195 case LeaderPattern.RULE: 196 LeaderArea leaderArea = new LeaderArea(fontState, red, green, 197 blue, "", 198 leaderLengthOptimum, 199 leaderPattern, 200 ruleThickness, 201 ruleStyle); 202 leaderArea.setYOffset(placementOffset); 203 children.add(idx,leaderArea); 204 break; 205 case LeaderPattern.DOTS: 206 if (this.leaderPatternWidth < dotWidth) { 209 this.leaderPatternWidth = 0; 210 } 211 if (this.leaderPatternWidth == 0) { 213 if (dotWidth == 0) { 214 MessageHandler.errorln("char " + dot 215 + " has width 0. Using width 100 instead."); 216 dotWidth = 100; 217 } 218 if (leaderAlignment == LeaderAlignment.REFERENCE_AREA) { 222 int nextRepeatedLeaderPatternCycle 223 = (int)Math.ceil((double)position/(double)dotWidth); 224 int spaceBeforeLeader = 225 dotWidth * nextRepeatedLeaderPatternCycle 226 - position; 227 if (spaceBeforeLeader > 0) { 231 children.add(idx, new InlineSpace(spaceBeforeLeader, 232 false)); 233 idx++; 234 leaderLengthOptimum -= spaceBeforeLeader; 238 } 239 } 240 int factor = (int)Math.floor(leaderLengthOptimum / dotWidth); 241 char[] leaderChars = new char[factor]; 242 for (int i = 0; i < factor; i++) { 243 leaderChars[i] = dot; 244 } 245 String leaderWord = new String (leaderChars); 246 int leaderWordWidth = fontState.getWordWidth(leaderWord); 247 WordArea leaderPatternArea = 248 new WordArea(fontState, red, green, blue, 249 leaderWord,leaderWordWidth); 250 leaderPatternArea.setYOffset(placementOffset); 251 children.add(idx, leaderPatternArea); 252 int spaceAfterLeader = leaderLengthOptimum 253 - leaderWordWidth; 254 if (spaceAfterLeader!=0) { 255 children.add(idx+1, new InlineSpace(spaceAfterLeader, 256 false)); 257 } 258 } else { 259 if (leaderAlignment == LeaderAlignment.REFERENCE_AREA) { 263 int nextRepeatedLeaderPatternCycle 264 = (int)Math.ceil((double)position/(double)leaderPatternWidth); 265 int spaceBeforeLeader = 266 leaderPatternWidth * nextRepeatedLeaderPatternCycle 267 - position; 268 if (spaceBeforeLeader > 0) { 272 children.add(idx, new InlineSpace(spaceBeforeLeader, 273 false)); 274 idx++; 275 leaderLengthOptimum -= spaceBeforeLeader; 279 } 280 } 281 int dotsFactor = 284 (int)Math.floor(((double)leaderLengthOptimum) 285 / ((double)leaderPatternWidth)); 286 for (int i = 0; i < dotsFactor; i++) { 289 InlineSpace spaceBetweenDots = 290 new InlineSpace(leaderPatternWidth - dotWidth, 291 false); 292 WordArea leaderPatternArea = 293 new WordArea(this.fontState, 294 this.red, this.green, this.blue, 295 new String ("."), dotWidth); 296 leaderPatternArea.setYOffset(placementOffset); 297 children.add(idx,leaderPatternArea); 298 idx++; 299 children.add(idx,spaceBetweenDots); 300 idx++; 301 } 302 children.add(idx,new InlineSpace(leaderLengthOptimum 304 - dotsFactor 305 * leaderPatternWidth)); 306 idx++; 307 } 308 break; 309 case LeaderPattern.USECONTENT: 311 MessageHandler.errorln("leader-pattern=\"use-content\" not " 312 + "supported by this version of Fop"); 313 return; 314 } 315 } 316 } 317 318 public LineArea(FontState fontState, int lineHeight, int halfLeading, 319 int allocationWidth, int startIndent, int endIndent, 320 LineArea prevLineArea) { 321 super(fontState); 322 323 this.currentFontState = fontState; 324 this.lineHeight = lineHeight; 325 this.nominalFontSize = fontState.getFontSize(); 326 this.nominalGlyphHeight = fontState.getAscender() 327 - fontState.getDescender(); 328 329 this.placementOffset = fontState.getAscender(); 330 this.contentRectangleWidth = allocationWidth - startIndent 331 - endIndent; 332 this.fontState = fontState; 333 334 this.allocationHeight = this.nominalGlyphHeight; 335 this.halfLeading = this.lineHeight - this.allocationHeight; 336 337 this.startIndent = startIndent; 338 this.endIndent = endIndent; 339 340 if (prevLineArea != null) { 341 boolean eatMoreSpace = true; 344 pendingWidth = prevLineArea.pendingWidth; 345 346 for (int i = 0; i < prevLineArea.pendingAreas.size(); i++) { 347 Object b = prevLineArea.pendingAreas.get(i); 348 if (eatMoreSpace) { 349 if (b instanceof InlineSpace) { 350 InlineSpace is = (InlineSpace)b; 351 if (is.isEatable()) { 352 pendingWidth -= is.getSize(); 353 } else { 354 eatMoreSpace = false; 355 pendingAreas.add(b); 356 } 357 } else { 358 eatMoreSpace = false; 359 pendingAreas.add(b); 360 } 361 } else { 362 pendingAreas.add(b); 363 } 364 } 365 prevLineArea.pendingWidth=0; 366 prevLineArea.pendingAreas=null; 367 } 368 } 369 370 public void render(Renderer renderer) { 371 if (pendingWidth > 0) { 372 MessageHandler.error("Areas pending, text probably lost in line" 373 + getLineText()); 374 } 375 if (hasPageNumbers) { 376 IDReferences idReferences = renderer.getIDReferences(); 377 for (int i = 0; i < children.size(); i++) { 378 Object o = children.get(i); 379 if ( o instanceof PageNumberInlineArea) { 380 PageNumberInlineArea pia = (PageNumberInlineArea)o; 381 FontState piaFontState = pia.getFontState(); 382 finalWidth-=piaFontState.getWordWidth(pia.getText()); 383 pia.resolve(idReferences); 384 finalWidth+=piaFontState.getWordWidth(pia.getText()); 385 } 386 } 387 } 388 if (!aligned) { 389 int padding = 0; 390 switch (textAlign) { 391 case TextAlign.START: padding = this.getContentWidth() - finalWidth; 393 endIndent += padding; 394 for (int i = 0; i < children.size(); i++) { 395 Object o = children.get(i); 396 if (o instanceof LineArea.Leader) { 397 LineArea.Leader leader = (LineArea.Leader)o; 398 leader.expand(); 399 } 400 } 401 break; 402 case TextAlign.END: padding = this.getContentWidth() - finalWidth; 404 startIndent += padding; 405 for (int i = 0; i < children.size(); i++) { 406 Object o = children.get(i); 407 if (o instanceof LineArea.Leader) { 408 LineArea.Leader leader = (LineArea.Leader)o; 409 leader.expand(); 410 } 411 } 412 break; 413 case TextAlign.CENTER: padding = (this.getContentWidth() - finalWidth) / 2; 415 startIndent += padding; 416 endIndent += padding; 417 for (int i = 0; i < children.size(); i++) { 418 Object o = children.get(i); 419 if (o instanceof LineArea.Leader) { 420 LineArea.Leader leader = (LineArea.Leader)o; 421 leader.expand(); 422 } 423 } 424 break; 425 case TextAlign.JUSTIFY: int leaderCount = 0; 428 int spaceCount = 0; 429 for (int i = 0; i < children.size(); i++ ) { 430 Object o = children.get(i); 431 if (o instanceof InlineSpace) { 432 InlineSpace space = (InlineSpace)o; 433 if (space.getResizeable()) { 434 spaceCount++; 435 } 436 } else if(o instanceof LineArea.Leader) { 437 leaderCount++; 438 } 439 } 440 padding = (this.getContentWidth() - finalWidth); 441 if (padding!=0) { 442 if (leaderCount>0) { 443 int offset=0; 444 for (int i = 0; i < children.size(); i++) { 445 Object o = children.get(i); 446 if (o instanceof LineArea.Leader) { 447 LineArea.Leader leader = (LineArea.Leader)o; 448 int leaderExpansionMaximum= 449 leader.leaderLengthMaximum - leader.leaderLengthOptimum; 450 int leaderExpansionMinimum= 451 leader.leaderLengthMinimum - leader.leaderLengthOptimum; 452 if (leaderExpansionMaximum < padding) { 453 leader.leaderLengthOptimum = 454 leader.leaderLengthMaximum; 455 leader.expand(); 456 padding-=leaderExpansionMaximum; 457 offset+=leaderExpansionMaximum; 458 } else if (padding < leaderExpansionMinimum) { 459 leader.leaderLengthOptimum = 460 leader.leaderLengthMinimum; 461 leader.expand(); 462 padding-=leaderExpansionMinimum; 463 offset+=leaderExpansionMinimum; 464 } else { 465 leader.leaderLengthOptimum += padding; 466 leader.expand(); 467 padding=0; 468 offset+=padding; 469 } 470 } else if (o instanceof InlineArea) { 471 ((InlineArea)o).setXOffset(((InlineArea)o).getXOffset() + offset); 472 } 473 } 474 } 475 if (padding != 0) { 476 if (spaceCount > 0) { 477 if (padding > 0) { 478 padding = padding/spaceCount; 487 spaceCount = 0; 488 for (int i = 0; i < children.size(); i++) { 490 Object o = children.get(i); 491 if (o instanceof InlineSpace) { 492 InlineSpace space = (InlineSpace)o; 493 if (space.getResizeable()) { 494 space.setSize(space.getSize() + padding); 495 spaceCount++; 496 } 497 } else if (o instanceof InlineArea) { 498 ((InlineArea)o).setXOffset(((InlineArea)o).getXOffset() + i * padding); 499 } 500 } 501 } else { 502 MessageHandler.log("Area overflow in line " 503 + getLineText()); 504 } 505 } else { 506 MessageHandler.log("No spaces to justify text in line " 508 + getLineText()); 509 } 510 } 511 } 512 break; 513 default: 514 MessageHandler.errorln("bad align: "+textAlign); 515 break; 516 } 517 aligned = true; 518 } 519 renderer.renderLineArea(this); 520 } 521 522 public int addPageNumberCitation(String refid, LinkSet ls) { 523 524 528 529 532 int width = 3*currentFontState.getCharWidth(' '); 533 534 535 PageNumberInlineArea pia 536 = new PageNumberInlineArea(currentFontState, 537 this.red, this.green, this.blue, 538 refid, width); 539 540 pia.setYOffset(placementOffset); 541 pendingAreas.add(pia); 542 pendingWidth += width; 543 prev = TEXT; 544 hasPageNumbers = true; 545 546 return -1; 547 } 548 549 550 555 public int addText(char data[], int start, int end, LinkSet ls, 556 TextState textState) { 557 if (start == -1) 560 return -1; 561 boolean overrun = false; 562 563 int wordStart = start; 564 int wordLength = 0; 565 int wordWidth = 0; 566 int whitespaceWidth = currentFontState.getCharWidth(' '); 568 569 boolean isText = false; 570 boolean isMultiByteChar = false; 571 572 573 for (int i = start; i < end; i++) { 574 int charWidth; 575 576 char c = data[i]; 577 if (!(isSpace(c) || (c == '\n') || (c == '\r') || (c == '\t') 578 || (c == '\u2028'))) { 579 charWidth = currentFontState.getCharWidth(c); 580 isText = true; 581 isMultiByteChar = (c > 127); 582 if (charWidth <= 0 && c != '\u200B' && c != '\uFEFF') 584 charWidth = whitespaceWidth; 585 } else { 586 if ((c == '\n') || (c == '\r') || (c == '\t')) 587 charWidth = whitespaceWidth; 588 else 589 charWidth = currentFontState.getCharWidth(c); 590 591 isText = false; 592 isMultiByteChar = false; 593 594 if (prev == WHITESPACE) { 595 596 598 if (this.whiteSpaceCollapse == WhiteSpaceCollapse.FALSE) { 599 if (isSpace(c)) { 600 spaceWidth += currentFontState.getCharWidth(c); 601 } else if (c == '\n' || c == '\u2028') { 602 if (spaceWidth > 0) { 604 InlineSpace is = new InlineSpace(spaceWidth); 605 is.setUnderlined(textState.getUnderlined()); 606 is.setOverlined(textState.getOverlined()); 607 is.setLineThrough(textState.getLineThrough()); 608 addChild(is); 609 finalWidth += spaceWidth; 610 spaceWidth = 0; 611 } 612 return i + 1; 613 } else if (c == '\t') { 614 spaceWidth += 8 * whitespaceWidth; 615 } 616 } else if (c == '\u2028') { 617 if (spaceWidth > 0) { 620 InlineSpace is = new InlineSpace(spaceWidth); 621 is.setUnderlined(textState.getUnderlined()); 622 is.setOverlined(textState.getOverlined()); 623 is.setLineThrough(textState.getLineThrough()); 624 addChild(is); 625 finalWidth += spaceWidth; 626 spaceWidth = 0; 627 } 628 return i + 1; 629 } 630 631 } else if (prev == TEXT || prev == MULTIBYTECHAR ) { 632 633 638 if (spaceWidth > 0) { 639 InlineSpace is = new InlineSpace(spaceWidth); 640 if (prevUlState) { 641 is.setUnderlined(textState.getUnderlined()); 642 } 643 if (prevOlState) { 644 is.setOverlined(textState.getOverlined()); 645 } 646 if (prevLTState) { 647 is.setLineThrough(textState.getLineThrough()); 648 } 649 addChild(is); 650 finalWidth += spaceWidth; 651 spaceWidth = 0; 652 } 653 654 656 for (int j = 0; j < pendingAreas.size(); j++ ) { 657 Box box = (Box)pendingAreas.get(j); 658 if (box instanceof InlineArea) { 659 if (ls != null) { 660 Rectangle lr = 661 new Rectangle (finalWidth, 0, 662 ((InlineArea)box).getContentWidth(), 663 fontState.getFontSize()); 664 ls.addRect(lr, this, (InlineArea)box); 665 } 666 } 667 addChild(box); 668 } 669 670 finalWidth += pendingWidth; 671 672 pendingWidth = 0; 674 pendingAreas = new ArrayList (); 675 676 678 if (wordLength > 0) { 679 addSpacedWord(new String (data, wordStart, wordLength), 684 ls, finalWidth, 0, textState, false); 685 finalWidth += wordWidth; 686 687 wordWidth = 0; 689 } 690 691 prev = WHITESPACE; 694 695 embeddedLinkStart = 696 0; 698 spaceWidth = currentFontState.getCharWidth(c); 699 700 709 710 711 if (this.whiteSpaceCollapse == WhiteSpaceCollapse.FALSE) { 712 if (c == '\n' || c == '\u2028') { 713 return i + 1; 715 } else if (c == '\t') { 716 spaceWidth = whitespaceWidth; 717 } 718 } else if (c == '\u2028') { 719 return i + 1; 720 } 721 } else { 722 723 725 if (this.whiteSpaceCollapse == WhiteSpaceCollapse.FALSE) { 726 if (isSpace(c)) { 727 prev = WHITESPACE; 728 spaceWidth = currentFontState.getCharWidth(c); 729 } else if (c == '\n') { 730 InlineSpace is = new InlineSpace(spaceWidth); 733 addChild(is); 734 return i + 1; 735 } else if (c == '\t') { 736 prev = WHITESPACE; 737 spaceWidth = 8 * whitespaceWidth; 738 } 739 740 } else { 741 wordStart++; 743 } 744 } 745 746 } 747 748 if (isText) { 750 int curr = isMultiByteChar ? MULTIBYTECHAR : TEXT; 751 if (prev == WHITESPACE) { 752 753 755 wordWidth = charWidth; 756 if ((finalWidth + spaceWidth + wordWidth) 757 > this.getContentWidth()) { 758 if (overrun) 759 MessageHandler.log("area contents overflows area " 760 + "in line " 761 + getLineText()); 762 if (this.wrapOption == WrapOption.WRAP) { 763 return i; 764 } 765 } 766 prev = curr; 767 wordStart = i; 768 wordLength = 1; 769 } else if (prev == TEXT || prev == MULTIBYTECHAR ) { 770 if ( prev == TEXT && curr == TEXT || ! canBreakMidWord()) { 771 wordLength++; 772 wordWidth += charWidth; 773 } else { 774 775 InlineSpace is = new InlineSpace(spaceWidth); 777 if (prevUlState) { 778 is.setUnderlined(textState.getUnderlined()); 779 } 780 if (prevOlState) { 781 is.setOverlined(textState.getOverlined()); 782 } 783 if (prevLTState) { 784 is.setLineThrough(textState.getLineThrough()); 785 } 786 addChild(is); 787 finalWidth += spaceWidth; 788 spaceWidth = 0; 789 791 793 for (int j = 0; j < pendingAreas.size(); j++ ) { 794 Box box = (Box)pendingAreas.get(j); 795 if (box instanceof InlineArea) { 796 if (ls != null) { 797 Rectangle lr = 798 new Rectangle (finalWidth, 0, 799 ((InlineArea)box).getContentWidth(), 800 fontState.getFontSize()); 801 ls.addRect(lr, this, (InlineArea)box); 802 } 803 } 804 addChild(box); 805 } 806 807 finalWidth += pendingWidth; 808 809 pendingWidth = 0; 811 pendingAreas = new ArrayList (); 812 813 815 if (wordLength > 0) { 816 addSpacedWord(new String (data, wordStart, wordLength), 821 ls, finalWidth, 0, textState, false); 822 finalWidth += wordWidth; 823 } 824 spaceWidth = 0; 825 wordStart = i; 826 wordLength = 1; 827 wordWidth = charWidth; 828 } 829 prev = curr; 830 } else { 832 prev = curr; 833 wordStart = i; 834 wordLength = 1; 835 wordWidth = charWidth; 836 } 837 838 if ((finalWidth + spaceWidth + pendingWidth + wordWidth) 839 > this.getContentWidth()) { 840 841 853 if (this.wrapOption == WrapOption.WRAP) { 854 855 if (hyphProps.hyphenate == Hyphenate.TRUE) { 856 int ret = wordStart; 857 ret = this.doHyphenation(data, i, wordStart, 858 this.getContentWidth() 859 - (finalWidth 860 + spaceWidth 861 + pendingWidth), 862 finalWidth + spaceWidth 863 + embeddedLinkStart, 864 ls, textState); 865 866 if ((ret == wordStart) && 870 (wordStart == start) && 871 (finalWidth == 0)) { 872 873 MessageHandler.log("area contents overflows" 874 + " area in line " 875 + getLineText()); 876 addSpacedWord(new String (data, wordStart, wordLength - 1), 877 ls, 878 embeddedLinkStart, 879 spaceWidth, textState, false); 880 881 finalWidth += wordWidth; 882 wordWidth = 0; 883 ret = i; 884 } 885 return ret; 886 } else if (wordStart == start) { 887 overrun = true; 889 if (finalWidth > 0) { 892 return wordStart; 893 } 894 } else { 895 return wordStart; 896 } 897 898 } 899 } 900 } 901 } 903 if (prev == TEXT || prev == MULTIBYTECHAR) { 904 905 if (spaceWidth > 0) { 906 InlineSpace pis = new InlineSpace(spaceWidth); 907 pis.setEatable(true); 910 if (prevUlState) { 911 pis.setUnderlined(textState.getUnderlined()); 912 } 913 if (prevOlState) { 914 pis.setOverlined(textState.getOverlined()); 915 } 916 if (prevLTState) { 917 pis.setLineThrough(textState.getLineThrough()); 918 } 919 pendingAreas.add(pis); 920 pendingWidth += spaceWidth; 921 spaceWidth = 0; 922 } 923 924 addSpacedWord(new String (data, wordStart, wordLength), ls, 925 finalWidth + pendingWidth, 926 spaceWidth, textState, true); 927 928 embeddedLinkStart += wordWidth; 929 wordWidth = 0; 930 } 931 932 if (overrun) 933 MessageHandler.log("area contents overflows area in line " 934 + getLineText()); 935 return -1; 936 } 937 938 944 public void addLeader(int leaderPattern, int leaderLengthMinimum, 945 int leaderLengthOptimum, int leaderLengthMaximum, 946 int ruleStyle, int ruleThickness, 947 int leaderPatternWidth, int leaderAlignment) { 948 if (leaderLengthMinimum>leaderLengthOptimum 949 || leaderLengthOptimum>leaderLengthMaximum) { 950 MessageHandler.errorln("leader sizes wrong"); 951 return; 952 } 953 if (leaderLengthOptimum>getRemainingWidth()) { 954 MessageHandler.errorln("leader width assertion failed"); 955 return; 956 } 957 addPending(); 958 children.add(new LineArea.Leader(leaderPattern, leaderLengthMinimum, 959 leaderLengthOptimum, leaderLengthMaximum, 960 ruleStyle, ruleThickness, 961 leaderPatternWidth, leaderAlignment, 962 fontState, red, green, blue, 963 placementOffset, getCurrentXPosition())); 964 finalWidth += leaderLengthOptimum; 965 } 966 967 972 public void addPending() { 973 if (spaceWidth > 0) { 974 addChild(new InlineSpace(spaceWidth)); 975 finalWidth += spaceWidth; 976 spaceWidth = 0; 977 } 978 979 for (int i = 0; i < pendingAreas.size(); i++ ) { 980 Box box = (Box)pendingAreas.get(i); 981 addChild(box); 982 } 983 984 finalWidth += pendingWidth; 985 986 pendingWidth = 0; 988 pendingAreas = new ArrayList (); 989 } 990 991 996 public void align(int type) { 997 textAlign = type; 998 } 999 1000 1003 public void verticalAlign() { 1004 int superHeight = -this.placementOffset; 1005 int maxHeight = this.allocationHeight; 1006 for (int i = 0; i < children.size(); i++ ) { 1007 Object o = children.get(i); 1008 if (o instanceof InlineArea) { 1009 InlineArea ia = (InlineArea)o; 1010 if (ia instanceof WordArea) { 1011 ia.setYOffset(placementOffset); 1012 } 1013 if (ia.getHeight() > maxHeight) { 1014 maxHeight = ia.getHeight(); 1015 } 1016 int vert = ia.getVerticalAlign(); 1017 if (vert == VerticalAlign.SUPER) { 1018 int fh = fontState.getAscender(); 1019 ia.setYOffset((int)(placementOffset - (2 * fh / 3.0))); 1020 } else if (vert == VerticalAlign.SUB) { 1021 int fh = fontState.getAscender(); 1022 ia.setYOffset((int)(placementOffset + (2 * fh / 3.0))); 1023 } 1024 } 1025 } 1026 this.allocationHeight = maxHeight; 1029 } 1030 1031 public void changeColor(float red, float green, float blue) { 1032 this.red = red; 1033 this.green = green; 1034 this.blue = blue; 1035 } 1036 1037 public void changeFont(FontState fontState) { 1038 this.currentFontState = fontState; 1039 } 1040 1041 public void changeWhiteSpaceCollapse(int whiteSpaceCollapse) { 1042 this.whiteSpaceCollapse = whiteSpaceCollapse; 1043 } 1044 1045 public void changeWrapOption(int wrapOption) { 1046 this.wrapOption = wrapOption; 1047 } 1048 1049 public void changeVerticalAlign(int vAlign) { 1050 this.vAlign = vAlign; 1051 } 1052 1053 public int getEndIndent() { 1054 return endIndent; 1055 } 1056 1057 public int getHeight() { 1058 return this.allocationHeight; 1059 } 1060 1061 public int getPlacementOffset() { 1062 return this.placementOffset; 1063 } 1064 1065 public int getStartIndent() { 1066 return startIndent; 1067 } 1068 1069 public boolean isEmpty() { 1070 return !(pendingAreas.size() > 0 || children.size() > 0); 1071 } 1073 1074 1079 public void changeHyphenation(HyphenationProps hyphProps) { 1080 this.hyphProps = hyphProps; 1081 } 1082 1083 1084 1088 private InlineArea buildSimpleLeader(char c, int leaderLength) { 1089 int width = currentFontState.getCharWidth(c); 1090 if (width == 0) { 1091 MessageHandler.errorln("char " + c 1092 + " has width 0. Using width 100 instead."); 1093 width = 100; 1094 } 1095 int factor = (int)Math.floor(leaderLength / width); 1096 char[] leaderChars = new char[factor]; 1097 for (int i = 0; i < factor; i++) { 1098 leaderChars[i] = c; 1099 } 1100 WordArea leaderPatternArea = new WordArea(currentFontState, this.red, 1101 this.green, this.blue, 1102 new String (leaderChars), 1103 leaderLength); 1104 leaderPatternArea.setYOffset(placementOffset); 1105 return leaderPatternArea; 1106 } 1107 1108 1111 private int getCurrentXPosition() { 1112 return finalWidth + spaceWidth + startIndent + pendingWidth; 1113 } 1114 1115 1118 private String getHyphenationWord(char[] characters, int wordStart) { 1119 boolean wordendFound = false; 1120 int counter = 0; 1121 char[] newWord = new char[characters.length]; while ((!wordendFound) 1123 && ((wordStart + counter) < characters.length)) { 1124 char tk = characters[wordStart + counter]; 1125 if (Character.isLetter(tk)) { 1126 newWord[counter] = tk; 1127 counter++; 1128 } else { 1129 wordendFound = true; 1130 } 1131 } 1132 return new String (newWord, 0, counter); 1133 } 1134 1135 1136 1141 public int doHyphenation(char[] characters, int position, int wordStart, 1142 int remainingWidth, int startw, LinkSet ls, 1143 TextState textState) { 1144 if (this.hyphProps.language.equalsIgnoreCase("none")) { 1146 MessageHandler.errorln("if property 'hyphenate' is used, a " 1147 + "language must be specified in line " 1148 + getLineText()); 1149 return wordStart; 1150 } 1151 1152 1155 StringBuffer remainingString = new StringBuffer (); 1156 1157 1160 StringBuffer preString = null; 1161 1162 1165 char inwordPunctuation; 1166 1167 1170 String wordToHyphenate; 1171 1172 int hyphCharWidth = currentFontState 1174 .getCharWidth(this.hyphProps.hyphenationChar); 1175 remainingWidth -= hyphCharWidth; 1176 1177 if (characters[wordStart] == '"' || characters[wordStart] == '\'') { 1179 remainingString.append(characters[wordStart]); 1180 wordToHyphenate = getHyphenationWord(characters, wordStart + 1); 1182 } else { 1183 wordToHyphenate = getHyphenationWord(characters, wordStart); 1184 } 1185 1186 if (currentFontState.getWordWidth(wordToHyphenate) < remainingWidth) { 1190 inwordPunctuation = 1191 characters[wordStart + remainingString.length() 1192 + wordToHyphenate.length()]; 1193 if (inwordPunctuation == '-' || inwordPunctuation == '/') { 1194 preString = new StringBuffer (wordToHyphenate); 1195 preString = preString.append(inwordPunctuation); 1196 wordToHyphenate = 1197 getHyphenationWord(characters, 1198 wordStart + remainingString.length() 1199 + wordToHyphenate.length() + 1); 1200 remainingWidth -= 1201 (currentFontState.getWordWidth(wordToHyphenate) 1202 + currentFontState.getCharWidth(inwordPunctuation)); 1203 } 1204 } 1205 1206 Hyphenation hyph = 1208 Hyphenator.hyphenate(hyphProps.language, hyphProps.country, 1209 wordToHyphenate, 1210 hyphProps.hyphenationRemainCharacterCount, 1211 hyphProps.hyphenationPushCharacterCount); 1212 if (hyph == null && preString == null) { 1213 return wordStart; 1215 } else if (hyph == null && preString != null) { 1216 remainingString.append(preString.toString()); 1218 this.addWord(remainingString, startw, ls, textState); 1219 return wordStart + remainingString.length(); 1220 } else if (hyph != null && preString == null) { 1221 int index = getFinalHyphenationPoint(hyph, remainingWidth); 1223 if (index != -1) { 1224 remainingString.append(hyph.getPreHyphenText(index)); 1225 remainingString.append(this.hyphProps.hyphenationChar); 1226 this.addWord(remainingString, startw, ls, textState); 1227 return wordStart + remainingString.length() - 1; 1228 } 1229 } else if (hyph != null && preString != null) { 1230 int index = getFinalHyphenationPoint(hyph, remainingWidth); 1232 if (index != -1) { 1233 remainingString.append(preString.append(hyph.getPreHyphenText(index)).toString()); 1234 remainingString.append(this.hyphProps.hyphenationChar); 1235 this.addWord(remainingString, startw, ls, textState); 1236 return wordStart + remainingString.length() - 1; 1237 } else { 1238 remainingString.append(preString.toString()); 1239 this.addWord(remainingString, startw, ls, textState); 1240 return wordStart + remainingString.length(); 1241 } 1242 } 1243 return wordStart; 1244 } 1245 1246 public int getRemainingWidth() { 1247 return this.getContentWidth() + startIndent - this.getCurrentXPosition(); 1248 } 1249 1250 public void setLinkSet(LinkSet ls) {} 1251 1252 public void addInlineArea(InlineArea box, LinkSet ls) { 1253 addPending(); 1254 addChild(box); 1255 if (ls != null) { 1256 Rectangle lr = new Rectangle (finalWidth, 0,box.getContentWidth(), 1257 box.getContentHeight()); 1258 ls.addRect(lr, this, box); 1259 } 1260 prev = TEXT; 1261 finalWidth += box.getContentWidth(); 1262 } 1263 1264 public void addInlineSpace(InlineSpace is, int spaceWidth) { 1265 addChild(is); 1266 finalWidth += spaceWidth; 1267 } 1269 1270 1273 public int addCharacter(char data, LinkSet ls, boolean ul) { 1274 WordArea ia = null; 1275 int remainingWidth = this.getRemainingWidth(); 1276 int width = currentFontState.getCharWidth(data); 1277 if (width > remainingWidth) { 1279 return org.apache.fop.fo.flow.Character.DOESNOT_FIT; 1280 } else { 1281 if (Character.isSpaceChar(data) 1283 && whiteSpaceCollapse == WhiteSpaceCollapse.TRUE) { 1284 return org.apache.fop.fo.flow.Character.OK; 1285 } 1286 ia = new WordArea(currentFontState, this.red, this.green, 1288 this.blue, new Character (data).toString(), 1289 width); 1290 ia.setYOffset(placementOffset); 1291 ia.setUnderlined(ul); 1292 pendingAreas.add(ia); 1293 if (Character.isSpaceChar(data)) { 1294 this.spaceWidth = +width; 1295 prev = LineArea.WHITESPACE; 1296 } else { 1297 pendingWidth += width; 1298 prev = LineArea.TEXT; 1299 } 1300 return org.apache.fop.fo.flow.Character.OK; 1301 } 1302 } 1303 1304 1305 1306 1310 private void addWord(StringBuffer wordBuf, int startw, 1311 LinkSet ls, TextState textState) { 1312 if (spaceWidth > 0) { 1313 InlineSpace is = new InlineSpace(spaceWidth); 1314 if (prevUlState) { 1315 is.setUnderlined(textState.getUnderlined()); 1316 } 1317 if (prevOlState) { 1318 is.setOverlined(textState.getOverlined()); 1319 } 1320 if (prevLTState) { 1321 is.setLineThrough(textState.getLineThrough()); 1322 } 1323 addChild(is); 1324 finalWidth += spaceWidth; 1325 spaceWidth = 0; 1326 } 1327 1328 1330 for (int i = 0; i < pendingAreas.size(); i++ ) { 1331 Box box = (Box)pendingAreas.get(i); 1332 if (box instanceof InlineArea) { 1333 if (ls != null) { 1334 Rectangle lr = 1335 new Rectangle (finalWidth, 0, 1336 ((InlineArea)box).getContentWidth(), 1337 fontState.getFontSize()); 1338 ls.addRect(lr, this, (InlineArea)box); 1339 } 1340 } 1341 addChild(box); 1342 } 1343 1344 finalWidth += pendingWidth; 1345 1346 pendingWidth = 0; 1348 pendingAreas = new ArrayList (); 1349 String word = (wordBuf != null) ? wordBuf.toString() : ""; 1350 int wordWidth = currentFontState.getWordWidth(word); 1351 WordArea hia = new WordArea(currentFontState, 1352 this.red, this.green, this.blue, 1353 word, wordWidth); 1354 hia.setYOffset(placementOffset); 1355 hia.setUnderlined(textState.getUnderlined()); 1356 prevUlState = textState.getUnderlined(); 1357 hia.setOverlined(textState.getOverlined()); 1358 prevOlState = textState.getOverlined(); 1359 hia.setLineThrough(textState.getLineThrough()); 1360 prevLTState = textState.getLineThrough(); 1361 hia.setVerticalAlign(vAlign); 1362 this.addChild(hia); 1363 1364 if (ls != null) { 1365 Rectangle lr = new Rectangle (startw, 0, 1366 hia.getContentWidth(), 1367 fontState.getFontSize()); 1368 ls.addRect(lr, this, hia); 1369 } 1370 finalWidth += wordWidth; 1372 } 1373 1374 1375 1378 private int getFinalHyphenationPoint(Hyphenation hyph, 1379 int remainingWidth) { 1380 int[] hyphenationPoints = hyph.getHyphenationPoints(); 1381 int numberOfHyphenationPoints = hyphenationPoints.length; 1382 1383 int index = -1; 1384 String wordBegin = ""; 1385 int wordBeginWidth = 0; 1386 1387 for (int i = 0; i < numberOfHyphenationPoints; i++) { 1388 wordBegin = hyph.getPreHyphenText(i); 1389 if (currentFontState.getWordWidth(wordBegin) > remainingWidth) { 1390 break; 1391 } 1392 index = i; 1393 } 1394 return index; 1395 } 1396 1397 1402 private boolean canBreakMidWord() { 1403 boolean ret = false; 1404 if (hyphProps != null && hyphProps.language != null 1405 &&!hyphProps.language.equals("NONE")) { 1406 String lang = hyphProps.language.toLowerCase(); 1407 if ("zh".equals(lang) || "ja".equals(lang) || "ko".equals(lang) 1408 || "vi".equals(lang)) 1409 ret = true; 1410 } 1411 return ret; 1412 } 1413 1414 1415 1416 1421 private boolean isSpace(char c) { 1422 if (c == ' ' || c == '\u2000' || c == '\u2001' || c == '\u2002' || c == '\u2003' || c == '\u2004' || c == '\u2005' || c == '\u2006' || c == '\u2007' || c == '\u2008' || c == '\u2009' || c == '\u200A' || c == '\u200B') return true; 1435 else 1436 return false; 1437 } 1438 1439 1440 1444 private boolean isNBSP(char c) { 1445 if (c == '\u00A0' || c == '\u202F' || c == '\u3000' || c == '\uFEFF') { return true; 1449 } else 1450 return false; 1451 } 1452 1453 1456 private boolean isAnySpace(char c) { 1457 boolean ret = (isSpace(c) || isNBSP(c)); 1458 return ret; 1459 } 1460 1461 1466 private void addSpacedWord(String word, LinkSet ls, int startw, 1467 int spacew, TextState textState, 1468 boolean addToPending) { 1469 StringTokenizer st = new StringTokenizer (word, "\u00A0\u202F\u3000\uFEFF", true); 1470 while (st.hasMoreTokens()) { 1471 String currentWord = st.nextToken(); 1472 1473 if (currentWord.length() == 1 1474 && (isNBSP(currentWord.charAt(0)))) { 1475 int spaceWidth = currentFontState 1477 .getCharWidth(currentWord.charAt(0)); 1478 if (spaceWidth > 0) { 1479 InlineSpace is = new InlineSpace(spaceWidth); 1480 startw += spaceWidth; 1481 if (prevUlState) { 1482 is.setUnderlined(textState.getUnderlined()); 1483 } 1484 if (prevOlState) { 1485 is.setOverlined(textState.getOverlined()); 1486 } 1487 if (prevLTState) { 1488 is.setLineThrough(textState.getLineThrough()); 1489 } 1490 1491 if (addToPending) { 1492 pendingAreas.add(is); 1493 pendingWidth += spaceWidth; 1494 } else { 1495 addChild(is); 1496 } 1497 } 1498 } else { 1499 int wordWidth = currentFontState.getWordWidth(currentWord); 1500 WordArea ia = new WordArea(currentFontState, this.red, 1501 this.green, this.blue, 1502 currentWord, 1503 wordWidth); 1504 ia.setYOffset(placementOffset); 1505 ia.setUnderlined(textState.getUnderlined()); 1506 prevUlState = textState.getUnderlined(); 1507 ia.setOverlined(textState.getOverlined()); 1508 prevOlState = textState.getOverlined(); 1509 ia.setLineThrough(textState.getLineThrough()); 1510 prevLTState = textState.getLineThrough(); 1511 ia.setVerticalAlign(vAlign); 1512 1513 if (addToPending) { 1514 pendingAreas.add(ia); 1515 pendingWidth += wordWidth; 1516 } else { 1517 addChild(ia); 1518 } 1519 if (ls != null) { 1520 Rectangle lr = new Rectangle (startw, spacew, 1521 ia.getContentWidth(), 1522 fontState.getFontSize()); 1523 ls.addRect(lr, this, ia); 1524 } 1525 startw += wordWidth; 1526 } 1527 } 1528 } 1529 1530 public String getLineText() { 1531 StringBuffer b = new StringBuffer (120); 1532 for (int i=0;i<children.size();i++) { 1533 Object o = children.get(i); 1534 if (o instanceof WordArea) { 1535 b.append(((WordArea)o).getText()); 1536 } else if (o instanceof InlineSpace) { 1537 b.append(' '); 1538 } else if (o instanceof org.apache.fop.image.ImageArea) { 1539 b.append("<img>"); 1540 } else { 1541 b.append('<'); 1542 b.append(o.getClass().getName()); 1543 b.append('>'); 1544 } 1545 } 1546 return b.toString(); 1547 } 1548} 1549 1550 | Popular Tags |