You are viewing a plain text version of this content. The canonical link for it is here.
Posted to fop-commits@xmlgraphics.apache.org by lf...@apache.org on 2005/05/02 18:23:37 UTC

cvs commit: xml-fop/src/java/org/apache/fop/layoutmgr BreakingAlgorithm.java PageBreakingAlgorithm.java LineLayoutManager.java KnuthSequence.java

lfurini     2005/05/02 09:23:37

  Modified:    src/java/org/apache/fop/layoutmgr Tag:
                        Temp_KnuthStylePageBreaking BreakingAlgorithm.java
                        PageBreakingAlgorithm.java LineLayoutManager.java
                        KnuthSequence.java
  Log:
  Finn's refactoring of Knuth's breaking code, with small changes
  
  Revision  Changes    Path
  No                   revision
  No                   revision
  1.1.2.2   +552 -516  xml-fop/src/java/org/apache/fop/layoutmgr/Attic/BreakingAlgorithm.java
  
  Index: BreakingAlgorithm.java
  ===================================================================
  RCS file: /home/cvs/xml-fop/src/java/org/apache/fop/layoutmgr/Attic/BreakingAlgorithm.java,v
  retrieving revision 1.1.2.1
  retrieving revision 1.1.2.2
  diff -u -r1.1.2.1 -r1.1.2.2
  --- BreakingAlgorithm.java	18 Mar 2005 09:02:54 -0000	1.1.2.1
  +++ BreakingAlgorithm.java	2 May 2005 16:23:37 -0000	1.1.2.2
  @@ -18,13 +18,114 @@
   
   package org.apache.fop.layoutmgr;
   
  -import java.util.LinkedList;
  -import java.util.ListIterator;
  +import java.util.ArrayList;
   
  +import org.apache.commons.logging.Log;
  +import org.apache.commons.logging.LogFactory;
  +
  +import org.apache.fop.traits.MinOptMax;
  +
  +/**
  + * The set of nodes is sorted into lines indexed into activeLines.
  + * The nodes in each line are linked together in a single linked list by the 
  + * KnuthNode.next field. The activeLines array contains a link to the head of
  + * the linked list in index 'line*2' and a link to the tail at index 'line*2+1'.
  + * <p>
  + * The set of active nodes can be traversed by 
  + * <pre>
  + * for (int line = startLine; line < endLine; line++) {
  + *     for (KnuthNode node = getNode(line); node != null; node = node.next) {
  + *         // Do something with 'node'
  + *     }
  + * }
  + * </pre> 
  + */
   public abstract class BreakingAlgorithm {
  +    // parameters of Knuth's algorithm:
  +    // penalty value for flagged penalties
  +    private int flaggedPenalty = 50;
  +    // demerit for consecutive lines ending at flagged penalties
  +    private int repeatedFlaggedDemerit = 50;
  +    // demerit for consecutive lines belonging to incompatible fitness classes 
  +    private int incompatibleFitnessDemerit = 50;
  +    // suggested modification to the "optimum" number of lines
  +    private int looseness = 0;
  +
  +    /**
  +     * The threshold for considering breaks to be acceptable.
  +     */
  +    private double threshold;
  +
  +    /**
  +     * The paragraph of KnuthElements.
  +     */
  +    private KnuthSequence par;
  +    
  +    /**
  +     * The width of a line.
  +     */
  +    private int lineWidth = 0;
  +    private boolean force =  false;
  +
  +    protected KnuthNode lastDeactivatedNode = null;
  +    private KnuthNode lastTooLong;
  +    private KnuthNode lastTooShort;
  +    private KnuthNode lastDeactivated;
  +
  +    protected int alignment;
  +    protected int alignmentLast;
  +    protected boolean bFirst;
  +
  +    /**
  +     * The set of active nodes.
  +     */
  +    private KnuthNode[] activeLines;
  +    
  +    /**
  +     * The number of active nodes.
  +     */
  +    protected int activeNodeCount;
  +    
  +    /**
  +     * The lowest available line in the set of active nodes.
  +     */
  +    protected int startLine = 0;
  +
  +    /**
  +     * The highest + 1 available line in the set of active nodes.
  +     */
  +    protected int endLine = 0;
  +
  +    /**
  +     * The total width of all elements handled so far.
  +     */
  +    private int totalWidth;
  +
  +    /**
  +     * The total stretch of all elements handled so far.
  +     */
  +    private int totalStretch = 0;
  +
  +    /**
  +     * The total shrink of all elements handled so far.
  +     */
  +    private int totalShrink = 0;
  +
  +    private BestRecords best;
  +    private KnuthNode[] positions;
  +
  +    private static final int INFINITE_RATIO = 1000;
  +
  +    protected static Log log = LogFactory.getLog(KnuthParagraph.class);
  +
  +    public BreakingAlgorithm(int align, int alignLast,
  +                             boolean first) {
  +        alignment = align;
  +        alignmentLast = alignLast;
  +        bFirst = first;
  +        this.best = new BestRecords();
  +    }
   
  -    private final int KNUTH_ALGORITHM = 0;
  -    private final int FIRST_FIT_ALGORITHM = 1;
   
       // this class represent a feasible breaking point
       public class KnuthNode {
  @@ -49,8 +150,11 @@
           // adjustment ratio if the line ends at this breakpoint
           public double adjustRatio;
   
  -/*LF*/  public int availableShrink;
  -/*LF*/  public int availableStretch;
  +        // available stretch of the line ending at this breakpoint
  +        public int availableShrink;
  +
  +        // available shrink of the line ending at this breakpoint
  +        public int availableStretch;
   
           // difference between target and actual line width
           public int difference;
  @@ -61,6 +165,10 @@
           // best node for the preceding breakpoint
           public KnuthNode previous;
   
  +        // next possible node in the same line 
  +        public KnuthNode next;
  +
  +
           public KnuthNode(int position, int line, int fitness,
                            int totalWidth, int totalStretch, int totalShrink,
                            double adjustRatio, int availableShrink, int availableStretch, int difference,
  @@ -72,44 +180,52 @@
               this.totalStretch = totalStretch;
               this.totalShrink = totalShrink;
               this.adjustRatio = adjustRatio;
  -/*LF*/      this.availableShrink = availableShrink;
  -/*LF*/      this.availableStretch = availableStretch;
  +            this.availableShrink = availableShrink;
  +            this.availableStretch = availableStretch;
               this.difference = difference;
               this.totalDemerits = totalDemerits;
               this.previous = previous;
           }
  +
  +        public String toString() {
  +            return "<KnuthNode at " + position + " " +
  +            totalWidth + "+" + totalStretch + "-" + totalShrink +
  +            " line:" + line +
  +            " prev:" + (previous != null ? previous.position : -1) +
  +            " dem:" + totalDemerits +
  +            ">"; 
  +        }
       }
   
       // this class stores information about how the nodes
       // which could start a line
       // ending at the current element
       private class BestRecords {
  -        private static final double INFINITE_DEMERITS = 1E11;
  +        private static final double INFINITE_DEMERITS = Double.POSITIVE_INFINITY;
  +        //private static final double INFINITE_DEMERITS = 1E11;
   
  -        private double bestDemerits[] = {
  -            INFINITE_DEMERITS, INFINITE_DEMERITS,
  -            INFINITE_DEMERITS, INFINITE_DEMERITS
  -        };
  -        private KnuthNode bestNode[] = {null, null, null, null};
  -        private double bestAdjust[] = {0.0, 0.0, 0.0, 0.0};
  -        private int bestDifference[] = {0, 0, 0, 0};
  -/*LF*/  private int bestAvailableShrink[] = {0, 0, 0, 0};
  -/*LF*/  private int bestAvailableStretch[] = {0, 0, 0, 0};
  +        private double bestDemerits[] = new double[4];
  +        private KnuthNode bestNode[] = new KnuthNode[4];
  +        private double bestAdjust[] = new double[4];
  +        private int bestDifference[] = new int[4];
  +        private int bestAvailableShrink[] = new int[4];
  +        private int bestAvailableStretch[] = new int[4];
           private int bestIndex = -1;
   
           public BestRecords() {
  +            reset();
           }
   
           public void addRecord(double demerits, KnuthNode node, double adjust,
                                 int availableShrink, int availableStretch, int difference, int fitness) {
               if (demerits > bestDemerits[fitness]) {
  -                //log.error("New demerits value greter than the old one");
  +                log.error("New demerits value greter than the old one");
               }
               bestDemerits[fitness] = demerits;
               bestNode[fitness] = node;
               bestAdjust[fitness] = adjust;
  -/*LF*/      bestAvailableShrink[fitness] = availableShrink;
  -/*LF*/      bestAvailableStretch[fitness] = availableStretch;
  +            bestAvailableShrink[fitness] = availableShrink;
  +            bestAvailableStretch[fitness] = availableStretch;
               bestDifference[fitness] = difference;
               if (bestIndex == -1 || demerits < bestDemerits[bestIndex]) {
                   bestIndex = fitness;
  @@ -136,13 +252,13 @@
               return bestAdjust[fitness];
           }
   
  -/*LF*/  public int getAvailableShrink(int fitness) {
  -/*LF*/      return bestAvailableShrink[fitness];
  -/*LF*/  }
  -
  -/*LF*/  public int getAvailableStretch(int fitness) {
  -/*LF*/      return bestAvailableStretch[fitness];
  -/*LF*/  }
  +        public int getAvailableShrink(int fitness) {
  +            return bestAvailableShrink[fitness];
  +        }
  +
  +        public int getAvailableStretch(int fitness) {
  +            return bestAvailableStretch[fitness];
  +        }
   
          public int getDifference(int fitness) {
               return bestDifference[fitness];
  @@ -156,40 +272,21 @@
                   return INFINITE_DEMERITS;
               }
           }
  -    }
   
  -    //     parameters of Knuth's algorithm:
  -    // penalty value for flagged penalties
  -    private int flaggedPenalty = 50;
  -    // demerit for consecutive lines ending at flagged penalties
  -    private int repeatedFlaggedDemerit = 50;
  -    // demerit for consecutive lines belonging to incompatible fitness classes 
  -    private int incompatibleFitnessDemerit = 50;
  -    // suggested modification to the "optimum" number of lines
  -    private int looseness = 0;
  -
  -    private static final int INFINITE_RATIO = 1000;
  -    private static int MAX_DEMERITS_INCREASE = 1000;
  -
  -    protected LinkedList activeList = null;
  -    protected LinkedList auxActiveList = null;
  -//    private LinkedList inactiveList = null;
  -    private LinkedList auxInactiveList = null;
  -    protected KnuthNode lastDeactivatedNode = null;
  -    private KnuthNode lastTooLong;
  -    private KnuthNode lastTooShort;
  -    private KnuthNode lastDeactivated;
  +        public void reset() {
  +            for (int i = 0; i < 4; i ++) {
  +                bestDemerits[i] = INFINITE_DEMERITS;
  +                bestNode[i] = null;
  +                bestAdjust[i] = 0.0;
  +                bestDifference[i] = 0;
  +                bestAvailableShrink[i] = 0;
  +                bestAvailableStretch[i] = 0;
  +            }
  +            bestIndex = -1;
  +        }
  +    }
   
  -    protected int alignment;
  -    protected int alignmentLast;
  -    protected boolean bFirst;
   
  -    public BreakingAlgorithm(int align, int alignLast,
  -                             boolean first) {
  -        alignment = align;
  -        alignmentLast = alignLast;
  -        bFirst = first;
  -    }
   
       public abstract void updateData1(int total, double demerits) ;
   
  @@ -200,135 +297,46 @@
       public int findBreakingPoints(KnuthSequence par, int lineWidth,
                                     double threshold, boolean force,
                                     boolean hyphenationAllowed) {
  +        this.par = par;
  +        this.threshold = threshold;
  +        this.force = force;
  +        this.lineWidth = lineWidth;
  +        this.totalWidth = 0;
  +        this.totalStretch = 0;
  +        this.totalShrink = 0;
   
  -        findBreakingPoints(KNUTH_ALGORITHM, par, 0, lineWidth, threshold, force, hyphenationAllowed);
  -
  -        boolean bForced = false;
  -        int optLineNumber;
  -        if (activeList.size() > 0) {
  -            // there is at least one set of breaking points
  -            // select one or more active nodes, removing the others from the list
  -            optLineNumber = filterActiveList();
  -/* a causa delle modifiche di Finn Bock, non c'e' piu' bisogno di usare first fit */
  -//        } else if (force) {
  -//            // no set of breaking points, but must find one
  -//            // use a different algorithm
  -///*LF*/      System.out.println("si ricorre all'algoritmo first fit dalla riga " + lastDeactivatedNode.line + " elemento " + (lastDeactivatedNode.position == 0 ? 0 :lastDeactivatedNode.position + 1));
  -//            findBreakingPoints(FIRST_FIT_ALGORITHM, par, lastDeactivatedNode.position == 0 ? 0 :lastDeactivatedNode.position + 1, lineWidth, threshold, force, hyphenationAllowed);
  -//            activeList.add(auxActiveList.getLast());
  -//            optLineNumber = ((KnuthNode) auxActiveList.getLast()).line;
  -//            bForced = true;
  -        } else {
  -            // no set of breaking points, do nothing
  -            return 0;
  -        }
  -
  -        // now, activeList is not empty:
  -        // if bForced == true, there is ONLY one node in activeList
  -        // if bForced == false, there is AT LEAST one node in activeList
  -/*LF*/  //System.out.println("BA> ora activeList.size() = " + activeList.size());
  -        ListIterator activeListIterator = activeList.listIterator();
  -        while (activeListIterator.hasNext()) {
  -            KnuthNode activeNode = (KnuthNode)activeListIterator.next();
  -            int totalLines;
  -            if (bForced) {
  -                updateData1(optLineNumber, 0);
  -                totalLines = optLineNumber;
  -                auxActiveList.clear();
  -                auxInactiveList.clear();
  -            } else {
  -                updateData1(activeNode.line, activeNode.totalDemerits);
  -                totalLines = activeNode.line;
  -            }
  -            calculateBreakPoints(activeNode, par, totalLines);
  -        }
  -
  -        activeList.clear();
  -//        inactiveList.clear();
  -        return optLineNumber;
  -    }
  -
  -/* ---------------------------- */
  -    public int firstFit(KnuthSequence par, int lineWidth,
  -                        double threshold, boolean force) {
  -        lastDeactivatedNode = new KnuthNode(0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, null);
  -
  -        findBreakingPoints(FIRST_FIT_ALGORITHM, par, 0, lineWidth, threshold, force, true);
  -
  -/*LF*/  activeList = new LinkedList();
  -///*LF*/  inactiveList = new LinkedList();
  -        activeList.add(auxActiveList.getLast());
  -        int optLineNumber = ((KnuthNode) auxActiveList.getLast()).line;
  -
  -        // now, activeList is not empty:
  -        // if bForced == true, there is ONLY one node in activeList
  -        // if bForced == false, there is AT LEAST one node in activeList
  -/*LF*/  //System.out.println("BA> ora activeList.size() = " + activeList.size());
  -        ListIterator activeListIterator = activeList.listIterator();
  -        while (activeListIterator.hasNext()) {
  -            KnuthNode activeNode = (KnuthNode)activeListIterator.next();
  -            int totalLines;
  -            updateData1(optLineNumber, 0);
  -            totalLines = optLineNumber;
  -            auxActiveList.clear();
  -            auxInactiveList.clear();
  -            calculateBreakPoints(activeNode, par, totalLines);
  -        }
  -
  -        activeList.clear();
  -//        inactiveList.clear();
  -        return optLineNumber;
  -    }
  -/* ---------------------------- */
  -
  -    private void findBreakingPoints(int algorithm,
  -                                    KnuthSequence par, int firstIndex,
  -                                    int lineWidth,
  -                                    double threshold, boolean force,
  -                                    boolean hyphenationAllowed) {
  -        int totalWidth = 0;
  -        int totalStretch = 0;
  -        int totalShrink = 0;
  +        activeLines = new KnuthNode[20];
   
           // reset lastTooShort and lastTooLong, as they could be not null
           // because of previous calls to findBreakingPoints
           lastTooShort = lastTooLong = null; 
  -
  +        // reset startLine and endLine
  +        startLine = endLine = 0;
           // current element in the paragraph
           KnuthElement thisElement = null;
  -        // previous element in the paragraph is a KnuthBox
  +        // previous element in the paragraph is a KnuthBox?
           boolean previousIsBox = false;
   
  -/*LF*/  // index of the first KnuthBox in the sequence
  -/*LF*/  int firstBoxIndex = firstIndex;
  -/*LF*/  while (alignment != org.apache.fop.fo.Constants.EN_CENTER
  -/*LF*/         && ! ((KnuthElement) par.get(firstBoxIndex)).isBox()) {
  -/*LF*/      firstBoxIndex++;
  -/*LF*/  }
  -/*LF*/
  +        // index of the first KnuthBox in the sequence
  +        int firstBoxIndex = 0;
  +        while (alignment != org.apache.fop.fo.Constants.EN_CENTER
  +               && ! ((KnuthElement) par.get(firstBoxIndex)).isBox()) {
  +            firstBoxIndex++;
  +        }
  +
           // create an active node representing the starting point
  -        if (algorithm == KNUTH_ALGORITHM) {
  -            activeList = new LinkedList();
  -/*LF*/      activeList.add(new KnuthNode(firstBoxIndex, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, null));
  -//            inactiveList = new LinkedList();
  -        } else {
  -            auxActiveList = new LinkedList();
  -/*LF*/      auxActiveList.add(new KnuthNode(lastDeactivatedNode.position, lastDeactivatedNode.line, 1,
  -                                            0, 0, 0,
  -                                            lastDeactivatedNode.adjustRatio,
  -                                            0, 0, lastDeactivatedNode.difference,
  -                                            0, lastDeactivatedNode.previous));
  -            auxInactiveList = new LinkedList();
  +        activeLines = new KnuthNode[20];
  +        addNode(0, new KnuthNode(firstBoxIndex, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, null));
  +
  +        if (log.isTraceEnabled()) {
  +            log.trace("Looping over " + par.size() + " box objects");
           }
  +        
  +        KnuthNode lastForced = getNode(0);
   
  -        KnuthNode lastForced = (KnuthNode) activeList.getFirst();
           // main loop
  -/*LF*/  ListIterator paragraphIterator = par.listIterator(firstBoxIndex);
  -/*LF*/  //System.out.println("");
  -/*LF*/  //System.out.println("inizio main loop");
  -        while (paragraphIterator.hasNext()) {
  -            thisElement = (KnuthElement) paragraphIterator.next();
  -/*LF*/      //System.out.println("main loop, elemento " + paragraphIterator.previousIndex());
  +        for (int i = 0; i < par.size(); i++) {
  +            thisElement = getElement(i);
               if (thisElement.isBox()) {
                   // a KnuthBox object is not a legal line break
                   totalWidth += thisElement.getW();
  @@ -337,19 +345,11 @@
                   // a KnuthGlue object is a legal line break
                   // only if the previous object is a KnuthBox
                   if (previousIsBox) {
  -                    if (algorithm == KNUTH_ALGORITHM) {
  -                        considerLegalBreak(par, lineWidth, thisElement,
  -                                           totalWidth, totalStretch, totalShrink,
  -                                           threshold);
  -                    } else {
  -                        considerLegalBreak2(par, lineWidth, thisElement,
  -                                            totalWidth, totalStretch, totalShrink,
  -                                            threshold);
  -                    }
  +                    considerLegalBreak(thisElement, i);
                   }
                   totalWidth += thisElement.getW();
  -                totalStretch += ((KnuthGlue) thisElement).getY();
  -                totalShrink += ((KnuthGlue) thisElement).getZ();
  +                totalStretch += thisElement.getY();
  +                totalShrink += thisElement.getZ();
                   previousIsBox = false;
               } else {
                   // a KnuthPenalty is a legal line break
  @@ -358,382 +358,418 @@
                   if (((KnuthPenalty) thisElement).getP()
                       < KnuthElement.INFINITE
                       && (hyphenationAllowed || !((KnuthPenalty) thisElement).isFlagged())) {
  -                    if (algorithm == KNUTH_ALGORITHM) {
  -                        considerLegalBreak(par, lineWidth, thisElement,
  -                                           totalWidth, totalStretch, totalShrink,
  -                                           threshold);
  -                    } else {
  -                        considerLegalBreak2(par, lineWidth, thisElement,
  -                                            totalWidth, totalStretch, totalShrink,
  -                                            threshold);
  -                    }
  +                    considerLegalBreak(thisElement, i);
                   }
                   previousIsBox = false;
               }
  -            /* *** inizio modifiche proposte da Finn Bock *** */
  -            if (activeList.size() == 0) {
  +            if (activeNodeCount == 0) {
                   if (!force) {
  -                    return;
  +                    log.debug("Could not find a set of breaking points " + threshold);
  +                    return 0;
                   }
  -/*LF*/          //System.out.println(" ");
  -/*LF*/          //System.out.println("  lastTooShort.position= " + (lastTooShort == null ? -1 : lastTooShort.position) + " lastTooLong.position= " + (lastTooLong == null ? -1 : lastTooLong.position));
                   if (lastTooShort == null || lastForced.position == lastTooShort.position) {
                       lastForced = lastTooLong;
                   } else {
                       lastForced = lastTooShort;
                   }
   
  -/*LF*/          //System.out.println("  Restarting: position= " + lastForced.position);
  -/*LF*/          //System.out.println(" ");
  +                log.debug("Restarting at node " + lastForced);
                   lastForced.totalDemerits = 0;
  -                activeList.add(lastForced);
  +                addNode(lastForced.line, lastForced);
  +                i = lastForced.position;
  +                startLine = lastForced.line;
  +                endLine = startLine + 1;
                   totalWidth = lastForced.totalWidth;
                   totalStretch = lastForced.totalStretch;
                   totalShrink = lastForced.totalShrink;
                   lastTooShort = lastTooLong = null;
  -                while (paragraphIterator.nextIndex() > (lastForced.position + 1)) {
  -                    paragraphIterator.previous();
  -                }
               }
  -            /* *** fine modifiche proposte da Finn Bock  *** */
           }
  -    }
  -
  -    private void considerLegalBreak2(LinkedList par, int lineWidth,
  -                                     KnuthElement element,
  -                                     int totalWidth, int totalStretch,
  -                                     int totalShrink, double threshold) {
  -        //System.out.println("(" + par.indexOf(element) + ")");
  -        KnuthNode startNode = (KnuthNode) auxActiveList.getFirst();
  -        KnuthNode endNode = auxActiveList.size() > 1 ? (KnuthNode) auxActiveList.getLast() : null;
  -
  -        // these are the new values that must be computed
  -        // in order to define a new active node
  -        int newWidth;
  -        int newStretch;
  -        int newShrink;
  -        int newDifference;
  -        double newRatio;
  -
  -        // compute width, stretch and shrink of the new node
  -        newWidth = totalWidth;
  -        newStretch = totalStretch;
  -        newShrink = totalShrink;
  -        ListIterator tempIterator = par.listIterator(par.indexOf(element));
  -        while (tempIterator.hasNext()) {
  -            KnuthElement tempElement = (KnuthElement)tempIterator.next();
  -            if (tempElement.isBox()) {
  -                break;
  -            } else if (tempElement.isGlue()) {
  -                newWidth += ((KnuthGlue) tempElement).getW();
  -                newStretch += ((KnuthGlue) tempElement).getY();
  -                newShrink += ((KnuthGlue) tempElement).getZ();
  -            } else if (((KnuthPenalty) tempElement).getP() 
  -                       == -KnuthElement.INFINITE
  -                       && tempElement != element) {
  -                break;
  -            }
  +        if (log.isTraceEnabled()) {
  +            log.trace("Main loop completed " + activeNodeCount);
  +            log.trace("Active nodes=" + toString(""));
           }
   
  -        if (endNode == null
  -            || totalWidth + (element.isPenalty() ? element.getW() : 0) - startNode.totalWidth <= lineWidth
  -            || alignment == org.apache.fop.fo.Constants.EN_JUSTIFY
  -               && totalWidth  + (element.isPenalty() ? element.getW() : 0)- startNode.totalWidth - (totalShrink - startNode.totalShrink) <= lineWidth) {
  -            // add content to the same line
  -
  -            // compute difference and ratio
  -            int actualWidth = totalWidth - startNode.totalWidth;
  -            if (element.isPenalty()) {
  -                actualWidth += element.getW();
  -            }
  -            newDifference = lineWidth - actualWidth;
  -            int available = newDifference >= 0 ? totalStretch - startNode.totalStretch
  -                                               : totalShrink - startNode.totalShrink;
  -            newRatio = available != 0 ? (float) newDifference / available
  -                                      : 0;
  -
  -            if (endNode != null) {
  -                auxActiveList.removeLast();
  -            }
  -            auxActiveList.add(new KnuthNode(par.indexOf(element), startNode.line + 1, 0,
  -                                            newWidth, newStretch, newShrink,
  -                                            newRatio, 0, 0, newDifference, 0.0,
  -                                            startNode));
  -            //System.out.println("da " + startNode.position + " a " + par.indexOf(element));
  -            //System.out.println("difference = " + newDifference + " available = " + available + " ratio = " + newRatio);
  -            //System.out.println(" ");
  -        } else {
  -            // start a new line
  +        // there is at least one set of breaking points
  +        // select one or more active nodes, removing the others from the list
  +        int line = filterActiveNodes();
   
  -            // compute difference and ratio
  -            int actualWidth = totalWidth - endNode.totalWidth;
  -            if (element.isPenalty()) {
  -                actualWidth += element.getW();
  -            }
  -            newDifference = lineWidth - actualWidth;
  -            int available = newDifference >= 0 ? totalStretch - endNode.totalStretch
  -                                               : totalShrink - endNode.totalShrink;
  -            newRatio = available != 0 ? (float) newDifference / available
  -                                      : 0;
  -
  -            auxInactiveList.add(auxActiveList.removeFirst());
  -            auxActiveList.add(new KnuthNode(par.indexOf(element), endNode.line + 1, 0,
  -                                            newWidth, newStretch, newShrink,
  -                                            newRatio, 0, 0, newDifference, 0.0,
  -                                            endNode));
  -            //System.out.println("da " + endNode.position + " a " + par.indexOf(element));
  -            //System.out.println("difference = " + newDifference + " available = " + available + " ratio = " + newRatio);
  -            //System.out.println(" ");
  +        // for each active node, create a set of breaking points
  +        for (int i = startLine; i < endLine; i++) {
  +            for (KnuthNode node = getNode(line); node != null; node = node.next) {
  +                updateData1(node.line, node.totalDemerits);
  +                calculateBreakPoints(node, par, node.line);
  +            }
           }
  +
  +        activeLines = null;
  +        return line;
       }
   
  +    private void considerLegalBreak(KnuthElement element, int elementIdx) {
   
  -    private void considerLegalBreak(LinkedList par, int lineWidth,
  -                                    KnuthElement element,
  -                                    int totalWidth, int totalStretch,
  -                                    int totalShrink, double threshold) {
  -        KnuthNode activeNode = null;
  -
  -        ListIterator activeListIterator = activeList.listIterator();
  -        if (activeListIterator.hasNext()) {
  -            activeNode = (KnuthNode)activeListIterator.next();
  -        } else {
  -            activeNode = null;
  +        if (log.isTraceEnabled()) {
  +            log.trace("Feasible breakpoint at " + par.indexOf(element) + " " + totalWidth + "+" + totalStretch + "-" + totalShrink);
  +            log.trace("\tCurrent active node list: " + activeNodeCount + " " + this.toString("\t"));
           }
   
           lastDeactivated = null;
           lastTooLong = null;
  -
  -        while (activeNode != null) {
  -            BestRecords best = new BestRecords();
  -
  -            // these are the new values that must be computed
  -            // in order to define a new active node
  -            int newLine = 0;
  -            int newFitnessClass = 0;
  -            int newWidth = 0;
  -            int newStretch = 0;
  -            int newShrink = 0;
  -            double newIPDAdjust = 0;
  -            double newDemerits = 0;
  -
  -            while (activeNode != null) {
  -                // compute the line number
  -                newLine = activeNode.line + 1;
  -
  -                // compute the adjustment ratio
  -                int actualWidth = totalWidth - activeNode.totalWidth;
  -                if (element.isPenalty()) {
  -                    actualWidth += element.getW();
  -                }
  -                int neededAdjustment = lineWidth - actualWidth;
  -                int maxAdjustment = 0;
  -/*LF*/          int availableShrink = totalShrink - activeNode.totalShrink;
  -/*LF*/          int availableStretch = totalStretch - activeNode.totalStretch;
  -                if (neededAdjustment > 0) {
  -                    maxAdjustment = availableStretch;
  -                    if (maxAdjustment > 0) {
  -                        newIPDAdjust
  -                            = (double) neededAdjustment / maxAdjustment;
  -                    } else {
  -                        newIPDAdjust = INFINITE_RATIO;
  -                    }
  -                } else if (neededAdjustment < 0) {
  -                    maxAdjustment = availableShrink;
  -                    if (maxAdjustment > 0) {
  -                        newIPDAdjust
  -                            = (double) neededAdjustment / maxAdjustment;
  -                    } else {
  -                        newIPDAdjust = -INFINITE_RATIO;
  +        for (int line = startLine; line < endLine; line++) {
  +            for (KnuthNode node = getNode(line); node != null; node = node.next) {
  +                if (node.position == elementIdx) {
  +                    continue;
  +                }
  +                int difference = computeDifference(node, element);
  +                double r = computeAdjustmentRatio(node, difference);
  +                int availableShrink = totalShrink - node.totalShrink;
  +                int availableStretch = totalStretch - node.totalStretch;
  +                if (log.isTraceEnabled()) {
  +                    log.trace("\tr=" + r);
  +                    log.trace("\tline=" + line);
  +                }
  +
  +                // The line would be too long.
  +                if (r < -1 || element.isForcedBreak()) {
  +                    // Deactivate node.
  +                    if (log.isTraceEnabled()) {
  +                        log.trace("Removing " + node);
                       }
  -/*LF*/          } else {
  -/*LF*/              // neededAdjustment == 0
  -/*LF*/              newIPDAdjust = 0;
  -                }
  -
  -                /* calcola demeriti e fitness class in ogni caso, poi,
  -                 * a seconda che il coefficiente sia o meno nel range permesso,
  -                 * aggiorna i record o i migliori nodi disattivati
  -                 */
  -                // compute demerits and fitness class
  -                if (element.isPenalty()
  -                    && ((KnuthPenalty) element).getP() >= 0) {
  -                    newDemerits
  -                        = Math.pow((1
  -                                    + 100 * Math.pow(Math.abs(newIPDAdjust), 3)
  -                                    + ((KnuthPenalty) element).getP()), 2);
  -                } else if (element.isPenalty()
  -                           && ((KnuthPenalty)element).getP()
  -                                > - KnuthElement.INFINITE) {
  -                                /*> -INFINITE_RATIO) { ??? */
  -                    newDemerits
  -                        = Math.pow((1
  -                                    + 100 * Math.pow(Math.abs(newIPDAdjust), 3)), 2)
  -                        - Math.pow(((KnuthPenalty) element).getP(), 2);
  -                } else {
  -                    newDemerits
  -                        = Math.pow((1
  -                                    + 100 * Math.pow(Math.abs(newIPDAdjust), 3)), 2);
  -                }
  -                if (element.isPenalty()
  -                    && ((KnuthPenalty) element).isFlagged()
  -                    && ((KnuthElement) par.get(activeNode.position)).isPenalty()
  -                    && ((KnuthPenalty) par.get(activeNode.position)).isFlagged()) {
  -                    // add demerit for consecutive breaks at flagged penalties
  -                    newDemerits += repeatedFlaggedDemerit;
  -                }
  -                if (newIPDAdjust < -0.5) {
  -                    newFitnessClass = 0;
  -                } else if (newIPDAdjust <= 0.5) {
  -                    newFitnessClass = 1;
  -                } else if (newIPDAdjust <= 1) {
  -                    newFitnessClass = 2;
  -                } else {
  -                    newFitnessClass = 3;
  +                    removeNode(line, node);
  +                    lastDeactivated = compareNodes(lastDeactivated, node);
                   }
  -                if (Math.abs(newFitnessClass - activeNode.fitness) > 1) {
  -                    // add demerit for consecutive breaks
  -                    // with very different fitness classes
  -                    newDemerits += incompatibleFitnessDemerit;
  -                }
  -                newDemerits += activeNode.totalDemerits;
  -
  -/*LF*/          //System.out.println(" possibile soluzione, da " + activeNode.position + " a " + par.indexOf(element));
  -                if ((-1 <= newIPDAdjust) && (newIPDAdjust <= threshold)) {
  -/*LF*/              //System.out.println("  possibile soluzione buona");
  -                    if (newDemerits < best.getDemerits(newFitnessClass)) {
  +    
  +                // The line is within the available shrink and the threshold.
  +                if (r >= -1 && r <= threshold) {
  +                    int fitnessClass = computeFitness(r);
  +                    double demerits = computeDemerits(node, element, fitnessClass, r);
  +    
  +                    if (log.isTraceEnabled()) {
  +                        log.trace("\tDemerits=" + demerits);
  +                        log.trace("\tFitness class=" + fitnessClass);
  +                    }
  +    
  +                    if (demerits < best.getDemerits(fitnessClass)) {
                           // updates best demerits data
  -                        best.addRecord(newDemerits, activeNode, newIPDAdjust,
  -                                       availableShrink, availableStretch, neededAdjustment, newFitnessClass);
  -/*LF*/                  lastTooShort = null;
  +                        best.addRecord(demerits, node, r, availableShrink, availableStretch,
  +                                       difference, fitnessClass);
  +                        lastTooShort = null;
                       }
  -                } else {
  -                     // la linea e' troppo piena o troppo vuota
  -                    if (newIPDAdjust <= -1) {
  -/*LF*/                  //System.out.println("  possibile soluzione lunga");
  -                        if (lastTooLong == null || newDemerits < lastTooLong.totalDemerits) {
  -/*LF*/                      //System.out.println("  aggiornato lastTooLong, da " + activeNode.position + " a " + par.indexOf(element));
  -                            lastTooLong = new KnuthNode(par.indexOf(element), newLine, newFitnessClass,
  +                }
  +                
  +                // The line is way too short, but we are in forcing mode, so a node is
  +                // calculated and stored in lastValidNode.
  +                if (force && (r <= -1 || r > threshold)) {
  +                    int fitnessClass = computeFitness(r);
  +                    double demerits = computeDemerits(node, element, fitnessClass, r);
  +                    if (r <= -1) {
  +                        if (lastTooLong == null || demerits < lastTooLong.totalDemerits) {
  +                            lastTooLong = new KnuthNode(elementIdx, line + 1, fitnessClass,
                                       totalWidth, totalStretch, totalShrink,
  -                                    newIPDAdjust, availableShrink, availableStretch, neededAdjustment, newDemerits, activeNode);
  +                                    r, availableShrink, availableStretch,
  +                                    difference, demerits, node);
  +                            if (log.isTraceEnabled()) {
  +                                log.trace("Picking tooLong " + lastTooLong);
  +                            }
                           }
                       } else {
  -/*LF*/                  //System.out.println("  possibile soluzione corta");
  -/*LF*/                  //if (lastTooShort != null) {
  -/*LF*/                  //    System.out.println("  vecchia soluzione corta da " + lastTooShort.previous.position + " a " + lastTooShort.position);
  -/*LF*/                  //}
  -                        if (lastTooShort == null || newDemerits <= lastTooShort.totalDemerits) {
  -/*LF*/                      //System.out.println("  aggiornato lastTooShort, da " + activeNode.position + " a " + par.indexOf(element));
  -                            lastTooShort = new KnuthNode(par.indexOf(element), newLine, newFitnessClass,
  +                        if (lastTooShort == null || demerits <= lastTooShort.totalDemerits) {
  +                            lastTooShort = new KnuthNode(elementIdx, line + 1, fitnessClass,
                                       totalWidth, totalStretch, totalShrink,
  -                                    newIPDAdjust, availableShrink, availableStretch, neededAdjustment, newDemerits, activeNode);
  +                                    r, availableShrink, availableStretch,
  +                                    difference, demerits, node);
  +                            if (log.isTraceEnabled()) {
  +                                log.trace("Picking tooShort " + lastTooShort);
  +                            }
                           }
                       }
                   }
  -                /* disattivazione di un nodo */
  -                if (newIPDAdjust < -1
  -                    || (element.isPenalty() 
  -                        && ((KnuthPenalty)element).getP()
  -                             == -KnuthElement.INFINITE)
  -/*LF*/                  && !(activeNode.position
  -                             == par.indexOf(element))) {
  -                    // deactivate activeNode
  -                    KnuthNode tempNode
  -                        = (KnuthNode)activeListIterator.previous();
  -                    int iCallNext = 0;
  -                    while (tempNode != activeNode) {
  -                        // this is not the node we meant to remove!
  -                        tempNode = (KnuthNode)activeListIterator.previous();
  -                        iCallNext ++;
  -                    }
  -                    activeListIterator.remove();
  -                    for (int i = 0; i < iCallNext; i++) {
  -                        activeListIterator.next();
  -                    }
  -                }
  +            }
  +            addBreaks(line, elementIdx);
  +        }
  +    }
   
  -                if (activeListIterator.hasNext()) {
  -                    activeNode = (KnuthNode) activeListIterator.next();
  -                } else {
  -                    activeNode = null;
  -                    break;
  -                }
  -                if (activeNode.line >= newLine) {
  -                    break;
  -                }
  -            } // end of the inner while
  +    private void addBreaks(int line, int elementIdx) {
  +        if (!best.hasRecords()) {
  +            return;
  +        }
   
  -            if (best.hasRecords()) {
  -                // compute width, stratchability and shrinkability
  -                newWidth = totalWidth;
  -                newStretch = totalStretch;
  -                newShrink = totalShrink;
  -                ListIterator tempIterator
  -                    = par.listIterator(par.indexOf(element));
  -                while (tempIterator.hasNext()) {
  -                    KnuthElement tempElement
  -                        = (KnuthElement)tempIterator.next();
  -                    if (tempElement.isBox()) {
  -                        break;
  -                    } else if (tempElement.isGlue()) {
  -                        newWidth += ((KnuthGlue) tempElement).getW();
  -                        newStretch += ((KnuthGlue) tempElement).getY();
  -                        newShrink += ((KnuthGlue) tempElement).getZ();
  -                    } else if (((KnuthPenalty) tempElement).getP() 
  -                               == -KnuthElement.INFINITE
  -                               && tempElement != element) {
  -                        break;
  -                    }
  -                }
  +        int newWidth = totalWidth;
  +        int newStretch = totalStretch;
  +        int newShrink = totalShrink;
   
  -                // add nodes to the active nodes list
  -                for (int i = 0; i <= 3; i++) {
  -                    if (best.notInfiniteDemerits(i)
  -                        && best.getDemerits(i)
  -                           <= (best.getMinDemerits()
  -                               + incompatibleFitnessDemerit)) {
  -                        // the nodes in activeList must be ordered
  -                        // by line number and position;
  -                        // so:
  -                        // 1) advance in the list until the end,
  -                        // or a node with a higher line number, is reached
  -/*LF*/                  int iStepsForward = 0;
  -/*LF*/                  KnuthNode tempNode;
  -/*LF*/                  while (activeListIterator.hasNext()) {
  -/*LF*/                      iStepsForward ++;
  -/*LF*/                      tempNode = (KnuthNode) activeListIterator.next();
  -/*LF*/                      if (tempNode.line > (best.getNode(i).line + 1)) {
  -/*LF*/                          activeListIterator.previous();
  -/*LF*/                          iStepsForward --;
  -/*LF*/                          break;
  -/*LF*/                      }
  -/*LF*/                  }
  -                        // 2) add the new node
  -                        activeListIterator.add
  -                            (new KnuthNode(par.indexOf(element),
  -                                           best.getNode(i).line + 1, i,
  -                                           newWidth, newStretch, newShrink,
  -                                           best.getAdjust(i),
  -                                           best.getAvailableShrink(i), best.getAvailableStretch(i),
  -                                           best.getDifference(i),
  -                                           best.getDemerits(i), 
  -                                           best.getNode(i)));
  -                        // 3) go back
  -/*LF*/                  for (int j = 0;
  -/*LF*/                       j <= iStepsForward;
  -/*LF*/                       j ++) {
  -/*LF*/                      activeListIterator.previous();
  -/*LF*/                  }
  -                    }
  -                }
  -            }
  -            if (activeNode == null) {
  +        for (int i = elementIdx; i < par.size(); i++) {
  +            KnuthElement tempElement = getElement(i);
  +            if (tempElement.isBox()) {
                   break;
  +            } else if (tempElement.isGlue()) {
  +                newWidth += tempElement.getW();
  +                newStretch += tempElement.getY();
  +                newShrink += tempElement.getZ();
  +            } else if (tempElement.isForcedBreak() && i != elementIdx) {
  +                break;
  +            }
  +        }
  +
  +        // add nodes to the active nodes list
  +        double minimumDemerits = best.getMinDemerits() + incompatibleFitnessDemerit;
  +        for (int i = 0; i <= 3; i++) {
  +            if (best.notInfiniteDemerits(i) && best.getDemerits(i) <= minimumDemerits) {
  +                // the nodes in activeList must be ordered
  +                // by line number and position;
  +                if (log.isTraceEnabled()) {
  +                    log.trace("\tInsert new break in list of " + activeNodeCount);
  +                }
  +                KnuthNode newNode = new KnuthNode(elementIdx, line + 1, i,
  +                                   newWidth, newStretch, newShrink,
  +                                   best.getAdjust(i),
  +                                   best.getAvailableShrink(i),
  +                                   best.getAvailableStretch(i),
  +                                   best.getDifference(i),
  +                                   best.getDemerits(i),
  +                                   best.getNode(i));
  +                addNode(line + 1, newNode);
  +            }
  +        }
  +        best.reset();
  +    }
  +
  +    /**
  +     * Return the difference between the line width and the width of the break that
  +     * ends in 'element'.
  +     * @param activeNode
  +     * @param element
  +     * @return The difference in width. Positive numbers mean extra space in the line,
  +     * negative number that the line overflows. 
  +     */
  +    private int computeDifference(KnuthNode activeNode, KnuthElement element) {
  +        // compute the adjustment ratio
  +        int actualWidth = totalWidth - activeNode.totalWidth;
  +        if (element.isPenalty()) {
  +            actualWidth += element.getW();
  +        }
  +        return lineWidth - actualWidth;
  +    }
  +
  +    /**
  +     * Return the adjust ration needed to make up for the difference. A ration of 
  +     * <ul>
  +     *    <li>0 means that the break has the exact right width</li>
  +     *    <li>&gt;= -1 && &lt; 0  means that the break is to wider than the line, 
  +     *        but within the minimim values of the glues.</li> 
  +     *    <li>&gt;0 && &lt 1 means that the break is smaller than the line width, 
  +     *        but within the maximum values of the glues.</li>
  +     *    <li>&gt 1 means that the break is too small to make up for the glues.</li> 
  +     * </ul>
  +     * @param activeNode
  +     * @param difference
  +     * @return The ration.
  +     */
  +    private double computeAdjustmentRatio(KnuthNode activeNode, int difference) {
  +        // compute the adjustment ratio
  +        if (difference > 0) {
  +            int maxAdjustment = totalStretch - activeNode.totalStretch;
  +            if (maxAdjustment > 0) {
  +                return (double) difference / maxAdjustment;
  +            } else {
  +                return INFINITE_RATIO;
  +            }
  +        } else if (difference < 0) {
  +            int maxAdjustment = totalShrink - activeNode.totalShrink;
  +            if (maxAdjustment > 0) {
  +                return (double) difference / maxAdjustment;
  +            } else {
  +                return -INFINITE_RATIO;
               }
  -        } // end of the outer while
  +        } else {
  +            return 0;
  +        }
  +    }
  +    
  +    /**
  +     * Figure out the fitness class of this line (tight, loose,
  +     * very tight or very loose).
  +     * @param r
  +     * @return
  +     */
  +    private int computeFitness(double r) {
  +        int newFitnessClass;
  +        if (r < -0.5) {
  +            return 0;
  +        } else if (r <= 0.5) {
  +            return 1;
  +        } else if (r <= 1) {
  +            return 2;
  +        } else {
  +            return 3;
  +        }
  +    }
  +
  +    private double computeDemerits(KnuthNode activeNode, KnuthElement element, 
  +                                  int fitnessClass, double r) {
  +        double demerits = 0;
  +        // compute demerits
  +        double f = Math.abs(r);
  +        f = 1 + 100 * f * f * f;
  +        if (element.isPenalty() && element.getP() >= 0) {
  +            f += element.getP();
  +            demerits = f * f;
  +        } else if (element.isPenalty() && !element.isForcedBreak()) {
  +            double penalty = element.getP();
  +            demerits = f * f - penalty * penalty;
  +        } else {
  +            demerits = f * f;
  +        }
  +    
  +        if (element.isPenalty() && ((KnuthPenalty) element).isFlagged()
  +            && getElement(activeNode.position).isPenalty()
  +            && ((KnuthPenalty) getElement(activeNode.position)).isFlagged()) {
  +            // add demerit for consecutive breaks at flagged penalties
  +            demerits += repeatedFlaggedDemerit;
  +        }
  +        if (Math.abs(fitnessClass - activeNode.fitness) > 1) {
  +            // add demerit for consecutive breaks
  +            // with very different fitness classes
  +            demerits += incompatibleFitnessDemerit;
  +        }
  +        demerits += activeNode.totalDemerits;
  +        return demerits;
  +    }
  +
  +    /**
  +     * Return the element at index idx in the paragraph.
  +     * @param idx index of the element.
  +     * @return
  +     */
  +    private KnuthElement getElement(int idx) {
  +        return (KnuthElement) par.get(idx);
  +    }
  +
  +    /**
  +     * Compare two KnuthNodes and return the node with the least demerit. 
  +     * @param node1 The first knuth node.
  +     * @param node2 The other knuth node.
  +     * @return
  +     */
  +    protected KnuthNode compareNodes(KnuthNode node1, KnuthNode node2) {
  +        if (node1 == null || node2.position > node1.position) {
  +            return node2;
  +        }
  +        if (node2.position == node1.position) {
  +            if (node2.totalDemerits < node1.totalDemerits) {
  +                return node2;
  +            }
  +        }
  +        return node1;
  +    }
  +
  +    /**
  +     * Add a KnuthNode at the end of line 'line'. 
  +     * If this is the first node in the line, adjust endLine accordingly.
  +     * @param line
  +     * @param node
  +     */
  +    private void addNode(int line, KnuthNode node) {
  +        int headIdx = line * 2;
  +        if (headIdx >= activeLines.length) {
  +            KnuthNode[] oldList = activeLines;
  +            activeLines = new KnuthNode[headIdx + headIdx];
  +            System.arraycopy(oldList, 0, activeLines, 0, oldList.length);
  +        }
  +        node.next = null;
  +        if (activeLines[headIdx + 1] != null) {
  +            activeLines[headIdx + 1].next = node;
  +        } else {
  +            activeLines[headIdx] = node;
  +            endLine = line+1;
  +        }
  +        activeLines[headIdx + 1] = node;
  +        activeNodeCount++;
  +    }
  +
  +    /**
  +     * Remove the first node in line 'line'. If the line then becomes empty, adjust the
  +     * startLine accordingly.
  +     * @param line
  +     * @param node
  +     */
  +    protected void removeNode(int line, KnuthNode node) {
  +        KnuthNode n = getNode(line);
  +        if (n != node) {
  +            log.error("Should be first");
  +        } else {
  +            activeLines[line*2] = node.next;
  +            if (node.next == null) {
  +                activeLines[line*2+1] = null;
  +            }
  +            while (startLine < endLine && getNode(startLine) == null) {
  +                startLine++;
  +            }
  +        }
  +        activeNodeCount--;
  +    }
  +
  +    protected KnuthNode getNode(int line) {
  +        return activeLines[line * 2];
  +    }
  +
  +    /**
  +     * Return true if the position 'idx' is a legal breakpoint.
  +     * @param idx
  +     * @return
  +     */
  +    private boolean isLegalBreakpoint(int idx) {
  +        KnuthElement elm = getElement(idx);
  +        if (elm.isPenalty() && elm.getP() != KnuthElement.INFINITE) {
  +            return true;
  +        } else if (idx > 0 && elm.isGlue() && getElement(idx-1).isBox()) {
  +            return true;
  +        } else {
  +            return false;
  +        }
  +    }
  +    
  +    public int getDifference(int line) {
  +        return positions[line].difference;
  +    }
  +
  +    public double getAdjustRatio(int line) {
  +        return positions[line].adjustRatio;
  +    }
  +
  +    public int getStart(int line) {
  +        KnuthNode previous = positions[line].previous;
  +        return line == 0 ? 0 : previous.position + 1; 
  +    }
  +
  +    public int getEnd(int line) {
  +        return positions[line].position;
  +    }
  +
  +    /**
  +     * Return a string representation of a MinOptMax in the form of a 
  +     * "width+stretch-shrink". Useful only for debugging.
  +     * @param mom
  +     * @return 
  +     */
  +    private static String width(MinOptMax mom) {
  +        return mom.opt + "+" + (mom.max - mom.opt) + "-" + (mom.opt - mom.min); 
  +
  +    }
  +
  +    public String toString(String prepend) {
  +        StringBuffer sb = new StringBuffer();
  +        sb.append("[\n");
  +        for (int i = startLine; i < endLine; i++) {
  +            for (KnuthNode node = getNode(i); node != null; node = node.next) {
  +                sb.append(prepend + "\t" + node + ",\n");
  +            }
  +        }
  +        sb.append(prepend + "]");
  +        return sb.toString();
       }
   
  -    protected abstract int filterActiveList() ;
  +    protected abstract int filterActiveNodes() ;
   
       private void calculateBreakPoints(KnuthNode node, KnuthSequence par,
                                         int total) {
  
  
  
  1.1.2.2   +9 -13     xml-fop/src/java/org/apache/fop/layoutmgr/Attic/PageBreakingAlgorithm.java
  
  Index: PageBreakingAlgorithm.java
  ===================================================================
  RCS file: /home/cvs/xml-fop/src/java/org/apache/fop/layoutmgr/Attic/PageBreakingAlgorithm.java,v
  retrieving revision 1.1.2.1
  retrieving revision 1.1.2.2
  diff -u -r1.1.2.1 -r1.1.2.2
  --- PageBreakingAlgorithm.java	18 Mar 2005 09:02:55 -0000	1.1.2.1
  +++ PageBreakingAlgorithm.java	2 May 2005 16:23:37 -0000	1.1.2.2
  @@ -64,21 +64,17 @@
                   bestActiveNode.position, ratio, difference));
       }
   
  -    protected int filterActiveList() {
  -        // leave only bestActiveNode in the activeList
  -        KnuthNode tempNode = null;
  +    protected int filterActiveNodes() {
  +        // leave only the active node with fewest total demerits
           KnuthNode bestActiveNode = null;
  -        System.out.println("PBA.filterActiveList> " + activeList.size() + " possibilita'");
  -        while (activeList.size() > 0) {
  -            tempNode = (KnuthNode)activeList.removeFirst();
  -            System.out.println("                      + linee= " + tempNode.line + " demeriti= " + tempNode.totalDemerits);
  -            if (bestActiveNode == null
  -                || tempNode.totalDemerits < bestActiveNode.totalDemerits) {
  -                bestActiveNode = tempNode;
  +        for (int i = startLine; i < endLine; i++) {
  +            for (KnuthNode node = getNode(i); node != null; node = node.next) {
  +                bestActiveNode = compareNodes(bestActiveNode, node);
  +                if (node != bestActiveNode) {
  +                    removeNode(i, node);
  +                }
               }
           }
  -        activeList.add(bestActiveNode);
  -        System.out.println("                      migliore " + bestActiveNode.line);
           return bestActiveNode.line;
       }
   
  
  
  
  1.42.2.6  +26 -36    xml-fop/src/java/org/apache/fop/layoutmgr/LineLayoutManager.java
  
  Index: LineLayoutManager.java
  ===================================================================
  RCS file: /home/cvs/xml-fop/src/java/org/apache/fop/layoutmgr/LineLayoutManager.java,v
  retrieving revision 1.42.2.5
  retrieving revision 1.42.2.6
  diff -u -r1.42.2.5 -r1.42.2.6
  --- LineLayoutManager.java	20 Apr 2005 17:14:42 -0000	1.42.2.5
  +++ LineLayoutManager.java	2 May 2005 16:23:37 -0000	1.42.2.6
  @@ -415,55 +415,45 @@
               return super.findBreakingPoints(par, lineWidth, threshold, force, hyphenationAllowed);
           }
   
  -        protected int filterActiveList() {
  +        protected int filterActiveNodes() {
               KnuthNode bestActiveNode = null;
   
               if (pageAlignment == EN_JUSTIFY) {
  -                // leave all active nodes in the activeList
  -                // and find the optimum line number
  -                ListIterator activeListIterator = activeList.listIterator();
  -                KnuthNode tempNode = null;
  -                //System.out.println("LBA.filterActiveList> " + activeList.size() + " layouts");
  -                while (activeListIterator.hasNext()) {
  -                    tempNode = (KnuthNode)activeListIterator.next();
  -                    //System.out.println("                      + lines = " + tempNode.line + " demerits = " + tempNode.totalDemerits);
  -                    if (bestActiveNode == null
  -                        || tempNode.totalDemerits < bestActiveNode.totalDemerits) {
  -                        bestActiveNode = tempNode;
  +                // leave all active nodes and find the optimum line number
  +                //System.out.println("LBA.filterActiveNodes> " + activeNodeCount + " layouts");
  +                for (int i = startLine; i < endLine; i++) {
  +                    for (KnuthNode node = getNode(i); node != null; node = node.next) {
  +                        //System.out.println("                       + lines = " + node.line + " demerits = " + node.totalDemerits);
  +                        bestActiveNode = compareNodes(bestActiveNode, node);
                       }
                   }
   
  -                // scan activeList once again and remove some nodes
  -                activeListIterator = activeList.listIterator();
  -                tempNode = null;
  +                // scan the node set once again and remove some nodes
                   //System.out.println("LBA.filterActiveList> layout selection");
  -                while (activeListIterator.hasNext()) {
  -                    tempNode = (KnuthNode)activeListIterator.next();
  -                    //if (Math.abs(tempNode.line - bestActiveNode.line) > maxDiff) {
  -                    //if (false) {
  -                    if (tempNode.line != bestActiveNode.line
  -                        && tempNode.totalDemerits > MAX_DEMERITS) {
  -                        //System.out.println("                    XXX lines = " + tempNode.line + " demerits = " + tempNode.totalDemerits);
  -                        activeListIterator.remove();
  -                    } else {
  -                        //System.out.println("                     ok lines = " + tempNode.line + " demerits = " + tempNode.totalDemerits);
  +                for (int i = startLine; i < endLine; i++) {
  +                    for (KnuthNode node = getNode(i); node != null; node = node.next) {
  +                        //if (Math.abs(node.line - bestActiveNode.line) > maxDiff) {
  +                        //if (false) {
  +                        if (node.line != bestActiveNode.line
  +                            && node.totalDemerits > MAX_DEMERITS) {
  +                            //System.out.println("                     XXX lines = " + node.line + " demerits = " + node.totalDemerits);
  +                            removeNode(i, node);
  +                        } else {
  +                            //System.out.println("                      ok lines = " + node.line + " demerits = " + node.totalDemerits);
  +                        }
                       }
                   }
               } else {
  -                // leave only bestActiveNode in the activeList
  -                KnuthNode tempNode = null;
  -                //System.out.println("LBA.filterActiveList> " + activeList.size() + " layouts");
  -                while (activeList.size() > 0) {
  -                    tempNode = (KnuthNode)activeList.removeFirst();
  -                    //System.out.println("                      + lines = " + tempNode.line + " demerits = " + tempNode.totalDemerits);
  -                    if (bestActiveNode == null
  -                        || tempNode.totalDemerits < bestActiveNode.totalDemerits) {
  -                        bestActiveNode = tempNode;
  +                // leave only the active node with fewest total demerits
  +                for (int i = startLine; i < endLine; i++) {
  +                    for (KnuthNode node = getNode(i); node != null; node = node.next) {
  +                        bestActiveNode = compareNodes(bestActiveNode, node);
  +                        if (node != bestActiveNode) {
  +                            removeNode(i, node);
  +                        }
                       }
                   }
  -                activeList.add(bestActiveNode);
               }
  -            //System.out.println("                      best " + bestActiveNode.line);
               return bestActiveNode.line;
           }
       }
  
  
  
  1.1.2.3   +19 -3     xml-fop/src/java/org/apache/fop/layoutmgr/Attic/KnuthSequence.java
  
  Index: KnuthSequence.java
  ===================================================================
  RCS file: /home/cvs/xml-fop/src/java/org/apache/fop/layoutmgr/Attic/KnuthSequence.java,v
  retrieving revision 1.1.2.2
  retrieving revision 1.1.2.3
  diff -u -r1.1.2.2 -r1.1.2.3
  --- KnuthSequence.java	22 Mar 2005 14:35:06 -0000	1.1.2.2
  +++ KnuthSequence.java	2 May 2005 16:23:37 -0000	1.1.2.3
  @@ -18,12 +18,12 @@
   
   package org.apache.fop.layoutmgr;
   
  -import java.util.LinkedList;
  +import java.util.ArrayList;
   
   /**
    * Represents a list of Knuth elements.
    */
  -public class KnuthSequence extends LinkedList {
  +public class KnuthSequence extends ArrayList {
       /** Number of elements to ignore at the beginning of the list. */ 
       public int ignoreAtStart = 0;
       /** Number of elements to ignore at the end of the list. */
  @@ -64,4 +64,20 @@
               return null;
           }
       }
  +
  +    public KnuthElement getLast() {
  +        int idx = size();
  +        if (idx == 0) {
  +            return null; 
  +        }
  +        return (KnuthElement) get(idx - 1);
  +    }
  +
  +    public KnuthElement removeLast() {
  +        int idx = size();
  +        if (idx == 0) {
  +            return null; 
  +        }
  +        return (KnuthElement) remove(idx - 1);
  +    }
   }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: fop-commits-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: fop-commits-help@xmlgraphics.apache.org