1 17 18 19 20 package org.apache.fop.layoutmgr; 21 22 import org.apache.commons.logging.Log; 23 import org.apache.commons.logging.LogFactory; 24 25 import org.apache.fop.fo.FONode; 26 27 42 public abstract class BreakingAlgorithm { 43 44 45 protected static Log log = LogFactory.getLog(BreakingAlgorithm.class); 46 47 48 protected static final int INFINITE_RATIO = 1000; 49 50 private static final int MAX_RECOVERY_ATTEMPTS = 5; 51 52 54 public static final int ALL_BREAKS = 0; 55 56 public static final int NO_FLAGGED_PENALTIES = 1; 57 58 public static final int ONLY_FORCED_BREAKS = 2; 59 60 62 private int flaggedPenalty = 50; 63 64 protected int repeatedFlaggedDemerit = 50; 65 66 protected int incompatibleFitnessDemerit = 50; 67 70 protected int maxFlaggedPenaltiesCount; 71 72 76 private double threshold; 77 78 81 protected KnuthSequence par; 82 83 87 protected int lineWidth = -1; 88 91 private boolean force = false; 92 93 protected boolean considerTooShort = false; 94 95 98 private KnuthNode lastTooLong; 99 102 private KnuthNode lastTooShort; 103 106 private KnuthNode lastDeactivated; 107 108 109 protected int alignment; 110 111 protected int alignmentLast; 112 113 protected boolean bFirst; 114 115 120 protected KnuthNode[] activeLines; 121 122 125 protected int activeNodeCount; 126 127 130 protected int startLine = 0; 131 132 135 protected int endLine = 0; 136 137 140 protected int totalWidth; 141 142 145 protected int totalStretch = 0; 146 147 150 protected int totalShrink = 0; 151 152 protected BestRecords best; 153 154 155 private boolean partOverflowRecoveryActivated = true; 156 private KnuthNode lastRecovered; 157 158 169 public BreakingAlgorithm(int align, int alignLast, 170 boolean first, boolean partOverflowRecovery, 171 int maxFlagCount) { 172 alignment = align; 173 alignmentLast = alignLast; 174 bFirst = first; 175 this.partOverflowRecoveryActivated = partOverflowRecovery; 176 this.best = new BestRecords(); 177 maxFlaggedPenaltiesCount = maxFlagCount; 178 } 179 180 181 184 public class KnuthNode { 185 186 public int position; 187 188 189 public int line; 190 191 192 public int fitness; 193 194 195 public int totalWidth; 196 197 198 public int totalStretch; 199 200 201 public int totalShrink; 202 203 204 public double adjustRatio; 205 206 207 public int availableShrink; 208 209 210 public int availableStretch; 211 212 213 public int difference; 214 215 216 public double totalDemerits; 217 218 219 public KnuthNode previous; 220 221 222 public KnuthNode next; 223 224 228 public int fitRecoveryCounter = 0; 229 230 public KnuthNode(int position, int line, int fitness, 231 int totalWidth, int totalStretch, int totalShrink, 232 double adjustRatio, int availableShrink, int availableStretch, 233 int difference, double totalDemerits, KnuthNode previous) { 234 this.position = position; 235 this.line = line; 236 this.fitness = fitness; 237 this.totalWidth = totalWidth; 238 this.totalStretch = totalStretch; 239 this.totalShrink = totalShrink; 240 this.adjustRatio = adjustRatio; 241 this.availableShrink = availableShrink; 242 this.availableStretch = availableStretch; 243 this.difference = difference; 244 this.totalDemerits = totalDemerits; 245 this.previous = previous; 246 } 247 248 public String toString() { 249 return "<KnuthNode at " + position + " " 250 + totalWidth + "+" + totalStretch + "-" + totalShrink 251 + " line:" + line + " prev:" + (previous != null ? previous.position : -1) 252 + " dem:" + totalDemerits + ">"; 253 } 254 } 255 256 259 protected class BestRecords { 260 private static final double INFINITE_DEMERITS = Double.POSITIVE_INFINITY; 261 263 private double[] bestDemerits = new double[4]; 264 private KnuthNode[] bestNode = new KnuthNode[4]; 265 private double[] bestAdjust = new double[4]; 266 private int[] bestDifference = new int[4]; 267 private int[] bestAvailableShrink = new int[4]; 268 private int[] bestAvailableStretch = new int[4]; 269 270 private int bestIndex = -1; 271 272 public BestRecords() { 273 reset(); 274 } 275 276 286 public void addRecord(double demerits, KnuthNode node, double adjust, 287 int availableShrink, int availableStretch, 288 int difference, int fitness) { 289 if (demerits > bestDemerits[fitness]) { 290 log.error("New demerits value greater than the old one"); 291 } 292 bestDemerits[fitness] = demerits; 293 bestNode[fitness] = node; 294 bestAdjust[fitness] = adjust; 295 bestAvailableShrink[fitness] = availableShrink; 296 bestAvailableStretch[fitness] = availableStretch; 297 bestDifference[fitness] = difference; 298 if (bestIndex == -1 || demerits < bestDemerits[bestIndex]) { 299 bestIndex = fitness; 300 } 301 } 302 303 public boolean hasRecords() { 304 return (bestIndex != -1); 305 } 306 307 312 public boolean notInfiniteDemerits(int fitness) { 313 return (bestDemerits[fitness] != INFINITE_DEMERITS); 314 } 315 316 public double getDemerits(int fitness) { 317 return bestDemerits[fitness]; 318 } 319 320 public KnuthNode getNode(int fitness) { 321 return bestNode[fitness]; 322 } 323 324 public double getAdjust(int fitness) { 325 return bestAdjust[fitness]; 326 } 327 328 public int getAvailableShrink(int fitness) { 329 return bestAvailableShrink[fitness]; 330 } 331 332 public int getAvailableStretch(int fitness) { 333 return bestAvailableStretch[fitness]; 334 } 335 336 public int getDifference(int fitness) { 337 return bestDifference[fitness]; 338 } 339 340 public double getMinDemerits() { 341 if (bestIndex != -1) { 342 return getDemerits(bestIndex); 343 } else { 344 return INFINITE_DEMERITS; 346 } 347 } 348 349 350 public void reset() { 351 for (int i = 0; i < 4; i++) { 352 bestDemerits[i] = INFINITE_DEMERITS; 353 } 355 bestIndex = -1; 356 } 357 } 358 359 363 protected int getMaxRecoveryAttempts() { 364 return MAX_RECOVERY_ATTEMPTS; 365 } 366 367 372 protected boolean isPartOverflowRecoveryActivated() { 373 return this.partOverflowRecoveryActivated; 374 } 375 376 381 public abstract void updateData1(int total, double demerits); 382 383 391 public abstract void updateData2(KnuthNode bestActiveNode, 392 KnuthSequence sequence, 393 int total); 394 395 public void setConstantLineWidth(int lineWidth) { 396 this.lineWidth = lineWidth; 397 } 398 399 400 public int findBreakingPoints(KnuthSequence par, 401 double threshold, boolean force, 402 int allowedBreaks) { 403 return findBreakingPoints(par, 0, threshold, force, allowedBreaks); 404 } 405 406 414 public int findBreakingPoints(KnuthSequence par, int startIndex, 415 416 double threshold, boolean force, 417 int allowedBreaks) { 418 this.par = par; 419 this.threshold = threshold; 420 this.force = force; 421 initialize(); 423 424 activeLines = new KnuthNode[20]; 425 426 lastTooShort = lastTooLong = null; 429 startLine = endLine = 0; 431 KnuthElement thisElement = null; 433 boolean previousIsBox = false; 435 436 int firstBoxIndex = startIndex; 438 if (alignment != org.apache.fop.fo.Constants.EN_CENTER) { 439 while (par.size() > firstBoxIndex 440 && !((KnuthElement) par.get(firstBoxIndex)).isBox()) { 441 firstBoxIndex++; 442 } 443 } 444 445 activeLines = new KnuthNode[20]; 447 addNode(0, createNode(firstBoxIndex, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, null)); 448 449 if (log.isTraceEnabled()) { 450 log.trace("Looping over " + (par.size() - startIndex) + " elements"); 451 } 452 453 KnuthNode lastForced = getNode(0); 454 455 for (int i = startIndex; i < par.size(); i++) { 457 thisElement = getElement(i); 458 if (thisElement.isBox()) { 459 totalWidth += thisElement.getW(); 461 previousIsBox = true; 462 handleBox((KnuthBox) thisElement); 463 } else if (thisElement.isGlue()) { 464 if (previousIsBox 468 && !(allowedBreaks == ONLY_FORCED_BREAKS)) { 469 considerLegalBreak(thisElement, i); 470 } 471 totalWidth += thisElement.getW(); 472 totalStretch += thisElement.getY(); 473 totalShrink += thisElement.getZ(); 474 previousIsBox = false; 475 } else { 476 if (((KnuthPenalty) thisElement).getP() < KnuthElement.INFINITE 481 && (!(allowedBreaks == NO_FLAGGED_PENALTIES) 482 || !(((KnuthPenalty) thisElement).isFlagged())) 483 && (!(allowedBreaks == ONLY_FORCED_BREAKS) 484 || ((KnuthPenalty) thisElement).getP() == -KnuthElement.INFINITE)) { 485 considerLegalBreak(thisElement, i); 486 } 487 previousIsBox = false; 488 } 489 if (activeNodeCount == 0) { 490 if (!force) { 491 log.debug("Could not find a set of breaking points " + threshold); 492 return 0; 493 } 494 if (lastTooShort == null || lastForced.position == lastTooShort.position) { 495 if (isPartOverflowRecoveryActivated()) { 496 if (this.lastRecovered == null) { 497 this.lastRecovered = lastTooLong; 498 if (log.isDebugEnabled()) { 499 log.debug("Recovery point: " + lastRecovered); 500 } 501 } 502 KnuthNode node = createNode( 504 lastTooLong.previous.position, lastTooLong.previous.line + 1, 1, 505 0, 0, 0, 506 0, 0, 0, 507 0, 0, lastTooLong.previous); 508 lastForced = node; 509 node.fitRecoveryCounter = lastTooLong.previous.fitRecoveryCounter + 1; 510 if (log.isDebugEnabled()) { 511 log.debug("first part doesn't fit into line, recovering: " 512 + node.fitRecoveryCounter); 513 } 514 if (node.fitRecoveryCounter > getMaxRecoveryAttempts()) { 515 while (lastForced.fitRecoveryCounter > 0) { 516 lastForced = lastForced.previous; 517 lastDeactivated = lastForced.previous; 518 startLine--; 519 endLine--; 520 } 521 lastForced = this.lastRecovered; 522 this.lastRecovered = null; 523 startLine = lastForced.line; 524 endLine = lastForced.line; 525 log.debug("rolled back..."); 526 } 527 } else { 528 lastForced = lastTooLong; 529 } 530 } else { 531 lastForced = lastTooShort; 532 this.lastRecovered = null; 533 } 534 535 if (log.isDebugEnabled()) { 536 log.debug("Restarting at node " + lastForced); 537 } 538 i = restartFrom(lastForced, i); 539 } 540 } 541 finish(); 542 if (log.isTraceEnabled()) { 543 log.trace("Main loop completed " + activeNodeCount); 544 log.trace("Active nodes=" + toString("")); 545 } 546 547 int line = filterActiveNodes(); 550 551 for (int i = startLine; i < endLine; i++) { 553 for (KnuthNode node = getNode(i); node != null; node = node.next) { 554 updateData1(node.line, node.totalDemerits); 555 calculateBreakPoints(node, par, node.line); 556 } 557 } 558 559 activeLines = null; 560 return line; 561 } 562 563 569 private FONode findContextFO(KnuthSequence seq, int position) { 570 ListElement el = seq.getElement(position); 571 while (el.getLayoutManager() == null && position < seq.size() - 1) { 572 position++; 573 el = seq.getElement(position); 574 } 575 Position pos = (el != null ? el.getPosition() : null); 576 LayoutManager lm = (pos != null ? pos.getLM() : null); 577 while (pos instanceof NonLeafPosition) { 578 pos = ((NonLeafPosition)pos).getPosition(); 579 if (pos != null && pos.getLM() != null) { 580 lm = pos.getLM(); 581 } 582 } 583 if (lm != null) { 584 return lm.getFObj(); 585 } else { 586 return null; 587 } 588 } 589 590 591 protected void initialize() { 592 this.totalWidth = 0; 593 this.totalStretch = 0; 594 this.totalShrink = 0; 595 } 596 597 614 protected KnuthNode createNode(int position, int line, int fitness, 615 int totalWidth, int totalStretch, int totalShrink, 616 double adjustRatio, int availableShrink, int availableStretch, 617 int difference, double totalDemerits, KnuthNode previous) { 618 return new KnuthNode(position, line, fitness, 619 totalWidth, totalStretch, totalShrink, 620 adjustRatio, availableShrink, availableStretch, 621 difference, totalDemerits, previous); 622 } 623 624 629 protected KnuthNode createNode(int position, int line, int fitness, 630 int totalWidth, int totalStretch, int totalShrink) { 631 return new KnuthNode(position, line, fitness, 632 totalWidth, totalStretch, totalShrink, best.getAdjust(fitness), 633 best.getAvailableShrink(fitness), best.getAvailableStretch(fitness), 634 best.getDifference(fitness), best.getDemerits(fitness), 635 best.getNode(fitness)); 636 } 637 638 639 protected void handleBox(KnuthBox box) { 640 } 641 642 protected int restartFrom(KnuthNode restartingNode, int currentIndex) { 643 restartingNode.totalDemerits = 0; 644 addNode(restartingNode.line, restartingNode); 645 startLine = restartingNode.line; 646 endLine = startLine + 1; 647 totalWidth = restartingNode.totalWidth; 648 totalStretch = restartingNode.totalStretch; 649 totalShrink = restartingNode.totalShrink; 650 lastTooShort = null; 651 lastTooLong = null; 652 int restartingIndex = restartingNode.position; 657 while (restartingIndex + 1 < par.size() 658 && !(getElement(restartingIndex + 1).isBox())) { 659 restartingIndex++; 660 } 661 return restartingIndex; 662 } 663 664 669 protected void considerLegalBreak(KnuthElement element, int elementIdx) { 670 671 if (log.isTraceEnabled()) { 672 log.trace("considerLegalBreak() at " + elementIdx 673 + " (" + totalWidth + "+" + totalStretch + "-" + totalShrink 674 + "), parts/lines: " + startLine + "-" + endLine); 675 log.trace("\tCurrent active node list: " + activeNodeCount + " " + this.toString("\t")); 676 } 677 678 lastDeactivated = null; 679 lastTooLong = null; 680 for (int line = startLine; line < endLine; line++) { 681 for (KnuthNode node = getNode(line); node != null; node = node.next) { 682 if (node.position == elementIdx) { 683 continue; 684 } 685 int difference = computeDifference(node, element, elementIdx); 686 double r = computeAdjustmentRatio(node, difference); 687 int availableShrink = totalShrink - node.totalShrink; 688 int availableStretch = totalStretch - node.totalStretch; 689 if (log.isTraceEnabled()) { 690 log.trace("\tr=" + r + " difference=" + difference); 691 log.trace("\tline=" + line); 692 } 693 694 if (r < -1 || element.isForcedBreak()) { 696 if (log.isTraceEnabled()) { 698 log.trace("Removing " + node); 699 } 700 removeNode(line, node); 701 lastDeactivated = compareNodes(lastDeactivated, node); 702 } 703 704 if (r >= -1 && r <= threshold) { 706 int fitnessClass = computeFitness(r); 707 double demerits = computeDemerits(node, element, fitnessClass, r); 708 709 if (log.isTraceEnabled()) { 710 log.trace("\tDemerits=" + demerits); 711 log.trace("\tFitness class=" + fitnessClass); 712 } 713 714 if (demerits < best.getDemerits(fitnessClass)) { 715 best.addRecord(demerits, node, r, availableShrink, availableStretch, 717 difference, fitnessClass); 718 lastTooShort = null; 719 } 720 } 721 722 if (force && (r <= -1 || r > threshold)) { 725 int fitnessClass = computeFitness(r); 726 double demerits = computeDemerits(node, element, fitnessClass, r); 727 int newWidth = totalWidth; 728 int newStretch = totalStretch; 729 int newShrink = totalShrink; 730 731 for (int i = elementIdx; i < par.size(); i++) { 738 KnuthElement tempElement = getElement(i); 739 if (tempElement.isBox()) { 740 break; 741 } else if (tempElement.isGlue()) { 742 newWidth += tempElement.getW(); 743 newStretch += tempElement.getY(); 744 newShrink += tempElement.getZ(); 745 } else if (tempElement.isForcedBreak() && i != elementIdx) { 746 break; 747 } 748 } 749 750 if (r <= -1) { 751 if (lastTooLong == null || demerits < lastTooLong.totalDemerits) { 752 lastTooLong = createNode(elementIdx, line + 1, fitnessClass, 753 newWidth, newStretch, newShrink, 754 r, availableShrink, availableStretch, 755 difference, demerits, node); 756 if (log.isTraceEnabled()) { 757 log.trace("Picking tooLong " + lastTooLong); 758 } 759 } 760 } else { 761 if (lastTooShort == null || demerits <= lastTooShort.totalDemerits) { 762 if (considerTooShort) { 763 best.addRecord(demerits, node, r, 765 availableShrink, availableStretch, 766 difference, fitnessClass); 767 } 768 lastTooShort = createNode(elementIdx, line + 1, fitnessClass, 769 newWidth, newStretch, newShrink, 770 r, availableShrink, availableStretch, 771 difference, demerits, node); 772 if (log.isTraceEnabled()) { 773 log.trace("Picking tooShort " + lastTooShort); 774 } 775 } 776 } 777 } 778 } 779 addBreaks(line, elementIdx); 780 } 781 } 782 783 788 private void addBreaks(int line, int elementIdx) { 789 if (!best.hasRecords()) { 790 return; 791 } 792 793 int newWidth = totalWidth; 794 int newStretch = totalStretch; 795 int newShrink = totalShrink; 796 797 for (int i = elementIdx; i < par.size(); i++) { 804 KnuthElement tempElement = getElement(i); 805 if (tempElement.isBox()) { 806 break; 807 } else if (tempElement.isGlue()) { 808 newWidth += tempElement.getW(); 809 newStretch += tempElement.getY(); 810 newShrink += tempElement.getZ(); 811 } else if (tempElement.isForcedBreak() && i != elementIdx) { 812 break; 813 } 814 } 815 816 double minimumDemerits = best.getMinDemerits() + incompatibleFitnessDemerit; 818 for (int i = 0; i <= 3; i++) { 819 if (best.notInfiniteDemerits(i) && best.getDemerits(i) <= minimumDemerits) { 820 if (log.isTraceEnabled()) { 823 log.trace("\tInsert new break in list of " + activeNodeCount 824 + " from fitness class " + i); 825 } 826 KnuthNode newNode = createNode(elementIdx, line + 1, i, 827 newWidth, newStretch, newShrink); 828 addNode(line + 1, newNode); 829 } 830 } 831 best.reset(); 832 } 833 834 843 protected int computeDifference(KnuthNode activeNode, KnuthElement element, 844 int elementIndex) { 845 int actualWidth = totalWidth - activeNode.totalWidth; 847 if (element.isPenalty()) { 848 actualWidth += element.getW(); 849 } 850 return getLineWidth() - actualWidth; 851 } 852 853 867 protected double computeAdjustmentRatio(KnuthNode activeNode, int difference) { 868 if (difference > 0) { 870 int maxAdjustment = totalStretch - activeNode.totalStretch; 871 if (maxAdjustment > 0) { 872 return (double) difference / maxAdjustment; 873 } else { 874 return INFINITE_RATIO; 875 } 876 } else if (difference < 0) { 877 int maxAdjustment = totalShrink - activeNode.totalShrink; 878 if (maxAdjustment > 0) { 879 return (double) difference / maxAdjustment; 880 } else { 881 return -INFINITE_RATIO; 882 } 883 } else { 884 return 0; 885 } 886 } 887 888 896 private int computeFitness(double r) { 897 if (r < -0.5) { 898 return 0; 899 } else if (r <= 0.5) { 900 return 1; 901 } else if (r <= 1) { 902 return 2; 903 } else { 904 return 3; 905 } 906 } 907 908 919 protected double computeDemerits(KnuthNode activeNode, KnuthElement element, 920 int fitnessClass, double r) { 921 double demerits = 0; 922 double f = Math.abs(r); 924 f = 1 + 100 * f * f * f; 925 if (element.isPenalty() && element.getP() >= 0) { 926 f += element.getP(); 927 demerits = f * f; 928 } else if (element.isPenalty() && !element.isForcedBreak()) { 929 double penalty = element.getP(); 930 demerits = f * f - penalty * penalty; 931 } else { 932 demerits = f * f; 933 } 934 935 if (element.isPenalty() && ((KnuthPenalty) element).isFlagged() 936 && getElement(activeNode.position).isPenalty() 937 && ((KnuthPenalty) getElement(activeNode.position)).isFlagged()) { 938 demerits += repeatedFlaggedDemerit; 940 int flaggedPenaltiesCount = 2; 944 for (KnuthNode prevNode = activeNode.previous; 945 prevNode != null && flaggedPenaltiesCount <= maxFlaggedPenaltiesCount; 946 prevNode = prevNode.previous) { 947 KnuthElement prevElement = getElement(prevNode.position); 948 if (prevElement.isPenalty() 949 && ((KnuthPenalty) prevElement).isFlagged()) { 950 flaggedPenaltiesCount++; 952 } else { 953 break; 956 } 957 } 958 if (maxFlaggedPenaltiesCount >= 1 959 && flaggedPenaltiesCount > maxFlaggedPenaltiesCount) { 960 demerits += BestRecords.INFINITE_DEMERITS; 963 } 964 } 965 if (Math.abs(fitnessClass - activeNode.fitness) > 1) { 966 demerits += incompatibleFitnessDemerit; 969 } 970 demerits += activeNode.totalDemerits; 971 return demerits; 972 } 973 974 protected void finish() { 975 } 976 977 982 protected KnuthElement getElement(int idx) { 983 return (KnuthElement) par.get(idx); 984 } 985 986 992 protected KnuthNode compareNodes(KnuthNode node1, KnuthNode node2) { 993 if (node1 == null || node2.position > node1.position) { 994 return node2; 995 } 996 if (node2.position == node1.position) { 997 if (node2.totalDemerits < node1.totalDemerits) { 998 return node2; 999 } 1000 } 1001 return node1; 1002 } 1003 1004 1010 protected void addNode(int line, KnuthNode node) { 1011 int headIdx = line * 2; 1012 if (headIdx >= activeLines.length) { 1013 KnuthNode[] oldList = activeLines; 1014 activeLines = new KnuthNode[headIdx + headIdx]; 1015 System.arraycopy(oldList, 0, activeLines, 0, oldList.length); 1016 } 1017 node.next = null; 1018 if (activeLines[headIdx + 1] != null) { 1019 activeLines[headIdx + 1].next = node; 1020 } else { 1021 activeLines[headIdx] = node; 1022 endLine = line + 1; 1023 } 1024 activeLines[headIdx + 1] = node; 1025 activeNodeCount++; 1026 } 1027 1028 1034 protected void removeNode(int line, KnuthNode node) { 1035 int headIdx = line * 2; 1036 KnuthNode n = getNode(line); 1037 if (n != node) { 1038 KnuthNode prevNode = null; 1040 while (n != node) { 1041 prevNode = n; 1042 n = n.next; 1043 } 1044 prevNode.next = n.next; 1045 if (prevNode.next == null) { 1046 activeLines[headIdx + 1] = prevNode; 1047 } 1048 } else { 1049 activeLines[headIdx] = node.next; 1050 if (node.next == null) { 1051 activeLines[headIdx + 1] = null; 1052 } 1053 while (startLine < endLine && getNode(startLine) == null) { 1054 startLine++; 1055 } 1056 } 1057 activeNodeCount--; 1058 } 1059 1060 1065 protected KnuthNode getNode(int line) { 1066 return activeLines[line * 2]; 1067 } 1068 1069 1074 protected int getLineWidth(int line) { 1075 if (this.lineWidth < 0) { 1076 throw new IllegalStateException ("lineWidth must be set" 1077 + (this.lineWidth != 0 ? " and positive, but it is: " + this.lineWidth : "")); 1078 } else { 1079 return this.lineWidth; 1080 } 1081 } 1082 1083 1084 protected int getLineWidth() { 1085 return this.lineWidth; 1086 } 1087 1088 1093 public String toString(String prepend) { 1094 StringBuffer sb = new StringBuffer (); 1095 sb.append("[\n"); 1096 for (int i = startLine; i < endLine; i++) { 1097 for (KnuthNode node = getNode(i); node != null; node = node.next) { 1098 sb.append(prepend + "\t" + node + ",\n"); 1099 } 1100 } 1101 sb.append(prepend + "]"); 1102 return sb.toString(); 1103 } 1104 1105 protected abstract int filterActiveNodes(); 1106 1107 1113 private void calculateBreakPoints(KnuthNode node, KnuthSequence par, 1114 int total) { 1115 KnuthNode bestActiveNode = node; 1116 for (int i = node.line; i > 0; i--) { 1118 updateData2(bestActiveNode, par, total); 1119 bestActiveNode = bestActiveNode.previous; 1120 } 1121 } 1122 1123 1124 public int getAlignment() { 1125 return this.alignment; 1126 } 1127 1128 1129 public int getAlignmentLast() { 1130 return this.alignmentLast; 1131 } 1132 1133} 1134 | Popular Tags |