1 17 18 19 20 package org.apache.fop.layoutmgr.inline; 21 22 import java.util.ArrayList ; 23 import java.util.List ; 24 import java.util.LinkedList ; 25 import java.util.ListIterator ; 26 27 import org.apache.commons.logging.Log; 28 import org.apache.commons.logging.LogFactory; 29 import org.apache.fop.area.Trait; 30 import org.apache.fop.area.inline.TextArea; 31 import org.apache.fop.fo.Constants; 32 import org.apache.fop.fo.FOText; 33 import org.apache.fop.fo.flow.Inline; 34 import org.apache.fop.fonts.Font; 35 import org.apache.fop.layoutmgr.InlineKnuthSequence; 36 import org.apache.fop.layoutmgr.KnuthBox; 37 import org.apache.fop.layoutmgr.KnuthElement; 38 import org.apache.fop.layoutmgr.KnuthGlue; 39 import org.apache.fop.layoutmgr.KnuthPenalty; 40 import org.apache.fop.layoutmgr.KnuthSequence; 41 import org.apache.fop.layoutmgr.LayoutContext; 42 import org.apache.fop.layoutmgr.LeafPosition; 43 import org.apache.fop.layoutmgr.Position; 44 import org.apache.fop.layoutmgr.PositionIterator; 45 import org.apache.fop.layoutmgr.TraitSetter; 46 import org.apache.fop.traits.MinOptMax; 47 import org.apache.fop.traits.SpaceVal; 48 import org.apache.fop.util.CharUtilities; 49 50 54 public class TextLayoutManager extends LeafNodeLayoutManager { 55 56 62 private class AreaInfo { 63 private short iStartIndex; 64 private short iBreakIndex; 65 private short iWScount; 66 private short iLScount; 67 private MinOptMax ipdArea; 68 private boolean bHyphenated; 69 private boolean isSpace; 70 public AreaInfo(short iSIndex, short iBIndex, short iWS, short iLS, 71 MinOptMax ipd, boolean bHyph, boolean isSpace) { 72 iStartIndex = iSIndex; 73 iBreakIndex = iBIndex; 74 iWScount = iWS; 75 iLScount = iLS; 76 ipdArea = ipd; 77 bHyphenated = bHyph; 78 this.isSpace = isSpace; 79 } 80 81 public String toString() { 82 return "[ lscnt=" + iLScount 83 + ", wscnt=" + iWScount 84 + ", ipd=" + ipdArea.toString() 85 + ", sidx=" + iStartIndex 86 + ", bidx=" + iBreakIndex 87 + ", hyph=" + bHyphenated 88 + ", space=" + isSpace 89 + "]"; 90 } 91 92 } 93 94 private class PendingChange { 97 public AreaInfo ai; 98 public int index; 99 100 public PendingChange(AreaInfo ai, int index) { 101 this.ai = ai; 102 this.index = index; 103 } 104 } 105 106 109 private static Log log = LogFactory.getLog(TextLayoutManager.class); 110 111 private ArrayList vecAreaInfo; 113 114 115 private static final String BREAK_CHARS = "-/"; 116 117 118 private static final MinOptMax ZERO_MINOPTMAX = new MinOptMax(0); 119 120 private FOText foText; 121 private char[] textArray; 122 127 private MinOptMax[] letterAdjustArray; 129 private static final char NEWLINE = '\n'; 130 131 private Font font = null; 132 133 private short iAreaStart = 0; 134 135 private short iNextStart = 0; 136 137 private MinOptMax ipdTotal; 138 139 141 private int spaceCharIPD; 142 private MinOptMax wordSpaceIPD; 143 private MinOptMax letterSpaceIPD; 144 145 private int hyphIPD; 146 147 private SpaceVal ws; 148 149 private SpaceVal halfWS; 150 151 private SpaceVal halfLS; 152 153 private int iNbSpacesPending; 154 155 private boolean bChanged = false; 156 private int iReturnedIndex = 0; 157 private short iThisStart = 0; 158 private short iTempStart = 0; 159 private LinkedList changeList = null; 160 161 private AlignmentContext alignmentContext = null; 162 163 private int lineStartBAP = 0; 164 private int lineEndBAP = 0; 165 166 171 public TextLayoutManager(FOText node) { 172 super(); 173 foText = node; 174 175 textArray = new char[node.endIndex - node.startIndex]; 176 System.arraycopy(node.ca, node.startIndex, textArray, 0, 177 node.endIndex - node.startIndex); 178 letterAdjustArray = new MinOptMax[textArray.length + 1]; 179 180 vecAreaInfo = new java.util.ArrayList (); 181 } 182 183 184 public void initialize() { 185 font = foText.getCommonFont().getFontState(foText.getFOEventHandler().getFontInfo(), this); 186 187 spaceCharIPD = font.getCharWidth(' '); 189 hyphIPD = font.getCharWidth(foText.getCommonHyphenation().hyphenationCharacter); 191 192 SpaceVal ls = SpaceVal.makeLetterSpacing(foText.getLetterSpacing()); 193 halfLS = new SpaceVal(MinOptMax.multiply(ls.getSpace(), 0.5), 194 ls.isConditional(), ls.isForcing(), ls.getPrecedence()); 195 196 ws = SpaceVal.makeWordSpacing(foText.getWordSpacing(), ls, font); 197 halfWS = new SpaceVal(MinOptMax.multiply(ws.getSpace(), 0.5), 199 ws.isConditional(), ws.isForcing(), ws.getPrecedence()); 200 201 209 letterSpaceIPD = ls.getSpace(); 213 wordSpaceIPD = MinOptMax.add(new MinOptMax(spaceCharIPD), ws.getSpace()); 214 215 if (foText.getParent() instanceof Inline) { 217 Inline fobj = (Inline)foText.getParent(); 218 } 219 } 220 221 226 public void resetPosition(Position prevPos) { 227 if (prevPos != null) { 228 if (prevPos.getLM() != this) { 230 log.error("TextLayoutManager.resetPosition: " 231 + "LM mismatch!!!"); 232 } 233 LeafPosition tbp = (LeafPosition) prevPos; 234 AreaInfo ai = (AreaInfo) vecAreaInfo.get(tbp.getLeafPos()); 235 if (ai.iBreakIndex != iNextStart) { 236 iNextStart = ai.iBreakIndex; 237 vecAreaInfo.ensureCapacity(tbp.getLeafPos() + 1); 238 ipdTotal = ai.ipdArea; 241 setFinished(false); 242 } 243 } else { 244 vecAreaInfo.clear(); 246 iNextStart = 0; 247 setFinished(false); 248 } 249 } 250 251 private boolean getHyphenIPD(HyphContext hc, MinOptMax hyphIPD) { 254 boolean bCanHyphenate = true; 256 int iStopIndex = iNextStart + hc.getNextHyphPoint(); 257 258 if (textArray.length < iStopIndex) { 259 iStopIndex = textArray.length; 260 bCanHyphenate = false; 261 } 262 hc.updateOffset(iStopIndex - iNextStart); 263 264 for (; iNextStart < iStopIndex; iNextStart++) { 265 char c = textArray[iNextStart]; 266 hyphIPD.opt += font.getCharWidth(c); 267 } 269 return bCanHyphenate; 273 } 274 275 285 public void addAreas(PositionIterator posIter, LayoutContext context) { 286 287 AreaInfo ai = null; 289 int iWScount = 0; 290 int iLScount = 0; 291 int firstAreaInfoIndex = -1; 292 int lastAreaInfoIndex = 0; 293 MinOptMax realWidth = new MinOptMax(0); 294 295 298 while (posIter.hasNext()) { 299 LeafPosition tbpNext = (LeafPosition) posIter.next(); 300 if (tbpNext == null) { 301 continue; } 303 if (tbpNext.getLeafPos() != -1) { 304 ai = (AreaInfo) vecAreaInfo.get(tbpNext.getLeafPos()); 305 if (firstAreaInfoIndex == -1) { 306 firstAreaInfoIndex = tbpNext.getLeafPos(); 307 } 308 iWScount += ai.iWScount; 309 iLScount += ai.iLScount; 310 realWidth.add(ai.ipdArea); 311 lastAreaInfoIndex = tbpNext.getLeafPos(); 312 } 313 } 314 if (ai == null) { 315 return; 316 } 317 int textLength = ai.iBreakIndex - ai.iStartIndex; 318 if (ai.iLScount == textLength 319 && context.isLastArea()) { 320 realWidth.add(MinOptMax.multiply(letterSpaceIPD, -1)); 323 iLScount--; 324 } 325 326 for (int i = ai.iStartIndex; i < ai.iBreakIndex; i++) { 327 MinOptMax ladj = letterAdjustArray[i + 1]; 328 if (ladj != null && ladj.isElastic()) { 329 iLScount++; 330 } 331 } 332 333 if (context.isLastArea() && ai.bHyphenated) { 335 realWidth.add(new MinOptMax(hyphIPD)); 336 } 337 338 int iDifference = 0; 340 int iTotalAdjust = 0; 341 int iWordSpaceDim = wordSpaceIPD.opt; 342 int iLetterSpaceDim = letterSpaceIPD.opt; 343 double dIPDAdjust = context.getIPDAdjust(); 344 double dSpaceAdjust = context.getSpaceAdjust(); 346 if (dIPDAdjust > 0.0) { 348 iDifference = (int) ((double) (realWidth.max - realWidth.opt) 349 * dIPDAdjust); 350 } else { 351 iDifference = (int) ((double) (realWidth.opt - realWidth.min) 352 * dIPDAdjust); 353 } 354 355 if (dIPDAdjust > 0.0) { 357 iLetterSpaceDim 358 += (int) ((double) (letterSpaceIPD.max - letterSpaceIPD.opt) 359 * dIPDAdjust); 360 } else { 361 iLetterSpaceDim 362 += (int) ((double) (letterSpaceIPD.opt - letterSpaceIPD.min) 363 * dIPDAdjust); 364 } 365 iTotalAdjust += (iLetterSpaceDim - letterSpaceIPD.opt) * iLScount; 366 367 if (iWScount > 0) { 370 iWordSpaceDim += (int) ((iDifference - iTotalAdjust) / iWScount); 371 } else { 372 } 374 iTotalAdjust += (iWordSpaceDim - wordSpaceIPD.opt) * iWScount; 375 if (iTotalAdjust != iDifference) { 376 log.trace("TextLM.addAreas: error in word / letter space adjustment = " 378 + (iTotalAdjust - iDifference)); 379 iTotalAdjust = iDifference; 383 } 384 385 TextArea t = createTextArea(realWidth, iTotalAdjust, context, 386 wordSpaceIPD.opt - spaceCharIPD, 387 firstAreaInfoIndex, lastAreaInfoIndex, 388 context.isLastArea()); 389 390 t.setTextLetterSpaceAdjust(iLetterSpaceDim); 403 t.setTextWordSpaceAdjust(iWordSpaceDim - spaceCharIPD 404 - 2 * t.getTextLetterSpaceAdjust()); 405 if (context.getIPDAdjust() != 0) { 406 t.setSpaceDifference(wordSpaceIPD.opt - spaceCharIPD 408 - 2 * t.getTextLetterSpaceAdjust()); 409 } 410 parentLM.addChildArea(t); 411 } 412 413 426 protected TextArea createTextArea(MinOptMax width, int adjust, 427 LayoutContext context, int spaceDiff, 428 int firstIndex, int lastIndex, boolean isLastArea) { 429 TextArea textArea; 430 if (context.getIPDAdjust() == 0.0) { 431 textArea = new TextArea(); 433 } else { 434 textArea = new TextArea(width.max - width.opt, 437 width.opt - width.min, 438 adjust); 439 } 440 textArea.setIPD(width.opt + adjust); 441 textArea.setBPD(font.getAscender() - font.getDescender()); 442 textArea.setBaselineOffset(font.getAscender()); 443 if (textArea.getBPD() == alignmentContext.getHeight()) { 444 textArea.setOffset(0); 445 } else { 446 textArea.setOffset(alignmentContext.getOffset()); 447 } 448 449 int wordStartIndex = -1; 451 AreaInfo areaInfo; 452 for (int i = firstIndex; i <= lastIndex; i++) { 453 areaInfo = (AreaInfo) vecAreaInfo.get(i); 454 if (areaInfo.isSpace) { 455 for (int j = areaInfo.iStartIndex; j < areaInfo.iBreakIndex; j++) { 458 char spaceChar = textArray[j]; 459 textArea.addSpace(spaceChar, 0, 460 CharUtilities.isAdjustableSpace(spaceChar)); 461 } 462 } else { 463 if (wordStartIndex == -1) { 465 wordStartIndex = areaInfo.iStartIndex; 467 } 468 if (i == lastIndex || ((AreaInfo) vecAreaInfo.get(i + 1)).isSpace) { 469 int len = areaInfo.iBreakIndex - wordStartIndex; 472 String wordChars = new String (textArray, wordStartIndex, len); 473 if (isLastArea 474 && i == lastIndex 475 && areaInfo.bHyphenated) { 476 wordChars += foText.getCommonHyphenation().hyphenationCharacter; 478 } 479 int[] letterAdjust = new int[wordChars.length()]; 480 int lsCount = areaInfo.iLScount; 481 for (int letter = 0; letter < len; letter++) { 482 MinOptMax adj = letterAdjustArray[letter + wordStartIndex]; 483 if (letter > 0) { 484 letterAdjust[letter] = (adj != null ? adj.opt : 0); 485 } 486 if (lsCount > 0) { 487 letterAdjust[letter] += textArea.getTextLetterSpaceAdjust(); 488 lsCount--; 489 } 490 } 491 textArea.addWord(wordChars, 0, letterAdjust); 492 wordStartIndex = -1; 493 } 494 } 495 } 496 TraitSetter.addFontTraits(textArea, font); 497 textArea.addTrait(Trait.COLOR, foText.getColor()); 498 499 TraitSetter.addTextDecoration(textArea, foText.getTextDecoration()); 500 501 return textArea; 502 } 503 504 private void addToLetterAdjust(int index, int width) { 505 if (letterAdjustArray[index] == null) { 506 letterAdjustArray[index] = new MinOptMax(width); 507 } else { 508 letterAdjustArray[index].add(width); 509 } 510 } 511 512 private void addToLetterAdjust(int index, MinOptMax width) { 513 if (letterAdjustArray[index] == null) { 514 letterAdjustArray[index] = new MinOptMax(width); 515 } else { 516 letterAdjustArray[index].add(width); 517 } 518 } 519 520 525 private static boolean isSpace(final char ch) { 526 return ch == CharUtilities.SPACE 527 || ch == CharUtilities.NBSPACE 528 || CharUtilities.isFixedWidthSpace(ch); 529 } 530 531 private static boolean isBreakChar(final char ch) { 532 return (BREAK_CHARS.indexOf(ch) >= 0); 533 } 534 535 536 public LinkedList getNextKnuthElements(LayoutContext context, int alignment) { 537 lineStartBAP = context.getLineStartBorderAndPaddingWidth(); 538 lineEndBAP = context.getLineEndBorderAndPaddingWidth(); 539 alignmentContext = context.getAlignmentContext(); 540 541 LinkedList returnList = new LinkedList (); 542 KnuthSequence sequence = new InlineKnuthSequence(); 543 AreaInfo ai = null; 544 returnList.add(sequence); 545 546 while (iNextStart < textArray.length) { 547 char ch = textArray[iNextStart]; 548 if (ch == CharUtilities.SPACE 549 && foText.getWhitespaceTreatment() != Constants.EN_PRESERVE) { 550 iThisStart = iNextStart; 553 iNextStart++; 554 while (iNextStart < textArray.length 555 && textArray[iNextStart] == CharUtilities.SPACE) { 556 iNextStart++; 557 } 558 ai = new AreaInfo(iThisStart, (short) (iNextStart), 560 (short) (iNextStart - iThisStart), (short) 0, 561 MinOptMax.multiply(wordSpaceIPD, iNextStart - iThisStart), 562 false, true); 563 vecAreaInfo.add(ai); 564 565 sequence.addAll 567 (createElementsForASpace(alignment, ai, vecAreaInfo.size() - 1)); 568 569 } else if (ch == CharUtilities.SPACE || ch == CharUtilities.NBSPACE) { 570 ai = new AreaInfo(iNextStart, (short) (iNextStart + 1), 573 (short) 1, (short) 0, 574 wordSpaceIPD, false, true); 575 vecAreaInfo.add(ai); 576 577 sequence.addAll 579 (createElementsForASpace(alignment, ai, vecAreaInfo.size() - 1)); 580 581 iNextStart++; 583 } else if (CharUtilities.isFixedWidthSpace(ch)) { 584 MinOptMax ipd = new MinOptMax(font.getCharWidth(ch)); 586 ai = new AreaInfo(iNextStart, (short) (iNextStart + 1), 587 (short) 0, (short) 0, 588 ipd, false, true); 589 vecAreaInfo.add(ai); 590 591 sequence.addAll 593 (createElementsForASpace(alignment, ai, vecAreaInfo.size() - 1)); 594 595 iNextStart++; 597 } else if (ch == NEWLINE) { 598 if (lineEndBAP != 0) { 601 sequence.add 602 (new KnuthGlue(lineEndBAP, 0, 0, 603 new LeafPosition(this, -1), true)); 604 } 605 sequence.endSequence(); 606 sequence = new InlineKnuthSequence(); 607 returnList.add(sequence); 608 609 iNextStart++; 611 } else { 612 iThisStart = iNextStart; 614 iTempStart = iNextStart; 615 for (; iTempStart < textArray.length 616 && !isSpace(textArray[iTempStart]) 617 && textArray[iTempStart] != NEWLINE 618 && !(iTempStart > iNextStart 619 && isBreakChar(textArray[iTempStart - 1])); 620 iTempStart++) { 621 } 623 624 int wordLength = iTempStart - iThisStart; 626 boolean kerning = font.hasKerning(); 627 MinOptMax wordIPD = new MinOptMax(0); 628 for (int i = iThisStart; i < iTempStart; i++) { 629 char c = textArray[i]; 630 631 int charWidth = font.getCharWidth(c); 633 wordIPD.add(charWidth); 634 635 int kern = 0; 637 if (kerning && (i > iThisStart)) { 638 char previous = textArray[i - 1]; 639 kern = font.getKernValue(previous, c) * font.getFontSize() / 1000; 640 if (kern != 0) { 641 addToLetterAdjust(i, kern); 643 } 644 wordIPD.add(kern); 645 } 646 } 647 648 int iLetterSpaces = wordLength - 1; 649 if (isBreakChar(textArray[iTempStart - 1]) 653 && iTempStart < textArray.length 654 && !isSpace(textArray[iTempStart])) { 655 iLetterSpaces++; 656 } 657 wordIPD.add(MinOptMax.multiply(letterSpaceIPD, iLetterSpaces)); 658 659 ai = new AreaInfo(iThisStart, iTempStart, (short) 0, 661 (short) iLetterSpaces, 662 wordIPD, false, false); 663 vecAreaInfo.add(ai); 664 665 sequence.addAll(createElementsForAWordFragment(alignment, ai, 667 vecAreaInfo.size() - 1, letterSpaceIPD)); 668 669 iNextStart = iTempStart; 671 } 672 } if (((List )returnList.getLast()).size() == 0) { 674 returnList.removeLast(); 676 } 677 setFinished(true); 678 if (returnList.size() > 0) { 679 return returnList; 680 } else { 681 return null; 682 } 683 } 684 685 686 public List addALetterSpaceTo(List oldList) { 687 ListIterator oldListIterator = oldList.listIterator(); 691 KnuthElement el = (KnuthElement)oldListIterator.next(); 692 LeafPosition pos = (LeafPosition) ((KnuthBox) el).getPosition(); 693 AreaInfo ai = (AreaInfo) vecAreaInfo.get(pos.getLeafPos()); 694 ai.iLScount++; 695 ai.ipdArea.add(letterSpaceIPD); 696 if (BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0) { 697 oldListIterator = oldList.listIterator(oldList.size()); 700 oldListIterator.add(new KnuthPenalty(0, KnuthPenalty.FLAGGED_PENALTY, true, 701 new LeafPosition(this, -1), false)); 702 oldListIterator.add(new KnuthGlue(letterSpaceIPD.opt, 703 letterSpaceIPD.max - letterSpaceIPD.opt, 704 letterSpaceIPD.opt - letterSpaceIPD.min, 705 new LeafPosition(this, -1), false)); 706 } else if (letterSpaceIPD.min == letterSpaceIPD.max) { 707 oldListIterator.set(new KnuthInlineBox(ai.ipdArea.opt, alignmentContext, pos, false)); 709 } else { 710 oldListIterator.next(); oldListIterator.next(); oldListIterator.set(new KnuthGlue(ai.iLScount * letterSpaceIPD.opt, 714 ai.iLScount * (letterSpaceIPD.max - letterSpaceIPD.opt), 715 ai.iLScount * (letterSpaceIPD.opt - letterSpaceIPD.min), 716 new LeafPosition(this, -1), true)); 717 } 718 return oldList; 719 } 720 721 728 public void removeWordSpace(List oldList) { 729 ListIterator oldListIterator = oldList.listIterator(); 732 if (((KnuthElement) ((LinkedList ) oldList).getFirst()).isPenalty()) { 733 oldListIterator.next(); 735 } 736 if (oldList.size() > 2) { 737 oldListIterator.next(); 740 oldListIterator.next(); 741 } 742 int leafValue = ((LeafPosition) ((KnuthElement) oldListIterator.next()).getPosition()).getLeafPos(); 743 if (leafValue == vecAreaInfo.size() - 1) { 745 vecAreaInfo.remove(leafValue); 746 } else { 747 log.error("trying to remove a non-trailing word space"); 748 } 749 } 750 751 752 public void hyphenate(Position pos, HyphContext hc) { 753 AreaInfo ai 754 = (AreaInfo) vecAreaInfo.get(((LeafPosition) pos).getLeafPos()); 755 int iStartIndex = ai.iStartIndex; 756 int iStopIndex; 757 boolean bNothingChanged = true; 758 759 while (iStartIndex < ai.iBreakIndex) { 760 MinOptMax newIPD = new MinOptMax(0); 761 boolean bHyphenFollows; 762 763 if (hc.hasMoreHyphPoints() 764 && (iStopIndex = iStartIndex + hc.getNextHyphPoint()) 765 <= ai.iBreakIndex) { 766 bHyphenFollows = true; 769 } else { 770 bHyphenFollows = false; 773 iStopIndex = ai.iBreakIndex; 774 } 775 776 hc.updateOffset(iStopIndex - iStartIndex); 777 778 for (int i = iStartIndex; i < iStopIndex; i++) { 780 char c = textArray[i]; 781 newIPD.add(new MinOptMax(font.getCharWidth(c))); 782 if (i < iStopIndex) { 784 MinOptMax la = this.letterAdjustArray[i + 1]; 785 if ((i == iStopIndex - 1) && bHyphenFollows) { 786 la = null; 789 } 790 if (la != null) { 791 newIPD.add(la); 792 } 793 } 794 } 795 boolean bIsWordEnd 797 = iStopIndex == ai.iBreakIndex 798 && ai.iLScount < (ai.iBreakIndex - ai.iStartIndex); 799 newIPD.add(MinOptMax.multiply(letterSpaceIPD, 800 (bIsWordEnd 801 ? (iStopIndex - iStartIndex - 1) 802 : (iStopIndex - iStartIndex)))); 803 804 if (!(bNothingChanged 805 && iStopIndex == ai.iBreakIndex 806 && bHyphenFollows == false)) { 807 if (changeList == null) { 809 changeList = new LinkedList (); 810 } 811 changeList.add 812 (new PendingChange 813 (new AreaInfo((short) iStartIndex, (short) iStopIndex, 814 (short) 0, 815 (short) (bIsWordEnd 816 ? (iStopIndex - iStartIndex - 1) 817 : (iStopIndex - iStartIndex)), 818 newIPD, bHyphenFollows, false), 819 ((LeafPosition) pos).getLeafPos())); 820 bNothingChanged = false; 821 } 822 iStartIndex = iStopIndex; 823 } 824 if (!bChanged && !bNothingChanged) { 825 bChanged = true; 826 } 827 } 828 829 830 public boolean applyChanges(List oldList) { 831 setFinished(false); 832 833 if (changeList != null) { 834 int iAddedAI = 0; 835 int iRemovedAI = 0; 836 int iOldIndex = -1; 837 PendingChange currChange = null; 838 ListIterator changeListIterator = changeList.listIterator(); 839 while (changeListIterator.hasNext()) { 840 currChange = (PendingChange) changeListIterator.next(); 841 if (currChange.index != iOldIndex) { 842 iRemovedAI++; 843 iAddedAI++; 844 iOldIndex = currChange.index; 845 vecAreaInfo.remove(currChange.index + iAddedAI - iRemovedAI); 846 vecAreaInfo.add(currChange.index + iAddedAI - iRemovedAI, 847 currChange.ai); 848 } else { 849 iAddedAI++; 850 vecAreaInfo.add(currChange.index + iAddedAI - iRemovedAI, 851 currChange.ai); 852 } 853 } 854 changeList.clear(); 855 } 856 857 iReturnedIndex = 0; 858 return bChanged; 859 } 860 861 862 public LinkedList getChangedKnuthElements(List oldList, 863 int alignment) { 864 if (isFinished()) { 865 return null; 866 } 867 868 LinkedList returnList = new LinkedList (); 869 870 while (iReturnedIndex < vecAreaInfo.size()) { 871 AreaInfo ai = (AreaInfo) vecAreaInfo.get(iReturnedIndex); 872 if (ai.iWScount == 0) { 873 returnList.addAll 875 (createElementsForAWordFragment(alignment, ai, iReturnedIndex, letterSpaceIPD)); 876 } else { 877 returnList.addAll 879 (createElementsForASpace(alignment, ai, iReturnedIndex)); 880 } 881 iReturnedIndex++; 882 } setFinished(true); 884 return returnList; 886 } 887 888 889 public void getWordChars(StringBuffer sbChars, Position pos) { 890 int iLeafValue = ((LeafPosition) pos).getLeafPos(); 891 if (iLeafValue != -1) { 892 AreaInfo ai = (AreaInfo) vecAreaInfo.get(iLeafValue); 893 sbChars.append(new String (textArray, ai.iStartIndex, 894 ai.iBreakIndex - ai.iStartIndex)); 895 } 896 } 897 898 private LinkedList createElementsForASpace(int alignment, 899 AreaInfo ai, int leafValue) { 900 LinkedList spaceElements = new LinkedList (); 901 LeafPosition mainPosition = new LeafPosition(this, leafValue); 902 903 if (textArray[ai.iStartIndex] == CharUtilities.NBSPACE) { 904 if (alignment == EN_JUSTIFY) { 907 spaceElements.add(new KnuthInlineBox(0, null, 910 notifyPos(new LeafPosition(this, -1)), true)); 911 spaceElements.add(new KnuthPenalty(0, KnuthElement.INFINITE, 912 false, new LeafPosition(this, -1), false)); 913 spaceElements.add(new KnuthGlue(ai.ipdArea.opt, ai.ipdArea.max - ai.ipdArea.opt, 914 ai.ipdArea.opt - ai.ipdArea.min, mainPosition, false)); 915 } else { 916 spaceElements.add(new KnuthInlineBox(ai.ipdArea.opt, null, 919 mainPosition, true)); 920 } 921 } else if (textArray[ai.iStartIndex] == CharUtilities.SPACE 922 && foText.getWhitespaceTreatment() == Constants.EN_PRESERVE) { 923 switch (alignment) { 925 case EN_CENTER: 926 spaceElements.add(new KnuthGlue(lineEndBAP, 931 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, 932 new LeafPosition(this, -1), false)); 933 spaceElements 934 .add(new KnuthPenalty( 935 0, 936 (textArray[ai.iStartIndex] == CharUtilities.NBSPACE 937 ? KnuthElement.INFINITE 938 : 0), false, 939 new LeafPosition(this, -1), false)); 940 spaceElements.add(new KnuthGlue( 941 - (lineStartBAP + lineEndBAP), -6 942 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, 943 new LeafPosition(this, -1), false)); 944 spaceElements.add(new KnuthInlineBox(0, null, 945 notifyPos(new LeafPosition(this, -1)), false)); 946 spaceElements.add(new KnuthPenalty(0, KnuthElement.INFINITE, 947 false, new LeafPosition(this, -1), false)); 948 spaceElements.add(new KnuthGlue(ai.ipdArea.opt + lineStartBAP, 949 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, 950 mainPosition, false)); 951 break; 952 953 case EN_START: case EN_END: 955 spaceElements.add(new KnuthGlue(lineEndBAP, 960 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, 961 new LeafPosition(this, -1), false)); 962 spaceElements.add(new KnuthPenalty(0, 0, false, 963 new LeafPosition(this, -1), false)); 964 spaceElements.add(new KnuthGlue( 965 - (lineStartBAP + lineEndBAP), -3 966 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, 967 new LeafPosition(this, -1), false)); 968 spaceElements.add(new KnuthInlineBox(0, null, 969 notifyPos(new LeafPosition(this, -1)), false)); 970 spaceElements.add(new KnuthPenalty(0, 971 KnuthElement.INFINITE, false, new LeafPosition( 972 this, -1), false)); 973 spaceElements.add(new KnuthGlue(ai.ipdArea.opt + lineStartBAP, 0, 0, 974 mainPosition, false)); 975 break; 976 977 case EN_JUSTIFY: 978 spaceElements.add(new KnuthGlue(lineEndBAP, 0, 0, 981 new LeafPosition(this, -1), false)); 982 spaceElements.add(new KnuthPenalty(0, 0, false, 983 new LeafPosition(this, -1), false)); 984 spaceElements.add(new KnuthGlue( 985 - (lineStartBAP + lineEndBAP), ai.ipdArea.max 986 - ai.ipdArea.opt, ai.ipdArea.opt - ai.ipdArea.min, 987 new LeafPosition(this, -1), false)); 988 spaceElements.add(new KnuthInlineBox(0, null, 989 notifyPos(new LeafPosition(this, -1)), false)); 990 spaceElements.add(new KnuthPenalty(0, 991 KnuthElement.INFINITE, false, new LeafPosition( 992 this, -1), false)); 993 spaceElements.add(new KnuthGlue(lineStartBAP + ai.ipdArea.opt, 0, 0, 994 mainPosition, false)); 995 break; 996 997 default: 998 spaceElements.add(new KnuthGlue(lineEndBAP, 0, 0, 1001 new LeafPosition(this, -1), false)); 1002 spaceElements.add(new KnuthPenalty(0, 0, false, 1003 new LeafPosition(this, -1), false)); 1004 spaceElements.add(new KnuthGlue( 1005 - (lineStartBAP + lineEndBAP), ai.ipdArea.max 1006 - ai.ipdArea.opt, 0, 1007 new LeafPosition(this, -1), false)); 1008 spaceElements.add(new KnuthInlineBox(0, null, 1009 notifyPos(new LeafPosition(this, -1)), false)); 1010 spaceElements.add(new KnuthPenalty(0, 1011 KnuthElement.INFINITE, false, new LeafPosition( 1012 this, -1), false)); 1013 spaceElements.add(new KnuthGlue(lineStartBAP + ai.ipdArea.opt, 0, 0, 1014 mainPosition, false)); 1015 } 1016 } else { 1017 switch (alignment) { 1019 case EN_CENTER: 1020 spaceElements.add(new KnuthGlue(lineEndBAP, 1025 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, 1026 new LeafPosition(this, -1), false)); 1027 spaceElements 1028 .add(new KnuthPenalty( 1029 0, 0, false, 1030 new LeafPosition(this, -1), false)); 1031 spaceElements.add(new KnuthGlue(ai.ipdArea.opt 1032 - (lineStartBAP + lineEndBAP), -6 1033 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, 1034 mainPosition, false)); 1035 spaceElements.add(new KnuthInlineBox(0, null, 1036 notifyPos(new LeafPosition(this, -1)), false)); 1037 spaceElements.add(new KnuthPenalty(0, KnuthElement.INFINITE, 1038 false, new LeafPosition(this, -1), false)); 1039 spaceElements.add(new KnuthGlue(lineStartBAP, 1040 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, 1041 new LeafPosition(this, -1), false)); 1042 break; 1043 1044 case EN_START: case EN_END: 1046 if (lineStartBAP != 0 || lineEndBAP != 0) { 1051 spaceElements.add(new KnuthGlue(lineEndBAP, 1052 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, 1053 new LeafPosition(this, -1), false)); 1054 spaceElements.add(new KnuthPenalty(0, 0, false, 1055 new LeafPosition(this, -1), false)); 1056 spaceElements.add(new KnuthGlue(ai.ipdArea.opt 1057 - (lineStartBAP + lineEndBAP), -3 1058 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, 1059 mainPosition, false)); 1060 spaceElements.add(new KnuthInlineBox(0, null, 1061 notifyPos(new LeafPosition(this, -1)), false)); 1062 spaceElements.add(new KnuthPenalty(0, 1063 KnuthElement.INFINITE, false, new LeafPosition( 1064 this, -1), false)); 1065 spaceElements.add(new KnuthGlue(lineStartBAP, 0, 0, 1066 new LeafPosition(this, -1), false)); 1067 } else { 1068 spaceElements.add(new KnuthGlue(0, 1069 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, 1070 new LeafPosition(this, -1), false)); 1071 spaceElements.add(new KnuthPenalty(0, 0, false, 1072 new LeafPosition(this, -1), false)); 1073 spaceElements.add(new KnuthGlue(ai.ipdArea.opt, -3 1074 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, 1075 mainPosition, false)); 1076 } 1077 break; 1078 1079 case EN_JUSTIFY: 1080 if (lineStartBAP != 0 || lineEndBAP != 0) { 1083 spaceElements.add(new KnuthGlue(lineEndBAP, 0, 0, 1084 new LeafPosition(this, -1), false)); 1085 spaceElements.add(new KnuthPenalty(0, 0, false, 1086 new LeafPosition(this, -1), false)); 1087 spaceElements.add(new KnuthGlue( 1088 ai.ipdArea.opt - (lineStartBAP + lineEndBAP), 1089 ai.ipdArea.max - ai.ipdArea.opt, 1090 ai.ipdArea.opt - ai.ipdArea.min, 1091 mainPosition, false)); 1092 spaceElements.add(new KnuthInlineBox(0, null, 1093 notifyPos(new LeafPosition(this, -1)), false)); 1094 spaceElements.add(new KnuthPenalty(0, 1095 KnuthElement.INFINITE, false, new LeafPosition( 1096 this, -1), false)); 1097 spaceElements.add(new KnuthGlue(lineStartBAP, 0, 0, 1098 new LeafPosition(this, -1), false)); 1099 } else { 1100 spaceElements.add(new KnuthGlue(ai.ipdArea.opt, 1101 ai.ipdArea.max - ai.ipdArea.opt, 1102 ai.ipdArea.opt - ai.ipdArea.min, 1103 mainPosition, false)); 1104 } 1105 break; 1106 1107 default: 1108 if (lineStartBAP != 0 || lineEndBAP != 0) { 1111 spaceElements.add(new KnuthGlue(lineEndBAP, 0, 0, 1112 new LeafPosition(this, -1), false)); 1113 spaceElements.add(new KnuthPenalty(0, 0, false, 1114 new LeafPosition(this, -1), false)); 1115 spaceElements.add(new KnuthGlue( 1116 ai.ipdArea.opt - (lineStartBAP + lineEndBAP), 1117 ai.ipdArea.max - ai.ipdArea.opt, 1118 0, mainPosition, false)); 1119 spaceElements.add(new KnuthInlineBox(0, null, 1120 notifyPos(new LeafPosition(this, -1)), false)); 1121 spaceElements.add(new KnuthPenalty(0, 1122 KnuthElement.INFINITE, false, new LeafPosition( 1123 this, -1), false)); 1124 spaceElements.add(new KnuthGlue(lineStartBAP, 0, 0, 1125 new LeafPosition(this, -1), false)); 1126 } else { 1127 spaceElements.add(new KnuthGlue(ai.ipdArea.opt, 1128 ai.ipdArea.max - ai.ipdArea.opt, 0, 1129 mainPosition, false)); 1130 } 1131 } 1132 } 1133 1134 return spaceElements; 1135 } 1136 1137 private LinkedList createElementsForAWordFragment(int alignment, 1138 AreaInfo ai, int leafValue, MinOptMax letterSpaceWidth) { 1139 LinkedList wordElements = new LinkedList (); 1140 LeafPosition mainPosition = new LeafPosition(this, leafValue); 1141 1142 boolean bSuppressibleLetterSpace 1146 = isBreakChar(textArray[ai.iBreakIndex - 1]); 1148 1149 if (letterSpaceWidth.min == letterSpaceWidth.max) { 1150 wordElements.add 1152 (new KnuthInlineBox( 1153 bSuppressibleLetterSpace 1154 ? ai.ipdArea.opt - letterSpaceWidth.opt 1155 : ai.ipdArea.opt, 1156 alignmentContext, 1157 notifyPos(mainPosition), false)); 1158 } else { 1159 int unsuppressibleLetterSpaces 1161 = bSuppressibleLetterSpace ? ai.iLScount - 1 : ai.iLScount; 1162 wordElements.add 1163 (new KnuthInlineBox(ai.ipdArea.opt 1164 - ai.iLScount * letterSpaceWidth.opt, 1165 alignmentContext, 1166 notifyPos(mainPosition), false)); 1167 wordElements.add 1168 (new KnuthPenalty(0, KnuthElement.INFINITE, false, 1169 new LeafPosition(this, -1), true)); 1170 wordElements.add 1171 (new KnuthGlue(unsuppressibleLetterSpaces * letterSpaceWidth.opt, 1172 unsuppressibleLetterSpaces * (letterSpaceWidth.max - letterSpaceWidth.opt), 1173 unsuppressibleLetterSpaces * (letterSpaceWidth.opt - letterSpaceWidth.min), 1174 new LeafPosition(this, -1), true)); 1175 wordElements.add 1176 (new KnuthInlineBox(0, null, 1177 notifyPos(new LeafPosition(this, -1)), true)); 1178 } 1179 1180 if (ai.bHyphenated) { 1183 MinOptMax widthIfNoBreakOccurs = null; 1184 if (ai.iBreakIndex < textArray.length) { 1185 widthIfNoBreakOccurs = letterAdjustArray[ai.iBreakIndex]; 1187 } 1188 1190 wordElements.addAll(createElementsForAHyphen(alignment, hyphIPD, widthIfNoBreakOccurs)); 1194 } else if (bSuppressibleLetterSpace) { 1195 wordElements.addAll(createElementsForAHyphen(alignment, 0, letterSpaceWidth)); 1199 } 1200 return wordElements; 1201 } 1202 1203 private LinkedList createElementsForAHyphen(int alignment, 1204 int widthIfBreakOccurs, MinOptMax widthIfNoBreakOccurs) { 1205 if (widthIfNoBreakOccurs == null) { 1206 widthIfNoBreakOccurs = ZERO_MINOPTMAX; 1207 } 1208 LinkedList hyphenElements = new LinkedList (); 1209 1210 switch (alignment) { 1211 case EN_CENTER : 1212 1235 hyphenElements.add 1236 (new KnuthGlue(lineEndBAP, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, 1237 new LeafPosition(this, -1), true)); 1238 hyphenElements.add 1239 (new KnuthPenalty(hyphIPD, 1240 KnuthPenalty.FLAGGED_PENALTY, true, 1241 new LeafPosition(this, -1), false)); 1242 hyphenElements.add 1243 (new KnuthGlue(-(lineEndBAP + lineStartBAP), 1244 -6 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, 1245 new LeafPosition(this, -1), false)); 1246 hyphenElements.add 1247 (new KnuthInlineBox(0, null, 1248 notifyPos(new LeafPosition(this, -1)), true)); 1249 hyphenElements.add 1250 (new KnuthPenalty(0, KnuthElement.INFINITE, false, 1251 new LeafPosition(this, -1), true)); 1252 hyphenElements.add 1253 (new KnuthGlue(lineStartBAP, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, 1254 new LeafPosition(this, -1), true)); 1255 break; 1256 1257 case EN_START : case EN_END : 1259 1273 if (lineStartBAP != 0 || lineEndBAP != 0) { 1274 hyphenElements.add 1275 (new KnuthGlue(lineEndBAP, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, 1276 new LeafPosition(this, -1), false)); 1277 hyphenElements.add 1278 (new KnuthPenalty(widthIfBreakOccurs, 1279 KnuthPenalty.FLAGGED_PENALTY, true, 1280 new LeafPosition(this, -1), false)); 1281 hyphenElements.add 1282 (new KnuthGlue(widthIfNoBreakOccurs.opt - (lineStartBAP + lineEndBAP), 1283 -3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, 1284 new LeafPosition(this, -1), false)); 1285 hyphenElements.add 1286 (new KnuthInlineBox(0, null, 1287 notifyPos(new LeafPosition(this, -1)), false)); 1288 hyphenElements.add 1289 (new KnuthPenalty(0, KnuthElement.INFINITE, false, 1290 new LeafPosition(this, -1), false)); 1291 hyphenElements.add 1292 (new KnuthGlue(lineStartBAP, 0, 0, 1293 new LeafPosition(this, -1), false)); 1294 } else { 1295 hyphenElements.add 1296 (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, 1297 new LeafPosition(this, -1), false)); 1298 hyphenElements.add 1299 (new KnuthPenalty(widthIfBreakOccurs, 1300 KnuthPenalty.FLAGGED_PENALTY, true, 1301 new LeafPosition(this, -1), false)); 1302 hyphenElements.add 1303 (new KnuthGlue(widthIfNoBreakOccurs.opt, 1304 -3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0, 1305 new LeafPosition(this, -1), false)); 1306 } 1307 break; 1308 1309 default: 1310 1318 if (lineStartBAP != 0 || lineEndBAP != 0) { 1319 hyphenElements.add 1320 (new KnuthGlue(lineEndBAP, 0, 0, 1321 new LeafPosition(this, -1), false)); 1322 hyphenElements.add 1323 (new KnuthPenalty(widthIfBreakOccurs, 1324 KnuthPenalty.FLAGGED_PENALTY, true, 1325 new LeafPosition(this, -1), false)); 1326 if (widthIfNoBreakOccurs.min != 0 1329 || widthIfNoBreakOccurs.max != 0) { 1330 hyphenElements.add 1331 (new KnuthGlue(widthIfNoBreakOccurs.opt - (lineStartBAP + lineEndBAP), 1332 widthIfNoBreakOccurs.max - widthIfNoBreakOccurs.opt, 1333 widthIfNoBreakOccurs.opt - widthIfNoBreakOccurs.min, 1334 new LeafPosition(this, -1), false)); 1335 } else { 1336 hyphenElements.add 1337 (new KnuthGlue(-(lineStartBAP + lineEndBAP), 0, 0, 1338 new LeafPosition(this, -1), false)); 1339 } 1340 hyphenElements.add 1341 (new KnuthInlineBox(0, null, 1342 notifyPos(new LeafPosition(this, -1)), false)); 1343 hyphenElements.add 1344 (new KnuthPenalty(0, KnuthElement.INFINITE, false, 1345 new LeafPosition(this, -1), false)); 1346 hyphenElements.add 1347 (new KnuthGlue(lineStartBAP, 0, 0, 1348 new LeafPosition(this, -1), false)); 1349 } else { 1350 hyphenElements.add 1351 (new KnuthPenalty(widthIfBreakOccurs, 1352 KnuthPenalty.FLAGGED_PENALTY, true, 1353 new LeafPosition(this, -1), false)); 1354 if (widthIfNoBreakOccurs.min != 0 1357 || widthIfNoBreakOccurs.max != 0) { 1358 hyphenElements.add 1359 (new KnuthGlue(widthIfNoBreakOccurs.opt, 1360 widthIfNoBreakOccurs.max - widthIfNoBreakOccurs.opt, 1361 widthIfNoBreakOccurs.opt - widthIfNoBreakOccurs.min, 1362 new LeafPosition(this, -1), false)); 1363 } 1364 } 1365 } 1366 1367 return hyphenElements; 1368 } 1369} 1370 1371 | Popular Tags |