]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Initial commit for the knuth-style page breaking code.
authorJeremias Maerki <jeremias@apache.org>
Fri, 18 Mar 2005 09:02:56 +0000 (09:02 +0000)
committerJeremias Maerki <jeremias@apache.org>
Fri, 18 Mar 2005 09:02:56 +0000 (09:02 +0000)
Based on work by Luca Furini.

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/branches/Temp_KnuthStylePageBreaking@198509 13f79535-47bb-0310-9956-ffa450edef68

34 files changed:
src/java/org/apache/fop/fo/Constants.java
src/java/org/apache/fop/fo/FOPropertyMapping.java
src/java/org/apache/fop/fo/PropertySets.java
src/java/org/apache/fop/layoutmgr/AbstractBreaker.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java
src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java
src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
src/java/org/apache/fop/layoutmgr/BlockLevelLayoutManager.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java
src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/CharacterLayoutManager.java
src/java/org/apache/fop/layoutmgr/ContentLayoutManager.java
src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java
src/java/org/apache/fop/layoutmgr/InlineLayoutManager.java
src/java/org/apache/fop/layoutmgr/InlineLevelLayoutManager.java
src/java/org/apache/fop/layoutmgr/InlineStackingLayoutManager.java
src/java/org/apache/fop/layoutmgr/KnuthBlockBox.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/KnuthBox.java
src/java/org/apache/fop/layoutmgr/KnuthGlue.java
src/java/org/apache/fop/layoutmgr/KnuthInlineBox.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/KnuthParagraph.java
src/java/org/apache/fop/layoutmgr/KnuthPenalty.java
src/java/org/apache/fop/layoutmgr/KnuthSequence.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/LayoutContext.java
src/java/org/apache/fop/layoutmgr/LayoutManager.java
src/java/org/apache/fop/layoutmgr/LeaderLayoutManager.java
src/java/org/apache/fop/layoutmgr/LeafNodeLayoutManager.java
src/java/org/apache/fop/layoutmgr/LineLayoutManager.java
src/java/org/apache/fop/layoutmgr/LineLayoutPossibilities.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java
src/java/org/apache/fop/layoutmgr/TextLayoutManager.java
src/java/org/apache/fop/traits/LayoutProps.java

index fb2dc7fe9d025893fc043abf9ddd2eabc10bd42e..9a0423bc4398cb0dae56115d982bbc94b3ff963d 100644 (file)
@@ -375,7 +375,8 @@ public interface Constants {
     int PR_INTRUSION_DISPLACE = 247;
     int PR_INDEX_CLASS = 248;   // XSL 1.1
     int PR_INDEX_KEY = 249;     // XSL 1.1
-    int PROPERTY_COUNT = 249;
+    int PR_X_BLOCK_PROGRESSION_UNIT = 250; //Custom extension
+    int PROPERTY_COUNT = 250;
 
     // compound property constants
 
@@ -553,5 +554,7 @@ public interface Constants {
     int EN_VISIBLE = 159;
     int EN_WIDER = 160;
     int EN_WRAP = 161;
-    int ENUM_COUNT = 161;
+    int EN_X_FILL = 162; //non-standard for display-align
+    int EN_X_DISTRIBUTE = 163; //non-standard for display-align
+    int ENUM_COUNT = 163;
 }
index a1328b9bfa69e71eaff2c5c665ce8cba8abc6bb2..11b69080df129a637c128d7a5c83746b06c6d4d6 100644 (file)
@@ -1354,6 +1354,8 @@ public class FOPropertyMapping implements Constants {
         m.addEnum("after", getEnumProperty(EN_AFTER, "AFTER"));
         m.addEnum("center", getEnumProperty(EN_CENTER, "CENTER"));
         m.addEnum("auto", getEnumProperty(EN_AUTO, "AUTO"));
+/*LF*/  m.addEnum("distribute", getEnumProperty(EN_X_DISTRIBUTE, "DISTRIBUTE"));
+/*LF*/  m.addEnum("fill", getEnumProperty(EN_X_FILL, "FILL"));
         m.setDefault("auto");
         addPropertyMaker("display-align", m);
 
@@ -1536,6 +1538,12 @@ public class FOPropertyMapping implements Constants {
         l.setPercentBase(LengthBase.BLOCK_WIDTH);
         l.setDefault("auto");
         addPropertyMaker("width", l);
+
+/*LF*/  // block-progression-unit (**CUSTOM EXTENSION**)
+/*LF*/  l  = new LengthProperty.Maker(PR_X_BLOCK_PROGRESSION_UNIT);
+/*LF*/  l.setInherited(false);
+/*LF*/  l.setDefault("0pt");
+/*LF*/  addPropertyMaker("block-progression-unit", l);
     }
     
     private void createBlockAndLineProperties() {
index c00b15782426292bc870b5d54d2d1f989512ee90..3209cb58b2780d18f6050a5125a695cf75d461fe 100644 (file)
@@ -395,6 +395,7 @@ public class PropertySets {
         elem.addProperties(CommonBorderPaddingBackgroundProperties);
         elem.addProperties(CommonMarginPropertiesBlock);
         elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION);
+        elem.addProperty(Constants.PR_X_BLOCK_PROGRESSION_UNIT);
         elem.addProperty(Constants.PR_PAGE_BREAK_AFTER);
         elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE);
         elem.addProperty(Constants.PR_BREAK_AFTER);
diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
new file mode 100644 (file)
index 0000000..269f384
--- /dev/null
@@ -0,0 +1,672 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.traits.MinOptMax;
+
+/**
+ * Abstract base class for breakers (page breakers, static region handlers etc.).
+ */
+public abstract class AbstractBreaker {
+
+    /** logging instance */
+    protected static Log log = LogFactory.getLog(AbstractBreaker.class);
+
+    /*LF*/
+    public static class PageBreakPosition extends LeafPosition {
+        double bpdAdjust; // Percentage to adjust (stretch or shrink)
+        int difference;
+
+        PageBreakPosition(LayoutManager lm, int iBreakIndex,
+                          double bpdA, int diff) {
+            super(lm, iBreakIndex);
+            bpdAdjust = bpdA;
+            difference = diff;
+        }
+    }
+
+    private class BlockSequence extends KnuthSequence {
+        static final int ANY_PAGE = 0;
+        static final int ODD_PAGE = 1;
+        static final int EVEN_PAGE = 2;
+
+        private int startOn;
+
+        public BlockSequence(int iStartOn) {
+            super();
+            startOn = iStartOn;
+        }
+
+        public BlockSequence endBlockSequence() {
+            KnuthSequence temp = super.endSequence();
+            if (temp != null) {
+                BlockSequence returnSequence = new BlockSequence(startOn);
+                returnSequence.addAll(temp);
+                returnSequence.ignoreAtEnd = this.ignoreAtEnd;
+                return returnSequence;
+            } else {
+                return null;
+            }
+        }
+    }
+
+    /** blockListIndex of the current BlockSequence in blockLists */
+    private int blockListIndex = 0;
+/*LF*/
+    /*LF*/
+    private List blockLists = null;
+
+    private int alignment;
+    private int alignmentLast;
+    /*LF*/
+
+    protected abstract int getCurrentDisplayAlign();
+    protected abstract boolean hasMoreContent();
+    protected abstract void addAreas(PositionIterator posIter, LayoutContext context);
+    protected abstract LayoutManager getTopLevelLM();
+    protected abstract LayoutManager getCurrentChildLM();
+
+    protected LayoutContext createLayoutContext() {
+        return new LayoutContext(0);
+    }
+    
+    public void doLayout(int flowBPD) {
+        LayoutContext childLC = createLayoutContext();
+
+        //System.err.println("Vertical alignment: " +
+        // currentSimplePageMaster.getRegion(FO_REGION_BODY).getDisplayAlign());
+        if (getCurrentDisplayAlign() == Constants.EN_X_FILL) {
+            //EN_FILL is non-standard (by LF)
+            alignment = Constants.EN_JUSTIFY;
+        } else {
+            alignment = Constants.EN_START;
+        }
+        alignmentLast = Constants.EN_START;
+
+        BlockSequence blockList;
+        blockLists = new java.util.ArrayList();
+
+        System.out.println("PLM> flow BPD =" + flowBPD);
+        
+        //*** Phase 1: Get Knuth elements ***
+        int nextSequenceStartsOn = BlockSequence.ANY_PAGE;
+        while (hasMoreContent()) {
+            nextSequenceStartsOn = getNextBlockList(childLC, nextSequenceStartsOn, blockLists);
+        }
+
+        //*** Phase 2: Alignment and breaking ***
+        System.out.println("PLM> blockLists.size() = " + blockLists.size());
+        for (blockListIndex = 0; blockListIndex < blockLists.size(); blockListIndex++) {
+            blockList = (BlockSequence) blockLists.get(blockListIndex);
+            
+            //debug code start
+            System.err.println("  blockListIndex = " + blockListIndex);
+            String pagina = (blockList.startOn == BlockSequence.ANY_PAGE) ? "any page"
+                    : (blockList.startOn == BlockSequence.ODD_PAGE) ? "odd page"
+                            : "even page";
+            System.err.println("  sequence starts on " + pagina);
+            logBlocklist(blockList);
+            //debug code end
+
+            System.out.println("PLM> start of algorithm (" + this.getClass().getName() 
+                    + "), flow BPD =" + flowBPD);
+            PageBreakingAlgorithm alg = new PageBreakingAlgorithm(getTopLevelLM(),
+                    alignment, alignmentLast);
+            int iOptPageNumber;
+
+            KnuthSequence effectiveList;
+            if (alignment == Constants.EN_JUSTIFY) {
+                /* ALLINEAMENTO GIUSTIFICATO */
+                effectiveList = justifyBoxes(blockList, alg, flowBPD);
+            } else {
+                /* ALLINEAMENTO NON GIUSTIFICATO */
+                effectiveList = blockList;
+            }
+
+            //iOptPageNumber = alg.firstFit(effectiveList, flowBPD, 1, true);
+            iOptPageNumber = alg.findBreakingPoints(effectiveList, flowBPD, 1,
+                    true, true);
+            System.out.println("PLM> iOptPageNumber= " + iOptPageNumber
+                    + " pageBreaks.size()= " + alg.getPageBreaks().size());
+
+            
+            //*** Phase 3: Add areas ***
+            doPhase3(alg, iOptPageNumber, blockList, effectiveList);
+        }
+    }
+
+    /**
+     * Phase 3 of Knuth algorithm: Adds the areas 
+     * @param alg PageBreakingAlgorithm instance which determined the breaks
+     * @param partCount number of parts (pages) to be rendered
+     * @param originalList original Knuth element list
+     * @param effectiveList effective Knuth element list (after adjustments)
+     */
+    protected abstract void doPhase3(PageBreakingAlgorithm alg, int partCount, 
+            KnuthSequence originalList, KnuthSequence effectiveList);
+    
+    /**
+     * Phase 3 of Knuth algorithm: Adds the areas 
+     * @param alg PageBreakingAlgorithm instance which determined the breaks
+     * @param partCount number of parts (pages) to be rendered
+     * @param originalList original Knuth element list
+     * @param effectiveList effective Knuth element list (after adjustments)
+     */
+    protected void addAreas(PageBreakingAlgorithm alg, int partCount, 
+            KnuthSequence originalList, KnuthSequence effectiveList) {
+        LayoutContext childLC;
+        // add areas
+        ListIterator effectiveListIterator = effectiveList.listIterator();
+        int startElementIndex = 0;
+        int endElementIndex = 0;
+        for (int p = 0; p < partCount; p++) {
+            int displayAlign = getCurrentDisplayAlign();
+            
+            PageBreakPosition pbp = (PageBreakPosition) alg.getPageBreaks().get(p);
+            endElementIndex = pbp.getLeafPos();
+            System.out.println("PLM> pagina: " + (p + 1)
+                    + ", break alla posizione " + endElementIndex);
+
+            // ignore the first elements added by the
+            // PageSequenceLayoutManager
+            startElementIndex += (startElementIndex == 0) 
+                    ? effectiveList.ignoreAtStart
+                    : 0;
+
+            // ignore the last elements added by the
+            // PageSequenceLayoutManager
+            endElementIndex -= (endElementIndex == (originalList.size() - 1)) 
+                    ? effectiveList.ignoreAtEnd
+                    : 0;
+
+            // ignore the last element in the page if it is a KnuthGlue
+            // object
+            if (((KnuthElement) effectiveList.get(endElementIndex))
+                    .isGlue()) {
+                endElementIndex--;
+            }
+
+            // ignore KnuthGlue and KnuthPenalty objects
+            // at the beginning of the line
+            effectiveListIterator = effectiveList
+                    .listIterator(startElementIndex);
+            while (effectiveListIterator.hasNext()
+                    && !((KnuthElement) effectiveListIterator.next())
+                            .isBox()) {
+                startElementIndex++;
+            }
+
+            if (startElementIndex <= endElementIndex) {
+                System.out.println("     addAreas da " + startElementIndex
+                        + " a " + endElementIndex);
+                childLC = new LayoutContext(0);
+                // add space before if display-align is center or bottom
+                // add space after if display-align is distribute and
+                // this is not the last page
+                if (pbp.difference != 0 && displayAlign == Constants.EN_CENTER) {
+                    childLC.setSpaceBefore(pbp.difference / 2);
+                } else if (pbp.difference != 0 && displayAlign == Constants.EN_AFTER) {
+                    childLC.setSpaceBefore(pbp.difference);
+                } else if (pbp.difference != 0 && displayAlign == Constants.EN_X_DISTRIBUTE
+                        && p < (partCount - 1)) {
+                    // count the boxes whose width is not 0
+                    int boxCount = 0;
+                    effectiveListIterator = effectiveList
+                            .listIterator(startElementIndex);
+                    while (effectiveListIterator.nextIndex() <= endElementIndex) {
+                        KnuthElement tempEl = (KnuthElement)effectiveListIterator.next();
+                        if (tempEl.isBox() && tempEl.getW() > 0) {
+                            boxCount++;
+                        }
+                    }
+                    // split the difference
+                    if (boxCount >= 2) {
+                        childLC.setSpaceAfter(pbp.difference / (boxCount - 1));
+                    }
+                }
+
+                /* *** *** non-standard extension *** *** */
+                if (displayAlign == Constants.EN_X_FILL) {
+                    int averageLineLength = optimizeLineLength(effectiveList, startElementIndex, endElementIndex);
+                    if (averageLineLength != 0) {
+                        childLC.setStackLimit(new MinOptMax(averageLineLength));
+                    }
+                }
+                /* *** *** non-standard extension *** *** */
+
+                addAreas(new KnuthPossPosIter(effectiveList,
+                        startElementIndex, endElementIndex + 1), childLC);
+            }
+
+            finishPart();
+
+            startElementIndex = pbp.getLeafPos() + 1;
+        }
+    }
+    protected abstract void finishPart();
+    
+    protected abstract LinkedList getNextKnuthElements(LayoutContext context, int alignment);
+    
+    /**
+     * Gets the next block list (sequence) and adds it to a list of block lists if it's not empty.
+     * @param childLC LayoutContext to use
+     * @param nextSequenceStartsOn indicates on what page the next sequence should start
+     * @param blockLists list of block lists (sequences)
+     * @return the page on which the next content should appear after a hard break
+     */
+    private int getNextBlockList(LayoutContext childLC, int nextSequenceStartsOn, List blockLists) {
+        LinkedList returnedList;
+        BlockSequence blockList;
+        if ((returnedList = getNextKnuthElements(childLC, alignment)) != null) {
+            blockList = new BlockSequence(nextSequenceStartsOn);
+            if (((KnuthElement) returnedList.getLast()).isPenalty()
+                    && ((KnuthPenalty) returnedList.getLast()).getP() == -KnuthElement.INFINITE) {
+                KnuthPenalty breakPenalty = (KnuthPenalty) returnedList
+                        .removeLast();
+                switch (breakPenalty.getBreakClass()) {
+                case Constants.EN_PAGE:
+                    System.err.println("PLM> break - PAGE");
+                    nextSequenceStartsOn = BlockSequence.ANY_PAGE;
+                    break;
+                case Constants.EN_ODD_PAGE:
+                    System.err.println("PLM> break - ODD PAGE");
+                    nextSequenceStartsOn = BlockSequence.ODD_PAGE;
+                    break;
+                case Constants.EN_EVEN_PAGE:
+                    System.err.println("PLM> break - EVEN PAGE");
+                    nextSequenceStartsOn = BlockSequence.EVEN_PAGE;
+                    break;
+                default:
+                    throw new IllegalStateException("Invalid break class: " 
+                            + breakPenalty.getBreakClass());
+                }
+            }
+            blockList.addAll(returnedList);
+            BlockSequence seq = null;
+            seq = blockList.endBlockSequence();
+            if (seq != null) {
+                blockLists.add(seq);
+            }
+        }
+        return nextSequenceStartsOn;
+    }
+
+    /**
+     * @param effectiveList effective block list to work on
+     * @param startElementIndex
+     * @param endElementIndex
+     * @return the average line length, 0 if there's no content
+     */
+    private int optimizeLineLength(KnuthSequence effectiveList, int startElementIndex, int endElementIndex) {
+        ListIterator effectiveListIterator;
+        // optimize line length
+        //System.out.println(" ");
+        int boxCount = 0;
+        int accumulatedLineLength = 0;
+        int greatestMinimumLength = 0;
+        effectiveListIterator = effectiveList
+                .listIterator(startElementIndex);
+        while (effectiveListIterator.nextIndex() <= endElementIndex) {
+            KnuthElement tempEl = (KnuthElement) effectiveListIterator
+                    .next();
+            if (tempEl instanceof KnuthBlockBox) {
+                KnuthBlockBox blockBox = (KnuthBlockBox) tempEl;
+                if (blockBox.getBPD() > 0) {
+                    log
+                            .debug("PSLM> lunghezza grezza della riga = "
+                                    + blockBox.getBPD());
+                    log.debug("      range = "
+                            + blockBox.getIPDRange());
+                    boxCount++;
+                    accumulatedLineLength += ((KnuthBlockBox) tempEl)
+                            .getBPD();
+                }
+                if (blockBox.getIPDRange().min > greatestMinimumLength) {
+                    greatestMinimumLength = blockBox
+                            .getIPDRange().min;
+                }
+            }
+        }
+        int averageLineLength = 0;
+        if (accumulatedLineLength > 0 && boxCount > 0) {
+            averageLineLength = (int) (accumulatedLineLength / boxCount);
+            //System.out.println("PSLM> lunghezza media = " + averageLineLength);
+            if (averageLineLength < greatestMinimumLength) {
+                averageLineLength = greatestMinimumLength;
+                //System.out.println("      correzione, ora e' = " + averageLineLength);
+            }
+        }
+        return averageLineLength;
+    }
+
+    /**
+     * Justifies the boxes and returns them as a new KnuthSequence.
+     * @param blockList block list to justify
+     * @param alg reference to the algorithm instance
+     * @param availableBPD the available BPD 
+     * @return the effective list
+     */
+    private KnuthSequence justifyBoxes(BlockSequence blockList, PageBreakingAlgorithm alg, int availableBPD) {
+        int iOptPageNumber;
+        iOptPageNumber = alg.findBreakingPoints(blockList, availableBPD, 1,
+                true, true);
+        System.out.println("PLM> iOptPageNumber= " + iOptPageNumber);
+
+        // 
+        ListIterator sequenceIterator = blockList.listIterator();
+        ListIterator breakIterator = alg.getPageBreaks().listIterator();
+        KnuthElement thisElement = null;
+        PageBreakPosition thisBreak;
+        int accumulatedS; // accumulated stretch or shrink
+        int adjustedDiff; // difference already adjusted
+        int firstElementIndex;
+
+        while (breakIterator.hasNext()) {
+            thisBreak = (PageBreakPosition) breakIterator.next();
+            System.out.println("| first page: break= "
+                    + thisBreak.getLeafPos() + " difference= "
+                    + thisBreak.difference + " ratio= "
+                    + thisBreak.bpdAdjust);
+            accumulatedS = 0;
+            adjustedDiff = 0;
+
+            // glue and penalty items at the beginning of the page must
+            // be ignored:
+            // the first element returned by sequenceIterator.next()
+            // inside the
+            // while loop must be a box
+            KnuthElement firstElement;
+            while (!(firstElement = (KnuthElement) sequenceIterator
+                    .next()).isBox()) {
+                // 
+                System.out
+                        .println("PLM> ignoring glue or penalty element at the beginning of the sequence");
+                if (firstElement.isGlue()) {
+                    ((BlockLevelLayoutManager) firstElement
+                            .getLayoutManager())
+                            .discardSpace((KnuthGlue) firstElement);
+                }
+            }
+            firstElementIndex = sequenceIterator.previousIndex();
+            sequenceIterator.previous();
+
+            // scan the sub-sequence representing a page,
+            // collecting information about potential adjustments
+            MinOptMax lineNumberMaxAdjustment = new MinOptMax(0);
+            MinOptMax spaceMaxAdjustment = new MinOptMax(0);
+            double spaceAdjustmentRatio = 0.0;
+            LinkedList blockSpacesList = new LinkedList();
+            LinkedList unconfirmedList = new LinkedList();
+            LinkedList adjustableLinesList = new LinkedList();
+            boolean bBoxSeen = false;
+            while (sequenceIterator.hasNext()
+                    && sequenceIterator.nextIndex() <= thisBreak
+                            .getLeafPos()) {
+                thisElement = (KnuthElement) sequenceIterator.next();
+                if (thisElement.isGlue()) {
+                    // glue elements are used to represent adjustable
+                    // lines
+                    // and adjustable spaces between blocks
+                    switch (((KnuthGlue) thisElement)
+                            .getAdjustmentClass()) {
+                    case BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT:
+                    // fall through
+                    case BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT:
+                        // potential space adjustment
+                        // glue items before the first box or after the
+                        // last one
+                        // must be ignored
+                        unconfirmedList.add(thisElement);
+                        break;
+                    case BlockLevelLayoutManager.LINE_NUMBER_ADJUSTMENT:
+                        // potential line number adjustment
+                        lineNumberMaxAdjustment.max += ((KnuthGlue) thisElement)
+                                .getY();
+                        lineNumberMaxAdjustment.min -= ((KnuthGlue) thisElement)
+                                .getZ();
+                        adjustableLinesList.add(thisElement);
+                        break;
+                    case BlockLevelLayoutManager.LINE_HEIGHT_ADJUSTMENT:
+                        // potential line height adjustment
+                        break;
+                    default:
+                    // nothing
+                    }
+                } else if (thisElement.isBox()) {
+                    if (!bBoxSeen) {
+                        // this is the first box met in this page
+                        bBoxSeen = true;
+                    } else if (unconfirmedList.size() > 0) {
+                        // glue items in unconfirmedList were not after
+                        // the last box
+                        // in this page; they must be added to
+                        // blockSpaceList
+                        while (unconfirmedList.size() > 0) {
+                            KnuthGlue blockSpace = (KnuthGlue) unconfirmedList
+                                    .removeFirst();
+                            spaceMaxAdjustment.max += ((KnuthGlue) blockSpace)
+                                    .getY();
+                            spaceMaxAdjustment.min -= ((KnuthGlue) blockSpace)
+                                    .getZ();
+                            blockSpacesList.add(blockSpace);
+                        }
+                    }
+                }
+            }
+            System.out.println("| line number adj= "
+                    + lineNumberMaxAdjustment);
+            System.out.println("| space adj      = "
+                    + spaceMaxAdjustment);
+
+            if (thisElement.isPenalty() && thisElement.getW() > 0) {
+                System.out
+                        .println("  variazione obbligatoria al numero di righe!");
+                ((BlockLevelLayoutManager) thisElement
+                        .getLayoutManager()).negotiateBPDAdjustment(
+                        thisElement.getW(), thisElement);
+            }
+
+            if (thisBreak.bpdAdjust != 0
+                    && (thisBreak.difference > 0 && thisBreak.difference <= spaceMaxAdjustment.max)
+                    || (thisBreak.difference < 0 && thisBreak.difference >= spaceMaxAdjustment.min)) {
+                // modify only the spaces between blocks
+                spaceAdjustmentRatio = ((double) thisBreak.difference / (thisBreak.difference > 0 ? spaceMaxAdjustment.max
+                        : spaceMaxAdjustment.min));
+                adjustedDiff += adjustBlockSpaces(
+                        blockSpacesList,
+                        thisBreak.difference,
+                        (thisBreak.difference > 0 ? spaceMaxAdjustment.max
+                                : -spaceMaxAdjustment.min));
+                System.out.println("solo spazi: "
+                        + (adjustedDiff == thisBreak.difference
+                                || thisBreak.bpdAdjust == 0 ? "ok"
+                                : "ERRORE"));
+            } else if (thisBreak.bpdAdjust != 0) {
+                adjustedDiff += adjustLineNumbers(
+                        adjustableLinesList,
+                        thisBreak.difference,
+                        (thisBreak.difference > 0 ? lineNumberMaxAdjustment.max
+                                : -lineNumberMaxAdjustment.min));
+                adjustedDiff += adjustBlockSpaces(
+                        blockSpacesList,
+                        thisBreak.difference - adjustedDiff,
+                        ((thisBreak.difference - adjustedDiff) > 0 ? spaceMaxAdjustment.max
+                                : -spaceMaxAdjustment.min));
+                System.out.println("linee e spazi: "
+                        + (adjustedDiff == thisBreak.difference
+                                || thisBreak.bpdAdjust == 0 ? "ok"
+                                : "ERRORE"));
+
+            }
+        }
+
+        // create a new sequence: the new elements will contain the
+        // Positions
+        // which will be used in the addAreas() phase
+        KnuthSequence effectiveList = new KnuthSequence();
+        effectiveList.addAll(getCurrentChildLM().getChangedKnuthElements(
+                blockList.subList(0, blockList.size() - blockList.ignoreAtEnd),
+                /* 0, */0));
+        //effectiveList.add(new KnuthPenalty(0, -KnuthElement.INFINITE,
+        // false, new Position(this), false));
+        effectiveList.endSequence();
+
+        logEffectiveList(effectiveList);
+
+        alg.getPageBreaks().clear(); //Why this?
+        return effectiveList;
+    }
+
+    /**
+     * Logs the contents of a block list for debugging purposes
+     * @param blockList block list to log
+     */
+    private void logBlocklist(BlockSequence blockList) {
+        ListIterator tempIter = blockList.listIterator();
+
+        KnuthElement temp;
+        System.out.println(" ");
+        while (tempIter.hasNext()) {
+            temp = (KnuthElement) tempIter.next();
+            String segno = temp.isAuxiliary() ? "+ " : "- ";
+            if (temp.isBox()) {
+                System.out.println(segno + tempIter.previousIndex()
+                        + ")  <box> " + temp.getW());
+            } else if (temp.isGlue()) {
+                System.out.println(segno + tempIter.previousIndex()
+                        + ")  <glue> " + temp.getW() + " + "
+                        + ((KnuthGlue) temp).getY() + " - "
+                        + ((KnuthGlue) temp).getZ());
+            } else {
+                System.out
+                        .println(segno
+                                + tempIter.previousIndex()
+                                + ")  <"
+                                + (((KnuthPenalty) temp).getP() == KnuthElement.INFINITE ? "PENALTY"
+                                        : "penalty") + "> " + temp.getW());
+            }
+        }
+        System.out.println(" ");
+    }
+
+    /**
+     * Logs the contents of an effective block list for debugging purposes
+     * @param effectiveList block list to log
+     * @todo combine with logBlocklist()
+     */
+    private void logEffectiveList(KnuthSequence effectiveList) {
+        ListIterator tempIter;
+        KnuthElement temp;
+        System.out.println("Lista effettiva");
+        System.out.println(" ");
+        tempIter = effectiveList.listIterator();
+        System.out.println(" ");
+        while (tempIter.hasNext()) {
+            temp = (KnuthElement) tempIter.next();
+            String segno = temp.isAuxiliary() ? "+ " : "- ";
+            if (temp.isBox()) {
+                System.out.println(segno + tempIter.previousIndex()
+                        + ")  <box> - - " + ((KnuthBox) temp).getW());
+            } else if (temp.isGlue()) {
+                System.out
+                        .println(segno
+                                + tempIter.previousIndex()
+                                + ")  <glue> "
+                                + (((KnuthGlue) temp).getW() - ((KnuthGlue) temp)
+                                        .getZ())
+                                + " - "
+                                + ((KnuthGlue) temp).getW()
+                                + " - "
+                                + (((KnuthGlue) temp).getW() + ((KnuthGlue) temp)
+                                        .getY()));
+            } else {
+                System.out
+                        .println(segno
+                                + tempIter.previousIndex()
+                                + ")  <"
+                                + (((KnuthPenalty) temp).getP() == KnuthElement.INFINITE ? "PENALTY"
+                                        : "penalty") + "> ");
+            }
+        }
+        System.out.println(" ");
+    }
+
+    private int adjustBlockSpaces(LinkedList spaceList, int difference, int total) {
+    /*LF*/  System.out.println("AdjustBlockSpaces: ripartire " + difference + " / " + total + " su " + spaceList.size() + " spazi fra blocchi");
+            ListIterator spaceListIterator = spaceList.listIterator();
+            int adjustedDiff = 0;
+            int partial = 0;
+            while (spaceListIterator.hasNext()) {
+                KnuthGlue blockSpace = (KnuthGlue)spaceListIterator.next();
+                partial += (difference > 0 ? blockSpace.getY() : blockSpace.getZ());
+                System.out.println("disponibile = " + partial +  " / " + total);
+                System.out.println("competenza  = " + (((int) ((float) partial * difference / total)) - adjustedDiff) + " / " + difference);
+                int newAdjust = ((BlockLevelLayoutManager) blockSpace.getLayoutManager()).negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, blockSpace);
+                adjustedDiff += newAdjust;
+            }
+            return adjustedDiff;
+        }
+
+    private int adjustLineNumbers(LinkedList lineList, int difference, int total) {
+    /*LF*/  System.out.println("AdjustLineNumbers: ripartire " + difference + " / " + total + " su " + lineList.size() + " elementi");
+
+//            int adjustedDiff = 0;
+//            int partial = 0;
+//            KnuthGlue prevLine = null;
+//            KnuthGlue currLine = null;
+//            ListIterator lineListIterator = lineList.listIterator();
+//            while (lineListIterator.hasNext()) {
+//                currLine = (KnuthGlue)lineListIterator.next();
+//                if (prevLine != null
+//                    && prevLine.getLayoutManager() != currLine.getLayoutManager()) {
+//                    int newAdjust = ((BlockLevelLayoutManager) prevLine.getLayoutManager())
+//                                    .negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, prevLine);
+//                    adjustedDiff += newAdjust;
+//                }
+//                partial += (difference > 0 ? currLine.getY() : currLine.getZ());
+//                prevLine = currLine;
+//            }
+//            if (currLine != null) {
+//                int newAdjust = ((BlockLevelLayoutManager) currLine.getLayoutManager())
+//                                .negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, currLine);
+//                adjustedDiff += newAdjust;
+//            }
+//            return adjustedDiff;
+
+            ListIterator lineListIterator = lineList.listIterator();
+            int adjustedDiff = 0;
+            int partial = 0;
+            while (lineListIterator.hasNext()) {
+                KnuthGlue line = (KnuthGlue)lineListIterator.next();
+                partial += (difference > 0 ? line.getY() : line.getZ());
+                int newAdjust = ((BlockLevelLayoutManager) line.getLayoutManager()).negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, line);
+                adjustedDiff += newAdjust;
+            }
+            return adjustedDiff;
+        }
+
+    
+}
index 9456b58e38dddfee1e1427d592f9b5a8ffe32979..7c6030b84e906d5c5322700b2f1512ee1da2794d 100644 (file)
@@ -31,6 +31,7 @@ import org.apache.fop.fo.flow.Marker;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
+import java.util.LinkedList;
 import java.util.List;
 import java.util.ArrayList;
 import java.util.ListIterator;
@@ -296,6 +297,43 @@ public abstract class AbstractLayoutManager implements LayoutManager, Constants
      * interface which are declared abstract in AbstractLayoutManager.
      * ---------------------------------------------------------*/
 
+    public LinkedList getNextKnuthElements(LayoutContext context,
+                                           int alignment) {
+        log.warn("null implementation of getNextKnuthElements() called!");
+        setFinished(true);
+        return null;
+    }
+
+    public KnuthElement addALetterSpaceTo(KnuthElement element) {
+        log.warn("null implementation of addALetterSpaceTo() called!");
+        return element;
+    }
+
+    public void getWordChars(StringBuffer sbChars, Position pos) {
+        log.warn("null implementation of getWordChars() called!");
+    }
+
+    public void hyphenate(Position pos, HyphContext hc) {
+        log.warn("null implementation of hyphenate called!");
+    }
+
+    public boolean applyChanges(List oldList) {
+        log.warn("null implementation of applyChanges() called!");
+        return false;
+    }
+
+    public LinkedList getChangedKnuthElements(List oldList,
+                                              /*int flaggedPenalty,*/
+                                              int alignment) {
+        log.warn("null implementation of getChangeKnuthElement() called!");
+        return null;
+    }
+
+    public int getWordSpaceIPD() {
+        log.warn("null implementation of getWordSpaceIPD() called!");
+        return 0;
+    }
+
     /**
      * @see org.apache.fop.layoutmgr.LayoutManager#getParentArea(org.apache.fop.area.Area)
      */
diff --git a/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java b/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java
new file mode 100644 (file)
index 0000000..57c1dba
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+
+public class AreaAdditionUtil {
+
+    private static class StackingIter extends PositionIterator {
+        StackingIter(Iterator parentIter) {
+            super(parentIter);
+        }
+
+        protected LayoutManager getLM(Object nextObj) {
+            return ((Position) nextObj).getLM();
+        }
+
+        protected Position getPos(Object nextObj) {
+            return ((Position) nextObj);
+        }
+    }
+
+    public static void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
+        LayoutManager childLM = null;
+        LayoutContext lc = new LayoutContext(0);
+        LayoutManager firstLM = null;
+        LayoutManager lastLM = null;
+
+        // "unwrap" the NonLeafPositions stored in parentIter
+        // and put them in a new list; 
+        LinkedList positionList = new LinkedList();
+        Position pos;
+        while (parentIter.hasNext()) {
+            pos = (Position)parentIter.next();
+            if (pos instanceof NonLeafPosition) {
+                // pos was created by a child of this FlowLM
+                positionList.add(((NonLeafPosition) pos).getPosition());
+                lastLM = ((NonLeafPosition) pos).getPosition().getLM();
+                if (firstLM == null) {
+                    firstLM = lastLM;
+                }
+            } else {
+                // pos was created by this LM, so it must be ignored
+            }
+        }
+
+        StackingIter childPosIter = new StackingIter(positionList.listIterator());
+        while ((childLM = childPosIter.getNextChildLM()) != null) {
+            // Add the block areas to Area
+            lc.setFlags(LayoutContext.FIRST_AREA, childLM == firstLM);
+            lc.setFlags(LayoutContext.LAST_AREA, childLM == lastLM);
+            // set space before for the first LM, in order to implement
+            // display-align = center or after
+            lc.setSpaceBefore((childLM == firstLM ? layoutContext.getSpaceBefore() : 0));
+            // set space after for each LM, in order to implement
+            // display-align = distribute
+            lc.setSpaceAfter(layoutContext.getSpaceAfter());
+            lc.setStackLimit(layoutContext.getStackLimit());
+            childLM.addAreas(childPosIter, lc);
+        }
+    }
+    
+}
index 36d3389430afca422229d742b5b91c6751e7504b..cc05436ecba61f60f16ff760820b2b9f9346d7a9 100644 (file)
@@ -18,7 +18,9 @@
 
 package org.apache.fop.layoutmgr;
 
+import java.util.LinkedList;
 import java.util.List;
+import java.util.ListIterator;
 import java.awt.Point;
 import java.awt.geom.Rectangle2D;
 
@@ -27,6 +29,7 @@ import org.apache.fop.area.BlockViewport;
 import org.apache.fop.area.Block;
 import org.apache.fop.area.PageViewport;
 import org.apache.fop.area.Trait;
+import org.apache.fop.fo.Constants;
 import org.apache.fop.fo.flow.BlockContainer;
 import org.apache.fop.fo.properties.CommonAbsolutePosition;
 import org.apache.fop.area.CTM;
@@ -39,7 +42,8 @@ import org.apache.fop.traits.SpaceVal;
 /**
  * LayoutManager for a block-container FO.
  */
-public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
+public class BlockContainerLayoutManager extends BlockStackingLayoutManager
+                implements BlockLevelLayoutManager {
     private BlockContainer fobj;
     
     private BlockViewport viewportBlockArea;
@@ -56,7 +60,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
     private int vpContentIPD;
     private int vpContentBPD;
     private int usedBPD;
-
+    
     // When viewport should grow with the content.
     private boolean autoHeight = true; 
 
@@ -73,6 +77,15 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
     //TODO space-before|after: handle space-resolution rules
     private MinOptMax foBlockSpaceBefore;
     private MinOptMax foBlockSpaceAfter;
+
+    private boolean bBreakBeforeServed = false;
+    private boolean bSpaceBeforeServed = false;
+
+    /*LF*/
+    /** Only used to store the original list when createUnitElements is called */
+    //TODO Maybe pull up as protected member if also used in this class (JM)
+    private LinkedList storedList = null;
+    
     
     /**
      * Create a new block container layout manager.
@@ -114,6 +127,17 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
             height = fobj.getBlockProgressionDimension().getOptimum().getLength();
             width = fobj.getInlineProgressionDimension().getOptimum().getLength();
         }
+        
+/*LF*/  bpUnit = 0; //layoutProps.blockProgressionUnit;
+/*LF*/  if (bpUnit == 0) {
+/*LF*/      // use optimum space values
+/*LF*/      adjustedSpaceBefore = fobj.getCommonMarginBlock().spaceBefore.getSpace().getOptimum().getLength().getValue();
+/*LF*/      adjustedSpaceAfter = fobj.getCommonMarginBlock().spaceAfter.getSpace().getOptimum().getLength().getValue();
+/*LF*/  } else {
+/*LF*/      // use minimum space values
+/*LF*/      adjustedSpaceBefore = fobj.getCommonMarginBlock().spaceBefore.getSpace().getMinimum().getLength().getValue();
+/*LF*/      adjustedSpaceAfter = fobj.getCommonMarginBlock().spaceAfter.getSpace().getMinimum().getLength().getValue();
+/*LF*/  }
     }
 
     /** @return the content IPD */
@@ -149,6 +173,385 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
         return (abProps.absolutePosition == EN_FIXED);
     }
     
+    /**
+     * @see org.apache.fop.layoutmgr.LayoutManager#getNextKnuthElements(org.apache.fop.layoutmgr.LayoutContext, int)
+     */
+    public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+        if (isAbsoluteOrFixed()) {
+            return getNextKnuthElementsAbsolute(context, alignment);
+        }
+        
+        autoHeight = false;
+        boolean rotated = (fobj.getReferenceOrientation() % 180 != 0); //vals[0] == 0.0;
+        referenceIPD = context.getRefIPD();
+        int maxbpd = context.getStackLimit().opt;
+        int allocBPD, allocIPD;
+        if (height.getEnum() != EN_AUTO) {
+            allocBPD = height.getValue(); //this is the content-height
+            allocBPD += getBPIndents();
+        } else {
+            allocBPD = maxbpd;
+            autoHeight = true;
+        }
+        if (width.getEnum() != EN_AUTO) {
+            allocIPD = width.getValue(); //this is the content-width
+            allocIPD += getIPIndents();
+        } else {
+            allocIPD = referenceIPD;
+        }
+
+        vpContentBPD = allocBPD - getBPIndents();
+        vpContentIPD = allocIPD - getIPIndents();
+        
+        double contentRectOffsetX = 0;
+        contentRectOffsetX += fobj.getCommonMarginBlock().startIndent.getValue();
+        double contentRectOffsetY = 0;
+        //contentRectOffsetY += fobj.getCommonMarginBlock().startIndent.getValue();
+        //contentRectOffsetY += getSpaceBefore();
+        contentRectOffsetY += fobj.getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
+        contentRectOffsetY += fobj.getCommonBorderPaddingBackground().getPaddingBefore(false);
+        
+        Rectangle2D rect = new Rectangle2D.Double(
+                contentRectOffsetX, contentRectOffsetY, 
+                vpContentIPD, vpContentBPD);
+        relDims = new FODimension(0, 0);
+        absoluteCTM = CTM.getCTMandRelDims(fobj.getReferenceOrientation(),
+                fobj.getWritingMode(), rect, relDims);
+
+        MinOptMax stackLimit = new MinOptMax(relDims.bpd);
+
+        LinkedList returnedList = null;
+        LinkedList contentList = new LinkedList();
+        LinkedList returnList = new LinkedList();
+        Position returnPosition = new NonLeafPosition(this, null);
+        
+        if (!bBreakBeforeServed) {
+            try {
+                if (addKnuthElementsForBreakBefore(returnList, returnPosition, 
+                        fobj.getBreakBefore())) {
+                    return returnList;
+                }
+            } finally {
+                bBreakBeforeServed = true;
+            }
+        }
+
+        if (!bSpaceBeforeServed) {
+            addKnuthElementsForSpaceBefore(returnList, returnPosition, alignment, 
+                    fobj.getCommonMarginBlock().spaceBefore);
+            bSpaceBeforeServed = true;
+        }
+        
+        addKnuthElementForBorderPaddingBefore(returnList, returnPosition, 
+                fobj.getCommonBorderPaddingBackground());
+
+        if (autoHeight) {
+            BlockLevelLayoutManager curLM; // currently active LM
+            BlockLevelLayoutManager prevLM = null; // previously active LM
+            while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) {
+                LayoutContext childLC = new LayoutContext(0);
+                // curLM is a ?
+                childLC.setStackLimit(MinOptMax.subtract(context
+                        .getStackLimit(), stackLimit));
+                childLC.setRefIPD(relDims.ipd);
+
+                // get elements from curLM
+                returnedList = curLM.getNextKnuthElements(childLC, alignment);
+                if (returnedList.size() == 1
+                        && ((KnuthElement) returnedList.getFirst()).isPenalty()
+                        && ((KnuthPenalty) returnedList.getFirst()).getP() == -KnuthElement.INFINITE) {
+                    // a descendant of this block has break-before
+                    if (returnList.size() == 0) {
+                        // the first child (or its first child ...) has
+                        // break-before;
+                        // all this block, including space before, will be put in
+                        // the
+                        // following page
+                        bSpaceBeforeServed = false;
+                    }
+                    contentList.addAll(returnedList);
+
+                    // "wrap" the Position inside each element
+                    // moving the elements from contentList to returnList
+                    returnedList = new LinkedList();
+                    wrapPositionElements(contentList, returnList);
+
+                    return returnList;
+                } else {
+                    if (prevLM != null) {
+                        // there is a block handled by prevLM
+                        // before the one handled by curLM
+                        if (mustKeepTogether() 
+                                || prevLM.mustKeepWithNext()
+                                || curLM.mustKeepWithPrevious()) {
+                            // add an infinite penalty to forbid a break between
+                            // blocks
+                            contentList.add(new KnuthPenalty(0,
+                                    KnuthElement.INFINITE, false,
+                                    new Position(this), false));
+                        } else if (!((KnuthElement) contentList.getLast()).isGlue()) {
+                            // add a null penalty to allow a break between blocks
+                            contentList.add(new KnuthPenalty(0, 0, false,
+                                    new Position(this), false));
+                        } else {
+                            // the last element in contentList is a glue;
+                            // it is a feasible breakpoint, there is no need to add
+                            // a penalty
+                        }
+                    }
+                    contentList.addAll(returnedList);
+                    if (returnedList.size() == 0) {
+                        //Avoid NoSuchElementException below (happens with empty blocks)
+                        continue;
+                    }
+                    if (((KnuthElement) returnedList.getLast()).isPenalty()
+                            && ((KnuthPenalty) returnedList.getLast()).getP() == -KnuthElement.INFINITE) {
+                        // a descendant of this block has break-after
+                        if (curLM.isFinished()) {
+                            // there is no other content in this block;
+                            // it's useless to add space after before a page break
+                            setFinished(true);
+                        }
+
+                        returnedList = new LinkedList();
+                        wrapPositionElements(contentList, returnList);
+
+                        return returnList;
+                    }
+                }
+                prevLM = curLM;
+            }
+
+            returnedList = new LinkedList();
+            wrapPositionElements(contentList, returnList);
+
+        } else {
+            MinOptMax range = new MinOptMax(relDims.ipd);
+            BlockContainerBreaker breaker = new BlockContainerBreaker(this, range);
+            breaker.doLayout(relDims.bpd);
+            boolean contentOverflows = (breaker.deferredAlg.getPageBreaks().size() > 1);
+
+            Position bcPosition = new BlockContainerPosition(this, breaker);
+            returnList.add(new KnuthBox(vpContentBPD, bcPosition, false));
+            //TODO Handle min/opt/max for block-progression-dimension
+            /* These two elements will be used to add stretchability to the above box
+            returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, returnPosition, false));
+            returnList.add(new KnuthGlue(0, 1 * constantLineHeight, 0,
+                                   LINE_NUMBER_ADJUSTMENT, returnPosition, false));
+            */
+
+            if (contentOverflows) {
+                log.warn("Contents overflow block-container viewport: clipping");
+                if (fobj.getOverflow() == EN_HIDDEN) {
+                    clip = true;
+                } else if (fobj.getOverflow() == EN_ERROR_IF_OVERFLOW) {
+                    //TODO Throw layout exception
+                    clip = true;
+                }
+            }
+        }
+        addKnuthElementsForBorderPaddingAfter(returnList, returnPosition, 
+                fobj.getCommonBorderPaddingBackground());
+        addKnuthElementsForSpaceAfter(returnList, returnPosition, alignment, 
+                fobj.getCommonMarginBlock().spaceAfter);
+        addKnuthElementsForBreakAfter(returnList, returnPosition, fobj.getBreakAfter());
+
+        setFinished(true);
+        return returnList;
+    }
+    
+    private LinkedList getNextKnuthElementsAbsolute(LayoutContext context, int alignment) {
+        MinOptMax stackSize = new MinOptMax();
+        autoHeight = false;
+
+        Point offset = getAbsOffset();
+        int allocBPD, allocIPD;
+        if (height.getEnum() != EN_AUTO) {
+            allocBPD = height.getValue(); //this is the content-height
+            allocBPD += getBPIndents();
+        } else {
+            allocBPD = 0;
+            if (abProps.bottom.getEnum() != EN_AUTO) {
+                if (isFixed()) {
+                    allocBPD = (int)getPageViewport().getViewArea().getHeight();
+                } else {
+                    allocBPD = context.getStackLimit().opt; 
+                }
+                allocBPD -= offset.y;
+                if (abProps.bottom.getEnum() != EN_AUTO) {
+                    allocBPD -= abProps.bottom.getValue();
+                }
+            } else {
+                autoHeight = true;
+            }
+        }
+        if (width.getEnum() != EN_AUTO) {
+            allocIPD = width.getValue(); //this is the content-width
+            allocIPD += getIPIndents();
+        } else {
+            if (isFixed()) {
+                allocIPD = (int)getPageViewport().getViewArea().getWidth(); 
+            } else {
+                allocIPD = context.getRefIPD();
+            }
+            if (abProps.left.getEnum() != EN_AUTO) {
+                allocIPD -= abProps.left.getValue();
+            }
+            if (abProps.right.getEnum() != EN_AUTO) {
+                allocIPD -= abProps.right.getValue();
+            }
+        }
+
+        vpContentBPD = allocBPD - getBPIndents();
+        vpContentIPD = allocIPD - getIPIndents();
+        
+        double contentRectOffsetX = offset.getX();
+        contentRectOffsetX += fobj.getCommonMarginBlock().startIndent.getValue();
+        double contentRectOffsetY = offset.getY();
+        contentRectOffsetY += getSpaceBefore();
+        contentRectOffsetY += fobj.getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
+        contentRectOffsetY += fobj.getCommonBorderPaddingBackground().getPaddingBefore(false);
+        
+        Rectangle2D rect = new Rectangle2D.Double(
+                contentRectOffsetX, contentRectOffsetY, 
+                vpContentIPD, vpContentBPD);
+        relDims = new FODimension(0, 0);
+        absoluteCTM = CTM.getCTMandRelDims(
+                fobj.getReferenceOrientation(),
+                fobj.getWritingMode(), 
+                rect, relDims);
+
+        MinOptMax range = new MinOptMax(relDims.ipd);
+        BlockContainerBreaker breaker = new BlockContainerBreaker(this, range);
+        breaker.doLayout(relDims.bpd);
+        boolean contentOverflows = (breaker.deferredAlg.getPageBreaks().size() > 1);
+        usedBPD = relDims.bpd - breaker.getDifferenceOfFirstPart(); 
+
+        Position bcPosition = new BlockContainerPosition(this, breaker);
+        LinkedList returnList = new LinkedList();
+        returnList.add(new KnuthBox(0, bcPosition, false));
+
+        //TODO Maybe check for page overflow when autoHeight=true
+        if (!autoHeight & (contentOverflows/*usedBPD > relDims.bpd*/)) {
+            log.warn("Contents overflow block-container viewport: clipping");
+            if (fobj.getOverflow() == EN_HIDDEN) {
+                clip = true;
+            } else if (fobj.getOverflow() == EN_ERROR_IF_OVERFLOW) {
+                //TODO Throw layout exception
+                clip = true;
+            }
+        }
+
+        setFinished(true);
+        return returnList;
+    }
+    
+    private class BlockContainerPosition extends NonLeafPosition {
+
+        private BlockContainerBreaker breaker;
+
+        public BlockContainerPosition(LayoutManager lm, BlockContainerBreaker breaker) {
+            super(lm, null);
+            this.breaker = breaker;
+        }
+        
+        public BlockContainerBreaker getBreaker() {
+            return this.breaker;
+        }
+        
+    }
+    
+    private class BlockContainerBreaker extends AbstractBreaker {
+        
+        private BlockContainerLayoutManager bclm;
+        private MinOptMax ipd;
+        
+        //Info for deferred adding of areas
+        private PageBreakingAlgorithm deferredAlg;
+        private KnuthSequence deferredOriginalList;
+        private KnuthSequence deferredEffectiveList;
+        
+        public BlockContainerBreaker(BlockContainerLayoutManager bclm, MinOptMax ipd) {
+            this.bclm = bclm;
+            this.ipd = ipd;
+        }
+
+        public int getDifferenceOfFirstPart() {
+            PageBreakPosition pbp = (PageBreakPosition)this.deferredAlg.getPageBreaks().getFirst();
+            return pbp.difference;
+        }
+        
+        protected LayoutManager getTopLevelLM() {
+            return bclm;
+        }
+
+        protected LayoutContext createLayoutContext() {
+            LayoutContext lc = super.createLayoutContext();
+            lc.setRefIPD(ipd.opt);
+            return lc;
+        }
+        
+        protected LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+            LayoutManager curLM; // currently active LM
+            LinkedList returnList = new LinkedList();
+
+            while ((curLM = getChildLM()) != null) {
+                LayoutContext childLC = new LayoutContext(0);
+                childLC.setStackLimit(context.getStackLimit());
+                childLC.setRefIPD(context.getRefIPD());
+
+                LinkedList returnedList = null;
+                if (!curLM.isFinished()) {
+                    returnedList = curLM.getNextKnuthElements(childLC, alignment);
+                }
+                if (returnedList != null) {
+                    bclm.wrapPositionElements(returnedList, returnList);
+                    //returnList.addAll(returnedList);
+                }
+            }
+            setFinished(true);
+            return returnList;
+        }
+
+        protected int getCurrentDisplayAlign() {
+            return fobj.getDisplayAlign();
+        }
+        
+        protected boolean hasMoreContent() {
+            return !isFinished();
+        }
+        
+        protected void addAreas(PositionIterator posIter, LayoutContext context) {
+            AreaAdditionUtil.addAreas(posIter, context);    
+        }
+        
+        protected void doPhase3(PageBreakingAlgorithm alg, int partCount, 
+                KnuthSequence originalList, KnuthSequence effectiveList) {
+            //Defer adding of areas until addAreas is called by the parent LM
+            this.deferredAlg = alg;
+            this.deferredOriginalList = originalList;
+            this.deferredEffectiveList = effectiveList;
+        }
+        
+        protected void finishPart() {
+            //nop for bclm
+        }
+        
+        protected LayoutManager getCurrentChildLM() {
+            return curChildLM;
+        }
+        
+        public void addContainedAreas() {
+            //Rendering all parts (not just the first) at once for the case where the parts that 
+            //overflow should be visible.
+            //TODO Check if this has any unwanted side-effects. Feels a bit like a hack.
+            addAreas(this.deferredAlg, 
+                    /*1*/ this.deferredAlg.getPageBreaks().size(), 
+                    this.deferredOriginalList, this.deferredEffectiveList);
+        }
+        
+    }
+    
     /**
      * @see org.apache.fop.layoutmgr.LayoutManager#getNextBreakPoss(org.apache.fop.layoutmgr.LayoutContext)
      */
@@ -438,6 +841,186 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
      * @see org.apache.fop.layoutmgr.LayoutManager#addAreas(org.apache.fop.layoutmgr.PositionIterator, org.apache.fop.layoutmgr.LayoutContext)
      */
     public void addAreas(PositionIterator parentIter,
+            LayoutContext layoutContext) {
+        getParentArea(null);
+
+        // if this will create the first block area in a page
+        // and display-align is bottom or center, add space before
+        if (layoutContext.getSpaceBefore() > 0) {
+            addBlockSpacing(0.0, new MinOptMax(layoutContext.getSpaceBefore()));
+        }
+
+        addID(fobj.getId());
+        //addMarkers(true, bp1.isFirstArea(), bp1.isLastArea());
+        addMarkers(true, true, false);
+
+        LayoutManager childLM = null;
+        LayoutManager lastLM = null;
+        LayoutContext lc = new LayoutContext(0);
+        // set space after in the LayoutContext for children
+        if (layoutContext.getSpaceAfter() > 0) {
+            lc.setSpaceAfter(layoutContext.getSpaceAfter());
+        }
+        BlockContainerPosition bcpos = null;
+        PositionIterator childPosIter;
+
+        // "unwrap" the NonLeafPositions stored in parentIter
+        // and put them in a new list;
+        LinkedList positionList = new LinkedList();
+        Position pos;
+        boolean bSpaceBefore = false;
+        boolean bSpaceAfter = false;
+        while (parentIter.hasNext()) {
+            pos = (Position) parentIter.next();
+            /* LF *///System.out.println("pos = " + pos.getClass().getName());
+            Position innerPosition = ((NonLeafPosition) pos).getPosition();
+            if (pos instanceof BlockContainerPosition) {
+                if (bcpos != null) {
+                    throw new IllegalStateException("Only one BlockContainerPosition allowed");
+                }
+                bcpos = (BlockContainerPosition)pos; 
+                //Add child areas inside the reference area
+                //bcpos.getBreaker().addContainedAreas();
+            } else if (innerPosition == null) {
+                // pos was created by this BCLM and was inside an element
+                // representing space before or after
+                // this means the space was not discarded
+                if (positionList.size() == 0) {
+                    // pos was in the element representing space-before
+                    bSpaceBefore = true;
+                    /* LF *///System.out.println(" space-before");
+                } else {
+                    // pos was in the element representing space-after
+                    bSpaceAfter = true;
+                    /* LF *///System.out.println(" space-after");
+                }
+            } else if (innerPosition.getLM() == this
+                    && !(innerPosition instanceof MappingPosition)) {
+                // pos was created by this BlockLM and was inside a penalty
+                // allowing or forbidding a page break
+                // nothing to do
+                /* LF *///System.out.println(" penalty");
+            } else {
+                // innerPosition was created by another LM
+                positionList.add(innerPosition);
+                lastLM = innerPosition.getLM();
+                /* LF *///System.out.println(" " +
+                      // innerPosition.getClass().getName());
+            }
+        }
+
+        if (bcpos == null) {
+            
+        if (bpUnit == 0) {
+            // the Positions in positionList were inside the elements
+            // created by the LineLM
+            childPosIter = new StackingIter(positionList.listIterator());
+        } else {
+            // the Positions in positionList were inside the elements
+            // created by the BCLM in the createUnitElements() method
+            //if (((Position) positionList.getLast()) instanceof
+                  // LeafPosition) {
+            //    // the last item inside positionList is a LeafPosition
+            //    // (a LineBreakPosition, more precisely); this means that
+            //    // the whole paragraph is on the same page
+            //    System.out.println("paragrafo intero");
+            //    childPosIter = new KnuthPossPosIter(storedList, 0,
+                  // storedList.size());
+            //} else {
+            //    // the last item inside positionList is a Position;
+            //    // this means that the paragraph has been split
+            //    // between consecutive pages
+            LinkedList splitList = new LinkedList();
+            int splitLength = 0;
+            int iFirst = ((MappingPosition) positionList.getFirst()).getFirstIndex();
+            int iLast = ((MappingPosition) positionList.getLast()).getLastIndex();
+            // copy from storedList to splitList all the elements from
+            // iFirst to iLast
+            ListIterator storedListIterator = storedList.listIterator(iFirst);
+            while (storedListIterator.nextIndex() <= iLast) {
+                KnuthElement element = (KnuthElement) storedListIterator
+                        .next();
+                // some elements in storedList (i.e. penalty items) were created
+                // by this BlockLM, and must be ignored
+                if (element.getLayoutManager() != this) {
+                    splitList.add(element);
+                    splitLength += element.getW();
+                    lastLM = element.getLayoutManager();
+                }
+            }
+            //System.out.println("addAreas riferito a storedList da " +
+                  // iFirst + " a " + iLast);
+            //System.out.println("splitLength= " + splitLength
+            //                   + " (" + neededUnits(splitLength) + " unita') "
+            //                   + (neededUnits(splitLength) * bpUnit - splitLength) + " spazi");
+            // add space before and / or after the paragraph
+            // to reach a multiple of bpUnit
+            if (bSpaceBefore && bSpaceAfter) {
+                foBlockSpaceBefore = new SpaceVal(fobj.getCommonMarginBlock().spaceBefore).getSpace();
+                foBlockSpaceAfter = new SpaceVal(fobj.getCommonMarginBlock().spaceAfter).getSpace();
+                adjustedSpaceBefore = (neededUnits(splitLength
+                        + foBlockSpaceBefore.min
+                        + foBlockSpaceAfter.min)
+                        * bpUnit - splitLength) / 2;
+                adjustedSpaceAfter = neededUnits(splitLength
+                        + foBlockSpaceBefore.min
+                        + foBlockSpaceAfter.min)
+                        * bpUnit - splitLength - adjustedSpaceBefore;
+            } else if (bSpaceBefore) {
+                adjustedSpaceBefore = neededUnits(splitLength
+                        + foBlockSpaceBefore.min)
+                        * bpUnit - splitLength;
+            } else {
+                adjustedSpaceAfter = neededUnits(splitLength
+                        + foBlockSpaceAfter.min)
+                        * bpUnit - splitLength;
+            }
+            //System.out.println("spazio prima = " + adjustedSpaceBefore
+                  // + " spazio dopo = " + adjustedSpaceAfter + " totale = " +
+                  // (adjustedSpaceBefore + adjustedSpaceAfter + splitLength));
+            childPosIter = new KnuthPossPosIter(splitList, 0, splitList
+                    .size());
+            //}
+        }
+
+        // if adjusted space before
+        if (bSpaceBefore) {
+            addBlockSpacing(0, new MinOptMax(adjustedSpaceBefore));
+        }
+
+        while ((childLM = childPosIter.getNextChildLM()) != null) {
+            // set last area flag
+            lc.setFlags(LayoutContext.LAST_AREA,
+                    (layoutContext.isLastArea() && childLM == lastLM));
+            /*LF*/lc.setStackLimit(layoutContext.getStackLimit());
+            // Add the line areas to Area
+            childLM.addAreas(childPosIter, lc);
+        }
+        } else {
+            // if adjusted space before
+            if (bSpaceBefore) {
+                addBlockSpacing(0, new MinOptMax(adjustedSpaceBefore));
+            }
+            //Add child areas inside the reference area
+            bcpos.getBreaker().addContainedAreas();
+        }
+
+        int bIndents = fobj.getCommonBorderPaddingBackground().getBPPaddingAndBorder(false);
+
+        addMarkers(false, false, true);
+
+        flush();
+
+        // if adjusted space after
+        if (bSpaceAfter) {
+            addBlockSpacing(0, new MinOptMax(adjustedSpaceAfter));
+        }
+
+        viewportBlockArea = null;
+        referenceArea = null;
+    }
+    
+    public void addAreasOLDOLDOLD(PositionIterator parentIter,
                          LayoutContext layoutContext) {
         getParentArea(null);
 
@@ -574,6 +1157,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
 
         //Handle display-align now that the used BPD can be determined
         usedBPD = referenceArea.getAllocBPD();
+        /* done by the breaker now by inserting additional boxes
         if (!autoHeight & (usedBPD > 0)) {
             if (fobj.getDisplayAlign() == EN_CENTER) {
                 viewportBlockArea.setCTM(viewportBlockArea.getCTM().multiply(
@@ -582,7 +1166,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
                 viewportBlockArea.setCTM(viewportBlockArea.getCTM().multiply(
                         new CTM().translate(0, (relDims.bpd - usedBPD))));
             }
-        }
+        }*/
         
         // Fake a 0 height for absolute positioned blocks.
         int saveBPD = viewportBlockArea.getBPD();
@@ -595,6 +1179,43 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
             viewportBlockArea.setBPD(saveBPD);
         }
     }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#negotiateBPDAdjustment(int, org.apache.fop.layoutmgr.KnuthElement)
+     */
+    public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#discardSpace(org.apache.fop.layoutmgr.KnuthGlue)
+     */
+    public void discardSpace(KnuthGlue spaceGlue) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether()
+     */
+    public boolean mustKeepTogether() {
+        return !fobj.getKeepTogether().getWithinPage().isAuto();
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious()
+     */
+    public boolean mustKeepWithPrevious() {
+        return !fobj.getKeepWithPrevious().getWithinPage().isAuto();
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext()
+     */
+    public boolean mustKeepWithNext() {
+        return !fobj.getKeepWithNext().getWithinPage().isAuto();
+    }
     
 }
 
index 74e29f7846b10f4023431e99f82bc607180b9427..d36a6f904e2ba345bacd49b0d2dae76133d9b128 100644 (file)
@@ -18,6 +18,8 @@
 
 package org.apache.fop.layoutmgr;
 
+import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.ListIterator;
 import java.util.List;
 
@@ -32,7 +34,8 @@ import org.apache.fop.traits.MinOptMax;
 /**
  * LayoutManager for a block FO.
  */
-public class BlockLayoutManager extends BlockStackingLayoutManager {
+public class BlockLayoutManager extends BlockStackingLayoutManager
+                                    implements BlockLevelLayoutManager {
     
     private static final int FINISHED_LEAF_POS = -2;
     
@@ -71,6 +74,15 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
     protected List childBreaks = new java.util.ArrayList();
 
     private boolean isfirst = true;
+    
+    /*LF*/
+    /** Only used to store the original list when createUnitElements is called */
+    private LinkedList storedList = null;
+
+    private boolean bBreakBeforeServed = false;
+    private boolean bSpaceBeforeServed = false;
+
+    private LineLayoutManager childLLM = null;
 
     /**
      * Creates a new BlockLayoutManager.
@@ -97,6 +109,16 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
     protected void initProperties() {
         foBlockSpaceBefore = new SpaceVal(fobj.getCommonMarginBlock().spaceBefore).getSpace();
         prevFoBlockSpaceAfter = foBlockSpaceAfter;
+/*LF*/  bpUnit = 0; //layoutProps.blockProgressionUnit;
+/*LF*/  if (bpUnit == 0) {
+/*LF*/      // use optimum space values
+/*LF*/      adjustedSpaceBefore = fobj.getCommonMarginBlock().spaceBefore.getSpace().getOptimum().getLength().getValue();
+/*LF*/      adjustedSpaceAfter = fobj.getCommonMarginBlock().spaceAfter.getSpace().getOptimum().getLength().getValue();
+/*LF*/  } else {
+/*LF*/      // use minimum space values
+/*LF*/      adjustedSpaceBefore = fobj.getCommonMarginBlock().spaceBefore.getSpace().getMinimum().getLength().getValue();
+/*LF*/      adjustedSpaceAfter = fobj.getCommonMarginBlock().spaceAfter.getSpace().getMinimum().getLength().getValue();
+/*LF*/  }
     }
 
     /**
@@ -178,10 +200,176 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
         return iIndents;
     }
     
+    public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+        /* LF *///System.err.println("BLM.getNextKnuthElements> keep-together = "
+              // + layoutProps.keepTogether.getType());
+        /* LF *///System.err.println(" keep-with-previous = " +
+              // layoutProps.keepWithPrevious.getType());
+        /* LF *///System.err.println(" keep-with-next = " +
+              // layoutProps.keepWithNext.getType());
+        BlockLevelLayoutManager curLM; // currently active LM
+        BlockLevelLayoutManager prevLM = null; // previously active LM
+
+        referenceIPD = context.getRefIPD();
+        int iIndents = fobj.getCommonMarginBlock().startIndent.getValue() 
+                + fobj.getCommonMarginBlock().endIndent.getValue();
+        int bIndents = fobj.getCommonBorderPaddingBackground().getBPPaddingAndBorder(false);
+        int ipd = referenceIPD - iIndents;
+
+        MinOptMax stackSize = new MinOptMax();
+
+        // Set context for percentage property values.
+        fobj.setLayoutDimension(PercentBase.BLOCK_IPD, ipd);
+        fobj.setLayoutDimension(PercentBase.BLOCK_BPD, -1);
+
+        LinkedList returnedList = null;
+        LinkedList contentList = new LinkedList();
+        LinkedList returnList = new LinkedList();
+        Position returnPosition = new NonLeafPosition(this, null);
+
+        if (!bBreakBeforeServed) {
+            try {
+                if (addKnuthElementsForBreakBefore(returnList, returnPosition, 
+                        fobj.getBreakBefore())) {
+                    return returnList;
+                }
+            } finally {
+                bBreakBeforeServed = true;
+            }
+        }
+
+        if (!bSpaceBeforeServed) {
+            addKnuthElementsForSpaceBefore(returnList, returnPosition, alignment, 
+                    fobj.getCommonMarginBlock().spaceBefore);
+            bSpaceBeforeServed = true;
+        }
+        
+        addKnuthElementForBorderPaddingBefore(returnList, returnPosition, 
+                fobj.getCommonBorderPaddingBackground());
+
+        while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) {
+            LayoutContext childLC = new LayoutContext(0);
+            if (curLM instanceof LineLayoutManager) {
+                // curLM is a LineLayoutManager
+                // set stackLimit for lines
+                childLC.setStackLimit(new MinOptMax(ipd/*
+                                                         * - iIndents -
+                                                         * iTextIndent
+                                                         */));
+                childLC.setRefIPD(ipd);
+            } else {
+                // curLM is a ?
+                childLC.setStackLimit(MinOptMax.subtract(context
+                        .getStackLimit(), stackSize));
+                childLC.setRefIPD(referenceIPD);
+            }
+
+            // get elements from curLM
+            returnedList = curLM.getNextKnuthElements(childLC, alignment);
+            if (returnedList.size() == 1
+                    && ((KnuthElement) returnedList.getFirst()).isPenalty()
+                    && ((KnuthPenalty) returnedList.getFirst()).getP() == -KnuthElement.INFINITE) {
+                // a descendant of this block has break-before
+                if (returnList.size() == 0) {
+                    // the first child (or its first child ...) has
+                    // break-before;
+                    // all this block, including space before, will be put in
+                    // the
+                    // following page
+                    bSpaceBeforeServed = false;
+                }
+                contentList.addAll(returnedList);
+
+                /* extension: conversione di tutta la sequenza fin'ora ottenuta */
+                if (bpUnit > 0) {
+                    storedList = contentList;
+                    contentList = createUnitElements(contentList);
+                }
+                /* end of extension */
+
+                // "wrap" the Position inside each element
+                // moving the elements from contentList to returnList
+                returnedList = new LinkedList();
+                wrapPositionElements(contentList, returnList);
+
+                return returnList;
+            } else {
+                if (prevLM != null) {
+                    // there is a block handled by prevLM
+                    // before the one handled by curLM
+                    if (mustKeepTogether() 
+                            || prevLM.mustKeepWithNext()
+                            || curLM.mustKeepWithPrevious()) {
+                        // add an infinite penalty to forbid a break between
+                        // blocks
+                        contentList.add(new KnuthPenalty(0,
+                                KnuthElement.INFINITE, false,
+                                new Position(this), false));
+                    } else if (!((KnuthElement) contentList.getLast()).isGlue()) {
+                        // add a null penalty to allow a break between blocks
+                        contentList.add(new KnuthPenalty(0, 0, false,
+                                new Position(this), false));
+                    } else {
+                        // the last element in contentList is a glue;
+                        // it is a feasible breakpoint, there is no need to add
+                        // a penalty
+                    }
+                }
+                contentList.addAll(returnedList);
+                if (returnedList.size() == 0) {
+                    //Avoid NoSuchElementException below (happens with empty blocks)
+                    continue;
+                }
+                if (((KnuthElement) returnedList.getLast()).isPenalty()
+                        && ((KnuthPenalty) returnedList.getLast()).getP() == -KnuthElement.INFINITE) {
+                    // a descendant of this block has break-after
+                    if (curLM.isFinished()) {
+                        // there is no other content in this block;
+                        // it's useless to add space after before a page break
+                        setFinished(true);
+                    }
+
+                    /* extension: conversione di tutta la sequenza fin'ora ottenuta */
+                    if (bpUnit > 0) {
+                        storedList = contentList;
+                        contentList = createUnitElements(contentList);
+                    }
+                    /* end of extension */
+
+                    returnedList = new LinkedList();
+                    wrapPositionElements(contentList, returnList);
+
+                    return returnList;
+                }
+            }
+            prevLM = curLM;
+        }
+
+        /* Extension: conversione di tutta la sequenza fin'ora ottenuta */
+        if (bpUnit > 0) {
+            storedList = contentList;
+            contentList = createUnitElements(contentList);
+        }
+        /* end of extension */
+
+        returnedList = new LinkedList();
+        wrapPositionElements(contentList, returnList);
+
+        addKnuthElementsForBorderPaddingAfter(returnList, returnPosition, 
+                fobj.getCommonBorderPaddingBackground());
+        addKnuthElementsForSpaceAfter(returnList, returnPosition, alignment, 
+                fobj.getCommonMarginBlock().spaceAfter);
+        addKnuthElementsForBreakAfter(returnList, returnPosition, fobj.getBreakAfter());
+
+        setFinished(true);
+
+        return returnList;
+    }
+
     /**
      * @see org.apache.fop.layoutmgr.LayoutManager#getNextBreakPoss(org.apache.fop.layoutmgr.LayoutContext)
      */
-    public BreakPoss getNextBreakPoss(LayoutContext context) {
+    public BreakPoss getNextBreakPossOLDOLDOLD(LayoutContext context) {
         LayoutManager curLM; // currently active LM
 
         //int refipd = context.getRefIPD();
@@ -289,10 +477,616 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
         return breakPoss;
     }
 
+    protected LinkedList createUnitElements(LinkedList oldList) {
+        //System.out.println(" ");
+        //System.out.println("Inizio conversione: " + oldList.size() + " elementi, spazio minimo prima= " + layoutProps.spaceBefore.getSpace().min
+        //                   + " spazio minimo dopo= " + layoutProps.spaceAfter.getSpace().min);
+        // add elements at the beginning and at the end of oldList
+        // representing minimum spaces
+        LayoutManager lm = ((KnuthElement)oldList.getFirst()).getLayoutManager();
+        boolean bAddedBoxBefore = false;
+        boolean bAddedBoxAfter = false;
+        if (adjustedSpaceBefore > 0) {
+            oldList.addFirst(new KnuthBox(adjustedSpaceBefore,
+                                          new Position(lm), true));
+            bAddedBoxBefore = true;
+        }
+        if (adjustedSpaceAfter > 0) {
+            oldList.addLast(new KnuthBox(adjustedSpaceAfter,
+                                         new Position(lm), true));
+            bAddedBoxAfter = true;
+        }
+
+        MinOptMax totalLength = new MinOptMax(0);
+        MinOptMax totalUnits = new MinOptMax(0);
+        LinkedList newList = new LinkedList();
+
+        //System.out.println(" ");
+        //System.out.println(" Prima scansione");
+        // scan the list once to compute total min, opt and max length
+        ListIterator oldListIterator = oldList.listIterator();
+        while (oldListIterator.hasNext()) {
+            KnuthElement element = (KnuthElement) oldListIterator.next();
+            if (element.isBox()) {
+/*LF*/          totalLength.add(new MinOptMax(element.getW()));
+/*LF*/          //System.out.println("box " + element.getW());               
+/*LF*/      } else if (element.isGlue()) {
+/*LF*/          totalLength.min -= ((KnuthGlue) element).getZ();
+/*LF*/          totalLength.max += ((KnuthGlue) element).getY();
+/*LF*/          //leafValue = ((LeafPosition) element.getPosition()).getLeafPos();
+/*LF*/          //System.out.println("glue " + element.getW() + " + " + ((KnuthGlue) element).getY() + " - " + ((KnuthGlue) element).getZ());
+/*LF*/      } else {
+/*LF*/          //System.out.println((((KnuthPenalty)element).getP() == KnuthElement.INFINITE ? "PENALTY " : "penalty ") + element.getW());
+            }
+        }
+        // compute the total amount of "units"
+        totalUnits = new MinOptMax(neededUnits(totalLength.min),
+                                   neededUnits(totalLength.opt),
+                                   neededUnits(totalLength.max));
+        //System.out.println(" totalLength= " + totalLength);
+        //System.out.println(" unita'= " + totalUnits);
+
+        //System.out.println(" ");
+        //System.out.println(" Seconda scansione");
+        // scan the list once more, stopping at every breaking point
+        // in order to compute partial min, opt and max length
+        // and create the new elements
+        oldListIterator = oldList.listIterator();
+        boolean bPrevIsBox = false;
+        MinOptMax lengthBeforeBreak = new MinOptMax(0);
+        MinOptMax lengthAfterBreak = (MinOptMax) totalLength.clone();
+        MinOptMax unitsBeforeBreak;
+        MinOptMax unitsAfterBreak;
+        MinOptMax unsuppressibleUnits = new MinOptMax(0);
+        int firstIndex = 0;
+        int lastIndex = -1;
+        while (oldListIterator.hasNext()) {
+            KnuthElement element = (KnuthElement) oldListIterator.next();
+            lastIndex ++;
+            if (element.isBox()) {
+                lengthBeforeBreak.add(new MinOptMax(element.getW()));
+                lengthAfterBreak.subtract(new MinOptMax(element.getW()));
+                bPrevIsBox = true;
+            } else if (element.isGlue()) {
+                lengthBeforeBreak.min -= ((KnuthGlue) element).getZ();
+                lengthAfterBreak.min += ((KnuthGlue) element).getZ();
+                lengthBeforeBreak.max += ((KnuthGlue) element).getY();
+                lengthAfterBreak.max -= ((KnuthGlue) element).getY();
+                bPrevIsBox = false;
+            } else {
+                lengthBeforeBreak.add(new MinOptMax(element.getW()));
+                bPrevIsBox = false;
+            }
+
+            // create the new elements
+            if (element.isPenalty() && ((KnuthPenalty) element).getP() < KnuthElement.INFINITE
+                || element.isGlue() && bPrevIsBox
+                || !oldListIterator.hasNext()) {
+                // suppress elements after the breaking point
+                int iStepsForward = 0;
+                while (oldListIterator.hasNext()) {
+                    KnuthElement el = (KnuthElement) oldListIterator.next();
+                    iStepsForward++;
+                    if (el.isGlue()) {
+                        // suppressed glue
+                        lengthAfterBreak.min += ((KnuthGlue) el).getZ();
+                        lengthAfterBreak.max -= ((KnuthGlue) el).getY();
+                    } else if (el.isPenalty()) {
+                        // suppressed penalty, do nothing
+                    } else {
+                        // box, end of suppressions
+                        break;
+                    }
+                }
+                // compute the partial amount of "units" before and after the break
+                unitsBeforeBreak = new MinOptMax(neededUnits(lengthBeforeBreak.min),
+                                                 neededUnits(lengthBeforeBreak.opt),
+                                                 neededUnits(lengthBeforeBreak.max));
+                unitsAfterBreak = new MinOptMax(neededUnits(lengthAfterBreak.min),
+                                                neededUnits(lengthAfterBreak.opt),
+                                                neededUnits(lengthAfterBreak.max));
+
+                // rewind the iterator and lengthAfterBreak
+                for (int i = 0; i < iStepsForward; i++) {
+                    KnuthElement el = (KnuthElement) oldListIterator.previous();
+                    if (el.isGlue()) {
+                        lengthAfterBreak.min -= ((KnuthGlue) el).getZ();
+                        lengthAfterBreak.max += ((KnuthGlue) el).getY();
+                    }
+                }
+
+                // compute changes in length, stretch and shrink
+                int uLengthChange = unitsBeforeBreak.opt + unitsAfterBreak.opt - totalUnits.opt;
+                int uStretchChange = (unitsBeforeBreak.max + unitsAfterBreak.max - totalUnits.max)
+                                     - (unitsBeforeBreak.opt + unitsAfterBreak.opt - totalUnits.opt);
+                int uShrinkChange = (unitsBeforeBreak.opt + unitsAfterBreak.opt - totalUnits.opt)
+                                    - (unitsBeforeBreak.min + unitsAfterBreak.min - totalUnits.min);
+
+                // compute the number of normal, stretch and shrink unit
+                // that must be added to the new sequence
+                int uNewNormal = unitsBeforeBreak.opt - unsuppressibleUnits.opt;
+                int uNewStretch = (unitsBeforeBreak.max - unitsBeforeBreak.opt)
+                                  - (unsuppressibleUnits.max - unsuppressibleUnits.opt);
+                int uNewShrink = (unitsBeforeBreak.opt - unitsBeforeBreak.min)
+                                 - (unsuppressibleUnits.opt - unsuppressibleUnits.min);
+
+/*LF*/          //System.out.println("(" + unsuppressibleUnits.min + "-" + unsuppressibleUnits.opt + "-" +  unsuppressibleUnits.max + ") "
+/*LF*/          //                   + " -> " + unitsBeforeBreak.min + "-" + unitsBeforeBreak.opt + "-" +  unitsBeforeBreak.max
+/*LF*/          //                   + " + " + unitsAfterBreak.min + "-" + unitsAfterBreak.opt + "-" +  unitsAfterBreak.max
+/*LF*/          //                   + (uLengthChange != 0 ? " [length " + uLengthChange + "] " : "")
+/*LF*/          //                   + (uStretchChange != 0 ? " [stretch " + uStretchChange + "] " : "")
+/*LF*/          //                   + (uShrinkChange != 0 ? " [shrink " + uShrinkChange + "]" : "")
+/*LF*/          //                   );
+
+                // create the MappingPosition which will be stored in the new elements
+                // correct firstIndex and lastIndex
+                int firstIndexCorrection = 0;
+                int lastIndexCorrection = 0;
+                if (bAddedBoxBefore) {
+                    if (firstIndex != 0) {
+                        firstIndexCorrection ++;
+                    }
+                    lastIndexCorrection ++;
+                }
+                if (bAddedBoxAfter && lastIndex == (oldList.size() - 1)) {
+                    lastIndexCorrection ++;
+                }
+                MappingPosition mappingPos = new MappingPosition(this,
+                                                                 firstIndex - firstIndexCorrection,
+                                                                 lastIndex - lastIndexCorrection);
+
+                // new box
+                newList.add(new KnuthBox((uNewNormal - uLengthChange) * bpUnit,
+                                         mappingPos,
+                                         false));
+                unsuppressibleUnits.add(new MinOptMax(uNewNormal - uLengthChange));
+                //System.out.println("        box " + (uNewNormal - uLengthChange));
+
+                // new infinite penalty, glue and box, if necessary
+                if (uNewStretch - uStretchChange > 0
+                    || uNewShrink - uShrinkChange > 0) {
+                    int iStretchUnits = (uNewStretch - uStretchChange > 0 ? (uNewStretch - uStretchChange) : 0);
+                    int iShrinkUnits = (uNewShrink - uShrinkChange > 0 ? (uNewShrink - uShrinkChange) : 0);
+                    newList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
+                                                 mappingPos,
+                                                 false));
+                    newList.add(new KnuthGlue(0,
+                                              iStretchUnits * bpUnit,
+                                              iShrinkUnits * bpUnit,
+                                              LINE_NUMBER_ADJUSTMENT,
+                                              mappingPos,
+                                              false));
+                    //System.out.println("        PENALTY");
+                    //System.out.println("        glue 0 " + iStretchUnits + " " + iShrinkUnits);
+                    unsuppressibleUnits.max += iStretchUnits;
+                    unsuppressibleUnits.min -= iShrinkUnits;
+                    if (!oldListIterator.hasNext()) {
+                        newList.add(new KnuthBox(0,
+                                                 mappingPos,
+                                                 false));
+                        //System.out.println("        box 0");
+                    }
+                }
+
+                // new breaking sequence
+                if (uStretchChange != 0
+                    || uShrinkChange != 0) {
+                    // new infinite penalty, glue, penalty and glue
+                    newList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
+                                                 mappingPos,
+                                                 false));
+                    newList.add(new KnuthGlue(0,
+                                              uStretchChange * bpUnit,
+                                              uShrinkChange * bpUnit,
+                                              LINE_NUMBER_ADJUSTMENT,
+                                              mappingPos,
+                                              false));
+                    newList.add(new KnuthPenalty(uLengthChange * bpUnit,
+                                                 0, false, element.getPosition(), false));
+                    newList.add(new KnuthGlue(0,
+                                              - uStretchChange * bpUnit,
+                                              - uShrinkChange * bpUnit,
+                                              LINE_NUMBER_ADJUSTMENT,
+                                              mappingPos,
+                                              false));
+                    //System.out.println("        PENALTY");
+                    //System.out.println("        glue 0 " + uStretchChange + " " + uShrinkChange);
+                    //System.out.println("        penalty " + uLengthChange + " * unit");
+                    //System.out.println("        glue 0 " + (- uStretchChange) + " " + (- uShrinkChange));
+                } else if (oldListIterator.hasNext()){
+                    // new penalty
+                    newList.add(new KnuthPenalty(uLengthChange * bpUnit,
+                                                 0, false,
+                                                 mappingPos,
+                                                 false));
+                    //System.out.println("        penalty " + uLengthChange + " * unit");
+                }
+                // update firstIndex
+                firstIndex = lastIndex + 1;
+            }
+
+            if (element.isPenalty()) {
+                lengthBeforeBreak.add(new MinOptMax(-element.getW()));
+            }
+
+        }
+
+        // remove elements at the beginning and at the end of oldList
+        // representing minimum spaces
+        if (adjustedSpaceBefore > 0) {
+            oldList.removeFirst();
+        }
+        if (adjustedSpaceAfter > 0) {
+            oldList.removeLast();
+        }
+
+        // if space-before.conditionality is "discard", correct newList
+        if (fobj.getCommonMarginBlock().spaceBefore.getSpace().isDiscard()) {
+            // remove the wrong element
+            KnuthBox wrongBox = (KnuthBox) newList.removeFirst();
+            // if this paragraph is at the top of a page, the space before
+            // must be ignored; compute the length change
+            int decreasedLength = (neededUnits(totalLength.opt)
+                                   - neededUnits(totalLength.opt - adjustedSpaceBefore))
+                                  * bpUnit;
+            // insert the correct elements
+            newList.addFirst(new KnuthBox(wrongBox.getW() - decreasedLength,
+                                          wrongBox.getPosition(), false));
+            newList.addFirst(new KnuthGlue(decreasedLength, 0, 0, SPACE_BEFORE_ADJUSTMENT,
+                                           wrongBox.getPosition(), false));
+            //System.out.println("        rimosso box " + neededUnits(wrongBox.getW()));
+            //System.out.println("        aggiunto glue " + neededUnits(decreasedLength) + " 0 0");
+            //System.out.println("        aggiunto box " + neededUnits(wrongBox.getW() - decreasedLength));
+        }
+
+        // if space-after.conditionality is "discard", correct newList
+        if (fobj.getCommonMarginBlock().spaceAfter.getSpace().isDiscard()) {
+            // remove the wrong element
+            KnuthBox wrongBox = (KnuthBox) newList.removeLast();
+            // if the old sequence is box(h) penalty(inf) glue(x,y,z) box(0)
+            // (it cannot be parted and has some stretch or shrink)
+            // the wrong box is the first one, not the last one
+            LinkedList preserveList = new LinkedList();
+            if (wrongBox.getW() == 0) {
+                preserveList.add(wrongBox);
+                preserveList.addFirst((KnuthGlue) newList.removeLast());
+                preserveList.addFirst((KnuthPenalty) newList.removeLast());
+                wrongBox = (KnuthBox) newList.removeLast();
+            }
+
+            // if this paragraph is at the bottom of a page, the space after
+            // must be ignored; compute the length change
+            int decreasedLength = (neededUnits(totalLength.opt)
+                                   - neededUnits(totalLength.opt - adjustedSpaceAfter))
+                                  * bpUnit;
+            // insert the correct box
+            newList.addLast(new KnuthBox(wrongBox.getW() - decreasedLength,
+                                         wrongBox.getPosition(), false));
+            // add preserved elements
+            if (preserveList.size() > 0) {
+                newList.addAll(preserveList);
+            }
+            // insert the correct glue
+            newList.addLast(new KnuthGlue(decreasedLength, 0, 0, SPACE_AFTER_ADJUSTMENT,
+                                          wrongBox.getPosition(), false));
+            //System.out.println("        rimosso box " + neededUnits(wrongBox.getW()));
+            //System.out.println("        aggiunto box " + neededUnits(wrongBox.getW() - decreasedLength));
+            //System.out.println("        aggiunto glue " + neededUnits(decreasedLength) + " 0 0");
+        }
+
+        return newList;
+    }
+/*LF*/
+
+    public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
+/*LF*/  //System.out.println("  BLM.negotiateBPDAdjustment> " + adj);
+/*LF*/  //System.out.println("  lastElement e' " + (lastElement.isPenalty() ? "penalty" : (lastElement.isGlue() ? "glue" : "box" )));
+/*LF*/  //System.out.println("  position e' " + lastElement.getPosition().getClass().getName());
+/*LF*/  //System.out.println("  " + (bpUnit > 0 ? "unit" : ""));
+        Position innerPosition = ((NonLeafPosition) lastElement.getPosition()).getPosition();
+
+        if (innerPosition == null && lastElement.isGlue()) {
+            // this adjustment applies to space-before or space-after of this block
+            if (((KnuthGlue) lastElement).getAdjustmentClass() == SPACE_BEFORE_ADJUSTMENT) {
+                // this adjustment applies to space-before
+                adjustedSpaceBefore += adj;
+/*LF*/          //System.out.println("  BLM.negotiateBPDAdjustment> spazio prima: " + adj);
+            } else {
+                // this adjustment applies to space-after
+                adjustedSpaceAfter += adj;
+/*LF*/          //System.out.println("  BLM.negotiateBPDAdjustment> spazio dopo: " + adj);
+            }
+            return adj;
+        } else if (innerPosition instanceof MappingPosition) {
+            // this block has block-progression-unit > 0: the adjustment can concern
+            // - the space-before or space-after of this block, 
+            // - the line number of a descendant of this block
+            if (lastElement.isGlue()) {
+                // lastElement is a glue
+/*LF*/          //System.out.println("  BLM.negotiateBPDAdjustment> bpunit con glue");
+                ListIterator storedListIterator = storedList.listIterator(((MappingPosition) innerPosition).getFirstIndex());
+                int newAdjustment = 0;
+                while (storedListIterator.nextIndex() <= ((MappingPosition) innerPosition).getLastIndex()) {
+                    KnuthElement storedElement = (KnuthElement) storedListIterator.next();
+                    if (storedElement.isGlue()) {
+                        newAdjustment += ((BlockLevelLayoutManager) storedElement.getLayoutManager()).negotiateBPDAdjustment(adj - newAdjustment, storedElement);
+/*LF*/                  //System.out.println("  BLM.negotiateBPDAdjustment> (progressivo) righe: " + newAdjustment);
+                    }
+                }
+                newAdjustment = (newAdjustment > 0 ? bpUnit * neededUnits(newAdjustment)
+                                                   : - bpUnit * neededUnits(- newAdjustment));
+                return newAdjustment;
+            } else {
+                // lastElement is a penalty: this means that the paragraph
+                // has been split between consecutive pages:
+                // this may involve a change in the number of lines
+/*LF*/          //System.out.println("  BLM.negotiateBPDAdjustment> bpunit con penalty");
+                KnuthPenalty storedPenalty = (KnuthPenalty)
+                                             storedList.get(((MappingPosition) innerPosition).getLastIndex());
+                if (storedPenalty.getW() > 0) {
+                    // the original penalty has width > 0
+/*LF*/              //System.out.println("  BLM.negotiateBPDAdjustment> chiamata passata");
+                    return ((BlockLevelLayoutManager) storedPenalty.getLayoutManager())
+                           .negotiateBPDAdjustment(storedPenalty.getW(), (KnuthElement) storedPenalty);
+                } else {
+                    // the original penalty has width = 0
+                    // the adjustment involves only the spaces before and after
+/*LF*/              //System.out.println("  BLM.negotiateBPDAdjustment> chiamata gestita");
+                    return adj;
+                }
+            }
+        } else if (innerPosition.getLM() != this) {
+            // this adjustment concerns another LM
+            NonLeafPosition savedPos = (NonLeafPosition) lastElement.getPosition();
+            lastElement.setPosition(innerPosition);
+            int returnValue = ((BlockLevelLayoutManager) lastElement.getLayoutManager()).negotiateBPDAdjustment(adj, lastElement);
+            lastElement.setPosition(savedPos);
+/*LF*/      //System.out.println("  BLM.negotiateBPDAdjustment> righe: " + returnValue);
+            return returnValue;
+        } else {
+            // this should never happen
+            System.err.println("BlockLayoutManager.negotiateBPDAdjustment(): unexpected Position");
+            return 0;
+        }
+    }
+
+    public void discardSpace(KnuthGlue spaceGlue) {
+/*LF*/  //System.out.println("  BLM.discardSpace> " + spaceGlue.getPosition().getClass().getName());
+        Position innerPosition = ((NonLeafPosition) spaceGlue.getPosition()).getPosition();
+
+/*LF*/  if (innerPosition == null || innerPosition.getLM() == this) {
+            // if this block has block-progression-unit > 0, innerPosition can be
+            // a MappingPosition
+            // spaceGlue represents space before or space after of this block
+            if (spaceGlue.getAdjustmentClass() == SPACE_BEFORE_ADJUSTMENT) {
+                // space-before must be discarded
+                adjustedSpaceBefore = 0;
+            } else {
+                // space-after must be discarded
+                adjustedSpaceAfter = 0;
+                //TODO Why are both cases handled in the same way?
+            }
+/*LF*/  } else {
+            // this element was not created by this BlockLM
+            NonLeafPosition savedPos = (NonLeafPosition)spaceGlue.getPosition();
+            spaceGlue.setPosition(innerPosition);
+            ((BlockLevelLayoutManager) spaceGlue.getLayoutManager()).discardSpace(spaceGlue);
+            spaceGlue.setPosition(savedPos);
+        }
+    }
+
+    public LinkedList getChangedKnuthElements(List oldList, /*int flaggedPenalty,*/ int alignment) {
+/*LF*/  //System.out.println("");
+/*LF*/  //System.out.println("  BLM.getChangedKnuthElements> inizio: oldList.size() = " + oldList.size());
+        ListIterator oldListIterator = oldList.listIterator();
+        KnuthElement returnedElement;
+        KnuthElement currElement = null;
+        KnuthElement prevElement = null;
+        LinkedList returnedList = new LinkedList();
+        LinkedList returnList = new LinkedList();
+        int fromIndex = 0;
+
+        // "unwrap" the Positions stored in the elements
+        KnuthElement oldElement = null;
+        while (oldListIterator.hasNext()) {
+            oldElement = (KnuthElement)oldListIterator.next();
+            Position innerPosition = ((NonLeafPosition) oldElement.getPosition()).getPosition();
+/*LF*/      //System.out.println(" BLM> unwrapping: " + (oldElement.isBox() ? "box    " : (oldElement.isGlue() ? "glue   " : "penalty")) + " creato da " + oldElement.getLayoutManager().getClass().getName());
+/*LF*/      //System.out.println(" BLM> unwrapping:         " + oldElement.getPosition().getClass().getName());
+            if (innerPosition != null) {
+                // oldElement was created by a descendant of this BlockLM
+                oldElement.setPosition(innerPosition);
+            } else {
+                // thisElement was created by this BlockLM
+                // modify its position in order to recognize it was not created
+                // by a child
+                oldElement.setPosition(new Position(this));
+            }
+        }
+
+        // create the iterator
+        List workList;
+        if (bpUnit == 0) {
+            workList = oldList;
+        } else {
+            // the storedList must be used instead of oldList;
+            // find the index of the first element of returnedList
+            // corresponding to the first element of oldList
+            oldListIterator = oldList.listIterator();
+            KnuthElement el = (KnuthElement) oldListIterator.next();
+            while (!(el.getPosition() instanceof MappingPosition)) {
+                el = (KnuthElement) oldListIterator.next();
+            }
+            int iFirst = ((MappingPosition) el.getPosition()).getFirstIndex();
+
+            // find the index of the last element of returnedList
+            // corresponding to the last element of oldList
+            oldListIterator = oldList.listIterator(oldList.size());
+            el = (KnuthElement) oldListIterator.previous();
+            while (!(el.getPosition() instanceof MappingPosition)) {
+                el = (KnuthElement) oldListIterator.previous();
+            }
+            int iLast = ((MappingPosition) el.getPosition()).getLastIndex();
+
+/*LF*/      //System.out.println("  si usa storedList da " + iFirst + " a " + iLast + " compresi su " + storedList.size() + " elementi totali");
+            workList = storedList.subList(iFirst, iLast + 1);
+        }
+        ListIterator workListIterator = workList.listIterator();
+
+/*LF*/  //System.out.println("");
+/*LF*/  //System.out.println("  BLM.getChangedKnuthElements> workList.size() = " + workList.size() + " da 0 a " + (workList.size() - 1));
+
+        while (workListIterator.hasNext()) {
+            currElement = (KnuthElement) workListIterator.next();
+/*LF*/      //System.out.println("elemento n. " + workListIterator.previousIndex() + " nella workList");
+            if (prevElement != null
+                && prevElement.getLayoutManager() != currElement.getLayoutManager()) {
+                // prevElement is the last element generated by the same LM
+                BlockLevelLayoutManager prevLM = (BlockLevelLayoutManager)
+                                                 prevElement.getLayoutManager();
+                BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
+                                                 currElement.getLayoutManager();
+                boolean bSomethingAdded = false;
+                if (prevLM != this) {
+/*LF*/              //System.out.println(" BLM.getChangedKnuthElements> chiamata da " + fromIndex + " a " + workListIterator.previousIndex() + " su " + prevLM.getClass().getName());
+                    returnedList.addAll(prevLM.getChangedKnuthElements(workList.subList(fromIndex, workListIterator.previousIndex()),
+                                                                       /*flaggedPenalty,*/ alignment));
+                    bSomethingAdded = true;
+                } else {
+                    // prevLM == this
+                    // do nothing
+/*LF*/              //System.out.println(" BLM.getChangedKnuthElements> elementi propri, ignorati, da " + fromIndex + " a " + workListIterator.previousIndex() + " su " + prevLM.getClass().getName());
+                }
+                fromIndex = workListIterator.previousIndex();
+
+                // there is another block after this one
+                if (bSomethingAdded
+                    && (this.mustKeepTogether()
+                        || prevLM.mustKeepWithNext()
+                        || currLM.mustKeepWithPrevious())) {
+                    // add an infinite penalty to forbid a break between blocks
+                    returnedList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, new Position(this), false));
+                } else if (bSomethingAdded && !((KnuthElement) returnedList.getLast()).isGlue()) {
+                    // add a null penalty to allow a break between blocks
+                    returnedList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
+                }
+            }
+            prevElement = currElement;
+        }
+        if (currElement != null) {
+            BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
+                                             currElement.getLayoutManager();
+            if (currLM != this) {
+/*LF*/          //System.out.println(" BLM.getChangedKnuthElements> chiamata da " + fromIndex + " a " + oldList.size() + " su " + currLM.getClass().getName());
+                returnedList.addAll(currLM.getChangedKnuthElements(workList.subList(fromIndex, workList.size()),
+                                                                   /*flaggedPenalty,*/ alignment));
+            } else {
+                // currLM == this
+                // there are no more elements to add
+                // remove the last penalty added to returnedList
+                if (returnedList.size() > 0) {
+                    returnedList.removeLast();
+                }
+/*LF*/          //System.out.println(" BLM.getChangedKnuthElements> elementi propri, ignorati, da " + fromIndex + " a " + workList.size());
+            }
+        }
+
+        // append elements representing space-before
+        boolean spaceBeforeIsConditional = fobj.getCommonMarginBlock().spaceBefore.getSpace().isDiscard();
+        if (bpUnit > 0
+            || adjustedSpaceBefore != 0) {
+            if (!spaceBeforeIsConditional) {
+                // add elements to prevent the glue to be discarded
+                returnList.add(new KnuthBox(0,
+                                            new NonLeafPosition(this, null), false));
+                returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
+                                                new NonLeafPosition(this, null), false));
+            }
+            if (bpUnit > 0) {
+                returnList.add(new KnuthGlue(0, 0, 0,
+                                             SPACE_BEFORE_ADJUSTMENT, new NonLeafPosition(this, null), true));
+            } else {
+                returnList.add(new KnuthGlue(adjustedSpaceBefore, 0, 0,
+                                             SPACE_BEFORE_ADJUSTMENT, new NonLeafPosition(this, null), true));
+            }
+        }
+
+/*LF*/  //System.out.println("  BLM.getChangedKnuthElements> intermedio: returnedList.size() = " + returnedList.size());
+
+/* estensione: conversione complessiva */
+/*LF*/  if (bpUnit > 0) {
+/*LF*/      storedList = returnedList;
+/*LF*/      returnedList = createUnitElements(returnedList);
+/*LF*/  }
+/* estensione */
+
+        // "wrap" the Position stored in each element of returnedList
+        // and add elements to returnList
+        ListIterator listIter = returnedList.listIterator();
+        while (listIter.hasNext()) {
+            returnedElement = (KnuthElement)listIter.next();
+            returnedElement.setPosition(new NonLeafPosition(this, returnedElement.getPosition()));
+            returnList.add(returnedElement);
+        }
+
+        // append elements representing space-after
+        boolean spaceAfterIsConditional = fobj.getCommonMarginBlock().spaceAfter.getSpace().isDiscard();
+        if (bpUnit > 0
+            || adjustedSpaceAfter != 0) {
+            if (!spaceAfterIsConditional) {
+                returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
+                                                new NonLeafPosition(this, null), false));
+            }
+            if (bpUnit > 0) {
+                returnList.add(new KnuthGlue(0, 0, 0,
+                                             SPACE_AFTER_ADJUSTMENT, new NonLeafPosition(this, null),
+                                             (!spaceAfterIsConditional) ? false : true));
+            } else {
+                returnList.add(new KnuthGlue(adjustedSpaceAfter, 0, 0,
+                                             SPACE_AFTER_ADJUSTMENT, new NonLeafPosition(this, null),
+                                             (!spaceAfterIsConditional) ? false : true));
+            }
+            if (!spaceAfterIsConditional) {
+                returnList.add(new KnuthBox(0,
+                                            new NonLeafPosition(this, null), true));
+            }
+        }
+
+/*LF*/  //System.out.println("  BLM.getChangedKnuthElements> fine: returnList.size() = " + returnList.size());
+        return returnList;
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether()
+     */
+    public boolean mustKeepTogether() {
+        return !fobj.getKeepTogether().getWithinPage().isAuto();
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious()
+     */
+    public boolean mustKeepWithPrevious() {
+        return !fobj.getKeepWithPrevious().getWithinPage().isAuto();
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext()
+     */
+    public boolean mustKeepWithNext() {
+        return !fobj.getKeepWithNext().getWithinPage().isAuto();
+    }
+
+    //TODO this method is no longer used
+    public BreakPoss getNextBreakPoss(LayoutContext context) {
+        setFinished(true);
+        return null;
+    }
+
     /**
      * @see org.apache.fop.layoutmgr.LayoutManager#addAreas(org.apache.fop.layoutmgr.PositionIterator, org.apache.fop.layoutmgr.LayoutContext)
      */
-    public void addAreas(PositionIterator parentIter,
+    public void addAreasOLDOLDOLD(PositionIterator parentIter,
                          LayoutContext layoutContext) {
         getParentArea(null);
 
@@ -339,6 +1133,168 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
         }
     }
 
+    public void addAreas(PositionIterator parentIter,
+            LayoutContext layoutContext) {
+        /* LF *///System.out.println(" BLM.addAreas>");
+        getParentArea(null);
+
+        // if this will create the first block area in a page
+        // and display-align is bottom or center, add space before
+        if (layoutContext.getSpaceBefore() > 0) {
+            addBlockSpacing(0.0, new MinOptMax(layoutContext.getSpaceBefore()));
+        }
+
+        addID(fobj.getId());
+        //addMarkers(true, bp1.isFirstArea(), bp1.isLastArea());
+        addMarkers(true, true, false);
+
+        LayoutManager childLM = null;
+        LayoutManager lastLM = null;
+        LayoutContext lc = new LayoutContext(0);
+        /* LF */// set space after in the LayoutContext for children
+        /* LF */if (layoutContext.getSpaceAfter() > 0) {
+            /* LF */lc.setSpaceAfter(layoutContext.getSpaceAfter());
+            /* LF */}
+        /* LF */PositionIterator childPosIter;
+
+        // "unwrap" the NonLeafPositions stored in parentIter
+        // and put them in a new list;
+        LinkedList positionList = new LinkedList();
+        Position pos;
+        boolean bSpaceBefore = false;
+        boolean bSpaceAfter = false;
+        while (parentIter.hasNext()) {
+            pos = (Position) parentIter.next();
+            /* LF *///System.out.println("pos = " + pos.getClass().getName());
+            Position innerPosition = ((NonLeafPosition) pos).getPosition();
+            if (innerPosition == null) {
+                // pos was created by this BlockLM and was inside an element
+                // representing space before or after
+                // this means the space was not discarded
+                if (positionList.size() == 0) {
+                    // pos was in the element representing space-before
+                    bSpaceBefore = true;
+                    /* LF *///System.out.println(" spazio prima");
+                } else {
+                    // pos was in the element representing space-after
+                    bSpaceAfter = true;
+                    /* LF *///System.out.println(" spazio dopo");
+                }
+            } else if (innerPosition.getLM() == this
+                    && !(innerPosition instanceof MappingPosition)) {
+                // pos was created by this BlockLM and was inside a penalty
+                // allowing or forbidding a page break
+                // nothing to do
+                /* LF *///System.out.println(" penalty");
+            } else {
+                // innerPosition was created by another LM
+                positionList.add(innerPosition);
+                lastLM = innerPosition.getLM();
+                /* LF *///System.out.println(" " +
+                      // innerPosition.getClass().getName());
+            }
+        }
+
+        if (bpUnit == 0) {
+            // the Positions in positionList were inside the elements
+            // created by the LineLM
+            childPosIter = new StackingIter(positionList.listIterator());
+            } else {
+            // the Positions in positionList were inside the elements
+            // created by the BlockLM in the createUnitElements() method
+            //if (((Position) positionList.getLast()) instanceof
+                  // LeafPosition) {
+            //    // the last item inside positionList is a LeafPosition
+            //    // (a LineBreakPosition, more precisely); this means that
+            //    // the whole paragraph is on the same page
+            //    System.out.println("paragrafo intero");
+            //    childPosIter = new KnuthPossPosIter(storedList, 0,
+                  // storedList.size());
+            //} else {
+            //    // the last item inside positionList is a Position;
+            //    // this means that the paragraph has been split
+            //    // between consecutive pages
+            LinkedList splitList = new LinkedList();
+            int splitLength = 0;
+            int iFirst = ((MappingPosition) positionList.getFirst()).getFirstIndex();
+            int iLast = ((MappingPosition) positionList.getLast()).getLastIndex();
+            // copy from storedList to splitList all the elements from
+            // iFirst to iLast
+            ListIterator storedListIterator = storedList.listIterator(iFirst);
+            while (storedListIterator.nextIndex() <= iLast) {
+                KnuthElement element = (KnuthElement) storedListIterator
+                        .next();
+                // some elements in storedList (i.e. penalty items) were created
+                // by this BlockLM, and must be ignored
+                if (element.getLayoutManager() != this) {
+                    splitList.add(element);
+                    splitLength += element.getW();
+                    lastLM = element.getLayoutManager();
+                }
+                }
+            //System.out.println("addAreas riferito a storedList da " +
+                  // iFirst + " a " + iLast);
+            //System.out.println("splitLength= " + splitLength
+            //                   + " (" + neededUnits(splitLength) + " unita') "
+            //                   + (neededUnits(splitLength) * bpUnit - splitLength) + " spazi");
+            // add space before and / or after the paragraph
+            // to reach a multiple of bpUnit
+            if (bSpaceBefore && bSpaceAfter) {
+                foBlockSpaceBefore = new SpaceVal(fobj.getCommonMarginBlock().spaceBefore).getSpace();
+                foBlockSpaceAfter = new SpaceVal(fobj.getCommonMarginBlock().spaceAfter).getSpace();
+                adjustedSpaceBefore = (neededUnits(splitLength
+                        + foBlockSpaceBefore.min
+                        + foBlockSpaceAfter.min)
+                        * bpUnit - splitLength) / 2;
+                adjustedSpaceAfter = neededUnits(splitLength
+                        + foBlockSpaceBefore.min
+                        + foBlockSpaceAfter.min)
+                        * bpUnit - splitLength - adjustedSpaceBefore;
+                } else if (bSpaceBefore) {
+                adjustedSpaceBefore = neededUnits(splitLength
+                        + foBlockSpaceBefore.min)
+                        * bpUnit - splitLength;
+                } else {
+                adjustedSpaceAfter = neededUnits(splitLength
+                        + foBlockSpaceAfter.min)
+                        * bpUnit - splitLength;
+                }
+            //System.out.println("spazio prima = " + adjustedSpaceBefore
+                  // + " spazio dopo = " + adjustedSpaceAfter + " totale = " +
+                  // (adjustedSpaceBefore + adjustedSpaceAfter + splitLength));
+            childPosIter = new KnuthPossPosIter(splitList, 0, splitList
+                    .size());
+            //}
+            }
+
+        // if adjusted space before
+        if (bSpaceBefore) {
+            addBlockSpacing(0, new MinOptMax(adjustedSpaceBefore));
+        }
+
+        while ((childLM = childPosIter.getNextChildLM()) != null) {
+            // set last area flag
+            lc.setFlags(LayoutContext.LAST_AREA,
+                    (layoutContext.isLastArea() && childLM == lastLM));
+            /*LF*/lc.setStackLimit(layoutContext.getStackLimit());
+            // Add the line areas to Area
+            childLM.addAreas(childPosIter, lc);
+        }
+
+        int bIndents = fobj.getCommonBorderPaddingBackground().getBPPaddingAndBorder(false);
+
+        addMarkers(false, false, true);
+
+        flush();
+
+        // if adjusted space after
+        if (bSpaceAfter) {
+            addBlockSpacing(0, new MinOptMax(adjustedSpaceAfter));
+        }
+
+        curBlockArea = null;
+    }
+
     /**
      * Return an Area which can contain the passed childArea. The childArea
      * may not yet have any content, but it has essential traits set.
diff --git a/src/java/org/apache/fop/layoutmgr/BlockLevelLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockLevelLayoutManager.java
new file mode 100644 (file)
index 0000000..a523830
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+package org.apache.fop.layoutmgr;
+
+/**
+ * The interface for LayoutManagers which generate block areas
+ */
+public interface BlockLevelLayoutManager extends LayoutManager {
+
+    static final int NO_ADJUSTMENT = -1;
+    static final int SPACE_BEFORE_ADJUSTMENT = 0;
+    static final int SPACE_AFTER_ADJUSTMENT = 1;
+    static final int LINE_NUMBER_ADJUSTMENT = 2;
+    static final int LINE_HEIGHT_ADJUSTMENT = 3;
+
+    int negotiateBPDAdjustment(int adj, KnuthElement lastElement);
+
+    void discardSpace(KnuthGlue spaceGlue);
+
+    /**
+     * @return true if this element must be kept together
+     */
+    boolean mustKeepTogether();
+
+    /**
+     * @return true if this element must be kept with the previous element.
+     */
+    boolean mustKeepWithPrevious();
+
+    /**
+     * @return true if this element must be kept with the next element.
+     */
+    boolean mustKeepWithNext();
+
+}
index 582c5c2857460977ba6789c6262f8cf1a8abe631..1366f5010d7f730d5be21605c4d03c58eaa2b9cf 100644 (file)
 
 package org.apache.fop.layoutmgr;
 
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
 import org.apache.fop.area.Area;
 import org.apache.fop.area.BlockParent;
 import org.apache.fop.area.Block;
+import org.apache.fop.fo.Constants;
 import org.apache.fop.fo.FObj;
+import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
+import org.apache.fop.fo.properties.CommonMarginBlock;
+import org.apache.fop.fo.properties.SpaceProperty;
 import org.apache.fop.traits.MinOptMax;
 
 /**
@@ -36,6 +45,15 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager {
     //protected LayoutManager curChildLM = null; AbstractLayoutManager also defines this!
     protected BlockParent parentArea = null;
 
+    /*LF*/
+    /** Value of the block-progression-unit (non-standard property) */
+    protected int bpUnit = 0;
+    /** space-before value adjusted for block-progression-unit handling */
+    protected int adjustedSpaceBefore = 0;
+    /** space-after value adjusted for block-progression-unit handling */
+    protected int adjustedSpaceAfter = 0;
+    /*LF*/
+
     public BlockStackingLayoutManager(FObj node) {
         super(node);
     }
@@ -135,5 +153,225 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager {
         }
     }
 
+    /**
+     * @param len length in millipoints to span with bp units
+     * @return the minimum integer n such that n * bpUnit >= len
+     */
+    protected int neededUnits(int len) {
+        return (int) Math.ceil((float)len / bpUnit);
+    }
+
+    /**
+     * Creates Knuth elements for before border padding and adds them to the return list.
+     * @param returnList return list to add the additional elements to
+     * @param returnPosition applicable return position
+     * @param borderAndPadding border and padding to work with
+     */
+    protected void addKnuthElementForBorderPaddingBefore(LinkedList returnList, 
+            Position returnPosition, CommonBorderPaddingBackground borderAndPadding) {
+        //Border and Padding (before)
+        //TODO Handle conditionality
+        int bpBefore = borderAndPadding.getBorderBeforeWidth(false)
+                    + borderAndPadding.getPaddingBefore(false);
+        if (bpBefore > 0) {
+            returnList.add(new KnuthBox(bpBefore, returnPosition, true));
+        }
+    }
+
+    /**
+     * Creates Knuth elements for after border padding and adds them to the return list.
+     * @param returnList return list to add the additional elements to
+     * @param returnPosition applicable return position
+     * @param borderAndPadding border and padding to work with
+     */
+    protected void addKnuthElementsForBorderPaddingAfter(LinkedList returnList, 
+            Position returnPosition, CommonBorderPaddingBackground borderAndPadding) {
+        //Border and Padding (after)
+        //TODO Handle conditionality
+        int bpAfter = borderAndPadding.getBorderAfterWidth(false)
+                    + borderAndPadding.getPaddingAfter(false);
+        if (bpAfter > 0) {
+            returnList.add(new KnuthBox(bpAfter, returnPosition, true));
+        }
+    }
+
+    /**
+     * Creates Knuth elements for break-before and adds them to the return list.
+     * @param returnList return list to add the additional elements to
+     * @param returnPosition applicable return position
+     * @param breakBefore break-before value
+     * @return true if an element has been added due to a break-before.
+     */
+    protected boolean addKnuthElementsForBreakBefore(LinkedList returnList, 
+            Position returnPosition, int breakBefore) {
+        if (breakBefore == EN_PAGE
+                || breakBefore == EN_EVEN_PAGE 
+                || breakBefore == EN_ODD_PAGE) {
+            // return a penalty element, representing a forced page break
+            returnList.add(new KnuthPenalty(0, -KnuthElement.INFINITE, false,
+                    breakBefore, returnPosition, false));
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Creates Knuth elements for break-after and adds them to the return list.
+     * @param returnList return list to add the additional elements to
+     * @param returnPosition applicable return position
+     * @param breakAfter break-after value
+     * @return true if an element has been added due to a break-after.
+     */
+    protected boolean addKnuthElementsForBreakAfter(LinkedList returnList, 
+            Position returnPosition, int breakAfter) {
+        if (breakAfter == EN_PAGE
+                || breakAfter == EN_EVEN_PAGE
+                || breakAfter == EN_ODD_PAGE) {
+            // add a penalty element, representing a forced page break
+            returnList.add(new KnuthPenalty(0, -KnuthElement.INFINITE, false,
+                    breakAfter, returnPosition, false));
+            /* LF *///System.out.println("BLM - break after!!");
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Creates Knuth elements for space-before and adds them to the return list.
+     * @param returnList return list to add the additional elements to
+     * @param returnPosition applicable return position
+     * @param alignment vertical alignment
+     * @param spaceBefore the space-before property
+     */
+    protected void addKnuthElementsForSpaceBefore(LinkedList returnList, 
+            Position returnPosition, int alignment, SpaceProperty spaceBefore) {
+        // append elements representing space-before
+        if (bpUnit > 0
+                || !(spaceBefore.getMinimum().getLength().getValue() == 0 
+                        && spaceBefore.getMaximum().getLength().getValue() == 0)) {
+            if (!spaceBefore.getSpace().isDiscard()) {
+                // add elements to prevent the glue to be discarded
+                returnList.add(new KnuthBox(0, returnPosition, false));
+                returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE,
+                        false, returnPosition, false));
+            }
+            if (bpUnit > 0) {
+                returnList.add(new KnuthGlue(0, 0, 0,
+                        BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT, 
+                        returnPosition, true));
+            } else if (alignment == EN_JUSTIFY) {
+                returnList.add(new KnuthGlue(
+                        spaceBefore.getOptimum().getLength().getValue(),
+                        spaceBefore.getMaximum().getLength().getValue()
+                                - spaceBefore.getOptimum().getLength().getValue(),
+                        spaceBefore.getOptimum().getLength().getValue()
+                                - spaceBefore.getMinimum().getLength().getValue(),
+                        BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT, 
+                        returnPosition, true));
+            } else {
+                returnList.add(new KnuthGlue(
+                        spaceBefore.getOptimum().getLength().getValue(), 
+                        0, 0, BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT,
+                        returnPosition, true));
+            }
+        }
+    }
+
+    /**
+     * Creates Knuth elements for space-after and adds them to the return list.
+     * @param returnList return list to add the additional elements to
+     * @param returnPosition applicable return position
+     * @param alignment vertical alignment
+     * @param spaceAfter the space-after property
+     */
+    protected void addKnuthElementsForSpaceAfter(LinkedList returnList, Position returnPosition, 
+                int alignment, SpaceProperty spaceAfter) {
+        // append elements representing space-after
+        if (bpUnit > 0
+                || !(spaceAfter.getMinimum().getLength().getValue() == 0 
+                        && spaceAfter.getMaximum().getLength().getValue() == 0)) {
+            if (!spaceAfter.getSpace().isDiscard()) {
+                returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE,
+                        false, returnPosition, false));
+            }
+            if (bpUnit > 0) {
+                returnList.add(new KnuthGlue(0, 0, 0, 
+                        BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT,
+                        returnPosition, true));
+            } else if (alignment == EN_JUSTIFY) {
+                returnList.add(new KnuthGlue(
+                        spaceAfter.getOptimum().getLength().getValue(),
+                        spaceAfter.getMaximum().getLength().getValue()
+                                - spaceAfter.getOptimum().getLength().getValue(),
+                        spaceAfter.getOptimum().getLength().getValue()
+                                - spaceAfter.getMinimum().getLength().getValue(),
+                        BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT, returnPosition,
+                        (!spaceAfter.getSpace().isDiscard()) ? false : true));
+            } else {
+                returnList.add(new KnuthGlue(
+                        spaceAfter.getOptimum().getLength().getValue(), 0, 0,
+                        BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT, returnPosition,
+                        (!spaceAfter.getSpace().isDiscard()) ? false : true));
+            }
+            if (!spaceAfter.getSpace().isDiscard()) {
+                returnList.add(new KnuthBox(0, returnPosition, true));
+            }
+        }
+    }
+
+    protected static class StackingIter extends PositionIterator {
+        StackingIter(Iterator parentIter) {
+            super(parentIter);
+        }
+    
+        protected LayoutManager getLM(Object nextObj) {
+            return ((Position) nextObj).getLM();
+        }
+    
+        protected Position getPos(Object nextObj) {
+            return ((Position) nextObj);
+        }
+    }
+
+    protected static class MappingPosition extends Position {
+        private int iFirstIndex;
+        private int iLastIndex;
+    
+        public MappingPosition(LayoutManager lm, int first, int last) {
+            super(lm);
+            iFirstIndex = first;
+            iLastIndex = last;
+        }
+        
+        public int getFirstIndex() {
+            return iFirstIndex;
+        }
+        
+        public int getLastIndex() {
+            return iLastIndex;
+        }
+    }
+
+    /**
+     * "wrap" the Position inside each element moving the elements from 
+     * SourceList to targetList
+     * @param sourceList source list
+     * @param targetList target list receiving the wrapped position elements
+     */
+    protected void wrapPositionElements(List sourceList, List targetList) {
+        ListIterator listIter = sourceList.listIterator();
+        while (listIter.hasNext()) {
+            KnuthElement tempElement;
+            tempElement = (KnuthElement) listIter.next();
+            //if (tempElement.getLayoutManager() != this) {
+            tempElement.setPosition(new NonLeafPosition(this,
+                    tempElement.getPosition()));
+            //}
+            targetList.add(tempElement);
+        }
+    }
+
 }
 
diff --git a/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java
new file mode 100644 (file)
index 0000000..d224217
--- /dev/null
@@ -0,0 +1,747 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import java.util.LinkedList;
+import java.util.ListIterator;
+
+public abstract class BreakingAlgorithm {
+
+    private final int KNUTH_ALGORITHM = 0;
+    private final int FIRST_FIT_ALGORITHM = 1;
+
+    // this class represent a feasible breaking point
+    public class KnuthNode {
+        // index of the breakpoint represented by this node
+        public int position;
+
+        // number of the line ending at this breakpoint
+        public int line;
+
+        // fitness class of the line ending at his breakpoint
+        public int fitness;
+
+        // accumulated width of the KnuthElements
+        public int totalWidth;
+
+        // accumulated stretchability of the KnuthElements
+        public int totalStretch;
+
+        // accumulated shrinkability of the KnuthElements
+        public int totalShrink;
+
+        // adjustment ratio if the line ends at this breakpoint
+        public double adjustRatio;
+
+/*LF*/  public int availableShrink;
+/*LF*/  public int availableStretch;
+
+        // difference between target and actual line width
+        public int difference;
+
+        // minimum total demerits up to this breakpoint
+        public double totalDemerits;
+
+        // best node for the preceding breakpoint
+        public KnuthNode previous;
+
+        public KnuthNode(int position, int line, int fitness,
+                         int totalWidth, int totalStretch, int totalShrink,
+                         double adjustRatio, int availableShrink, int availableStretch, int difference,
+                         double totalDemerits, KnuthNode previous) {
+            this.position = position;
+            this.line = line;
+            this.fitness = fitness;
+            this.totalWidth = totalWidth;
+            this.totalStretch = totalStretch;
+            this.totalShrink = totalShrink;
+            this.adjustRatio = adjustRatio;
+/*LF*/      this.availableShrink = availableShrink;
+/*LF*/      this.availableStretch = availableStretch;
+            this.difference = difference;
+            this.totalDemerits = totalDemerits;
+            this.previous = previous;
+        }
+    }
+
+    // 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 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 int bestIndex = -1;
+
+        public BestRecords() {
+        }
+
+        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");
+            }
+            bestDemerits[fitness] = demerits;
+            bestNode[fitness] = node;
+            bestAdjust[fitness] = adjust;
+/*LF*/      bestAvailableShrink[fitness] = availableShrink;
+/*LF*/      bestAvailableStretch[fitness] = availableStretch;
+            bestDifference[fitness] = difference;
+            if (bestIndex == -1 || demerits < bestDemerits[bestIndex]) {
+                bestIndex = fitness;
+            }
+        }
+
+        public boolean hasRecords() {
+            return (bestIndex != -1);
+        }
+
+        public boolean notInfiniteDemerits(int fitness) {
+            return (bestDemerits[fitness] != INFINITE_DEMERITS);
+        }
+
+        public double getDemerits(int fitness) {
+            return bestDemerits[fitness];
+        }
+
+        public KnuthNode getNode(int fitness) {
+            return bestNode[fitness];
+        }
+
+        public double getAdjust(int fitness) {
+            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 getDifference(int fitness) {
+            return bestDifference[fitness];
+        }
+
+        public double getMinDemerits() {
+            if (bestIndex != -1) {
+                return getDemerits(bestIndex);
+            } else {
+                // anyway, this should never happen
+                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;
+
+    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) ;
+
+    public abstract void updateData2(KnuthNode bestActiveNode,
+                                     KnuthSequence sequence,
+                                     int total) ;
+
+    public int findBreakingPoints(KnuthSequence par, int lineWidth,
+                                  double threshold, boolean force,
+                                  boolean hyphenationAllowed) {
+
+        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;
+
+        // reset lastTooShort and lastTooLong, as they could be not null
+        // because of previous calls to findBreakingPoints
+        lastTooShort = lastTooLong = null; 
+
+        // current element in the paragraph
+        KnuthElement thisElement = null;
+        // 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*/
+        // 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();
+        }
+
+        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());
+            if (thisElement.isBox()) {
+                // a KnuthBox object is not a legal line break
+                totalWidth += thisElement.getW();
+                previousIsBox = true;
+            } else if (thisElement.isGlue()) {
+                // 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);
+                    }
+                }
+                totalWidth += thisElement.getW();
+                totalStretch += ((KnuthGlue) thisElement).getY();
+                totalShrink += ((KnuthGlue) thisElement).getZ();
+                previousIsBox = false;
+            } else {
+                // a KnuthPenalty is a legal line break
+                // only if its penalty is not infinite;
+                // if hyphenationAllowed is false, ignore flagged penalties
+                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);
+                    }
+                }
+                previousIsBox = false;
+            }
+            /* *** inizio modifiche proposte da Finn Bock *** */
+            if (activeList.size() == 0) {
+                if (!force) {
+                    return;
+                }
+/*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(" ");
+                lastForced.totalDemerits = 0;
+                activeList.add(lastForced);
+                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 (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
+
+            // 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(" ");
+        }
+    }
+
+
+    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;
+        }
+
+        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;
+                    }
+/*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;
+                }
+                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)) {
+                        // updates best demerits data
+                        best.addRecord(newDemerits, activeNode, newIPDAdjust,
+                                       availableShrink, availableStretch, neededAdjustment, newFitnessClass);
+/*LF*/                  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,
+                                    totalWidth, totalStretch, totalShrink,
+                                    newIPDAdjust, availableShrink, availableStretch, neededAdjustment, newDemerits, activeNode);
+                        }
+                    } 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,
+                                    totalWidth, totalStretch, totalShrink,
+                                    newIPDAdjust, availableShrink, availableStretch, neededAdjustment, newDemerits, activeNode);
+                        }
+                    }
+                }
+                /* 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();
+                    }
+                }
+
+                if (activeListIterator.hasNext()) {
+                    activeNode = (KnuthNode) activeListIterator.next();
+                } else {
+                    activeNode = null;
+                    break;
+                }
+                if (activeNode.line >= newLine) {
+                    break;
+                }
+            } // end of the inner while
+
+            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;
+                    }
+                }
+
+                // 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) {
+                break;
+            }
+        } // end of the outer while
+    }
+
+    protected abstract int filterActiveList() ;
+
+    private void calculateBreakPoints(KnuthNode node, KnuthSequence par,
+                                      int total) {
+        KnuthNode bestActiveNode = node;
+        // use bestActiveNode to determine the optimum breakpoints
+        for (int i = node.line; i > 0; i--) {
+            updateData2(bestActiveNode, par, total);
+            bestActiveNode = bestActiveNode.previous;
+        }
+    }
+}
index 3a067d524d78b5cb955602bc831a0aa9bdb5b256..f3b234c3c9d9a0290effe6d100325fdac6ff23b5 100644 (file)
@@ -137,7 +137,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
         // node is a fo:Character
         if (letterSpaceIPD.min == letterSpaceIPD.max) {
             // constant letter space, only return a box
-            returnList.add(new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
+            returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
                                         areaInfo.total, areaInfo.middle,
                                         new LeafPosition(this, 0), false));
         } else {
@@ -145,14 +145,14 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
             // at the moment the character is supposed to have no letter spaces,
             // but returning this sequence allows us to change only one element
             // if addALetterSpaceTo() is called
-            returnList.add(new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
+            returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
                                         areaInfo.total, areaInfo.middle,
                                         new LeafPosition(this, 0), false));
             returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
                                             new LeafPosition(this, -1), true));
             returnList.add(new KnuthGlue(0, 0, 0,
                                          new LeafPosition(this, -1), true));
-            returnList.add(new KnuthBox(0, 0, 0, 0,
+            returnList.add(new KnuthInlineBox(0, 0, 0, 0,
                                         new LeafPosition(this, -1), true));
         }
 
@@ -171,7 +171,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
 
         if (letterSpaceIPD.min == letterSpaceIPD.max) {
             // constant letter space, return a new box
-            return new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
+            return new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
                                 areaInfo.total, areaInfo.middle,
                                 new LeafPosition(this, 0), false);
         } else {
@@ -220,7 +220,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
         if (letterSpaceIPD.min == letterSpaceIPD.max
             || areaInfo.iLScount == 0) {
             // constant letter space, or no letter space
-            returnList.add(new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
+            returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
                                         areaInfo.total, areaInfo.middle,
                                         new LeafPosition(this, 0), false));
             if (areaInfo.bHyphenated) {
@@ -231,7 +231,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
         } else {
             // adjustable letter space
             returnList.add
-                (new KnuthBox(areaInfo.ipdArea.opt
+                (new KnuthInlineBox(areaInfo.ipdArea.opt
                               - areaInfo.iLScount * letterSpaceIPD.opt,
                               areaInfo.lead, areaInfo.total, areaInfo.middle,
                               new LeafPosition(this, 0), false));
@@ -242,7 +242,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
                                areaInfo.iLScount * letterSpaceIPD.max - letterSpaceIPD.opt,
                                areaInfo.iLScount * letterSpaceIPD.opt - letterSpaceIPD.min,
                                new LeafPosition(this, -1), true));
-            returnList.add(new KnuthBox(0, 0, 0, 0,
+            returnList.add(new KnuthInlineBox(0, 0, 0, 0,
                                         new LeafPosition(this, -1), true));
             if (areaInfo.bHyphenated) {
                 returnList.add
index 093439fbadc590510640e1d0b6995e6437da848a..da351aa210ecb892e8996d217f161dcf54cb4058 100644 (file)
@@ -117,7 +117,7 @@ public class ContentLayoutManager implements InlineLevelLayoutManager {
         while (contentIter.hasNext()) {
             KnuthElement element = (KnuthElement) contentIter.next();
             if (element.isBox()) {
-                KnuthBox box = (KnuthBox) element;
+                KnuthInlineBox box = (KnuthInlineBox) element;
                 if (box.getLead() > lineLead) {
                     lineLead = box.getLead();
                 }
@@ -358,8 +358,8 @@ public class ContentLayoutManager implements InlineLevelLayoutManager {
         return contentList;
     }
 
-    public KnuthElement addALetterSpaceTo(KnuthElement element) {
-        return element;
+    public List addALetterSpaceTo(List oldList) {
+        return oldList;
     }
 
     public void getWordChars(StringBuffer sbChars, Position pos) {
@@ -373,7 +373,7 @@ public class ContentLayoutManager implements InlineLevelLayoutManager {
     }
 
     public LinkedList getChangedKnuthElements(List oldList,
-                                              int flaggedPenalty,
+                                              /*int flaggedPenalty,*/
                                               int alignment) {
         return null;
     }
index f249941c056ad7c5a6c45084bb42cfc154310e34..a608b70072fb9f7d731da4d1ea09c26b9e128d38 100644 (file)
@@ -24,7 +24,11 @@ import org.apache.fop.fo.pagination.Flow;
 import org.apache.fop.area.Area;
 import org.apache.fop.area.BlockParent;
 
+import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
+import java.util.ListIterator;
+
 import org.apache.fop.traits.MinOptMax;
 
 /**
@@ -33,8 +37,8 @@ import org.apache.fop.traits.MinOptMax;
  * This LM is responsible for getting columns of the appropriate size
  * and filling them with block-level areas generated by its children.
  */
-public class FlowLayoutManager extends BlockStackingLayoutManager {
-    
+public class FlowLayoutManager extends BlockStackingLayoutManager
+                               implements BlockLevelLayoutManager {
     private Flow fobj;
     
     /** List of break possibilities */
@@ -51,6 +55,22 @@ public class FlowLayoutManager extends BlockStackingLayoutManager {
      */
     private int numSubsequentOverflows = 0;
     
+/*LF*/
+    private static class StackingIter extends PositionIterator {
+        StackingIter(Iterator parentIter) {
+            super(parentIter);
+        }
+
+        protected LayoutManager getLM(Object nextObj) {
+            return ((Position) nextObj).getLM();
+        }
+
+        protected Position getPos(Object nextObj) {
+            return ((Position) nextObj);
+        }
+    }
+/*LF*/
+
     /**
      * This is the top level layout manager.
      * It is created by the PageSequence FO.
@@ -61,9 +81,7 @@ public class FlowLayoutManager extends BlockStackingLayoutManager {
         fobj = node;
     }
 
-    /**
-     * @see org.apache.fop.layoutmgr.LayoutManager#getNextBreakPoss(LayoutContext)
-     */
+    /*
     public BreakPoss getNextBreakPoss(LayoutContext context) {
 
         // currently active LM
@@ -131,26 +149,252 @@ public class FlowLayoutManager extends BlockStackingLayoutManager {
                              new LeafPosition(this, blockBreaks.size() - 1));
         }
         return null;
+    }*/
+
+
+//TODO Reintroduce emergency counter (generate error to avoid endless loop)
+//TODO Reintroduce layout dimensions
+    public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+        // currently active LM
+        BlockLevelLayoutManager curLM;
+        BlockLevelLayoutManager prevLM = null;
+        MinOptMax stackSize = new MinOptMax();
+        LinkedList returnedList;
+        LinkedList returnList = new LinkedList();
+
+        while ((curLM = ((BlockLevelLayoutManager) getChildLM())) != null) {
+            if (curLM.generatesInlineAreas()) {
+                log.error("inline area not allowed under flow - ignoring");
+                curLM.setFinished(true);
+                continue;
+            }
+
+            // Set up a LayoutContext
+            MinOptMax bpd = context.getStackLimit();
+            BreakPoss bp;
+            bp = null;
+
+            LayoutContext childLC = new LayoutContext(0);
+            boolean breakPage = false;
+            childLC.setStackLimit(MinOptMax.subtract(bpd, stackSize));
+            childLC.setRefIPD(context.getRefIPD());
+
+            // get elements from curLM
+            returnedList = curLM.getNextKnuthElements(childLC, alignment);
+/*LF*/      //System.out.println("FLM.getNextKnuthElements> returnedList.size() = " + returnedList.size());
+
+            // "wrap" the Position inside each element
+            LinkedList tempList = returnedList;
+            KnuthElement tempElement;
+            returnedList = new LinkedList();
+            ListIterator listIter = tempList.listIterator();
+            while (listIter.hasNext()) {
+                tempElement = (KnuthElement)listIter.next();
+                tempElement.setPosition(new NonLeafPosition(this, tempElement.getPosition()));
+                returnedList.add(tempElement);
+            }
+
+            if (returnedList.size() == 1
+                && ((KnuthElement)returnedList.getFirst()).isPenalty()
+                && ((KnuthPenalty)returnedList.getFirst()).getP() == -KnuthElement.INFINITE) {
+                // a descendant of this flow has break-before
+                returnList.addAll(returnedList);
+                return returnList;
+            } else {
+                if (returnList.size() > 0) {
+                    // there is a block before this one
+                    if (prevLM.mustKeepWithNext()
+                        || curLM.mustKeepWithPrevious()) {
+                        // add an infinite penalty to forbid a break between blocks
+                        returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, new Position(this), false));
+                    } else if (!((KnuthElement) returnList.getLast()).isGlue()) {
+                        // add a null penalty to allow a break between blocks
+                        returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
+                    }
+                }
+/*LF*/          if (returnedList.size() > 0) { // controllare!
+                    returnList.addAll(returnedList);
+                    if (((KnuthElement)returnedList.getLast()).isPenalty()
+                        && ((KnuthPenalty)returnedList.getLast()).getP() == -KnuthElement.INFINITE) {
+                        // a descendant of this flow has break-after
+/*LF*/                  //System.out.println("FLM - break after!!");
+                        return returnList;
+                    }
+/*LF*/          }
+            }
+            prevLM = curLM;
+        }
+
+        setFinished(true);
+
+        if (returnList.size() > 0) {
+            return returnList;
+        } else {
+            return null;
+        }
+    }
+
+    public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
+/*LF*/  System.out.println(" FLM.negotiateBPDAdjustment> " + adj);
+
+/*LF*/  if (lastElement.getPosition() instanceof NonLeafPosition) {
+            // this element was not created by this FlowLM
+            NonLeafPosition savedPos = (NonLeafPosition)lastElement.getPosition();
+            lastElement.setPosition(savedPos.getPosition());
+            int returnValue = ((BlockLevelLayoutManager) lastElement.getLayoutManager()).negotiateBPDAdjustment(adj, lastElement);
+            lastElement.setPosition(savedPos);
+/*LF*/      System.out.println(" FLM.negotiateBPDAdjustment> gestito " + returnValue);
+            return returnValue;
+/*LF*/  } else {
+/*LF*/      return 0;
+/*LF*/  }
+    }
+
+    public void discardSpace(KnuthGlue spaceGlue) {
+/*LF*/  System.out.println(" FLM.discardSpace> ");
+
+/*LF*/  if (spaceGlue.getPosition() instanceof NonLeafPosition) {
+            // this element was not created by this FlowLM
+            NonLeafPosition savedPos = (NonLeafPosition)spaceGlue.getPosition();
+            spaceGlue.setPosition(savedPos.getPosition());
+            ((BlockLevelLayoutManager) spaceGlue.getLayoutManager()).discardSpace(spaceGlue);
+            spaceGlue.setPosition(savedPos);
+/*LF*/  }
+    }
+
+    public boolean mustKeepTogether() {
+        return false;
+    }
+
+    public boolean mustKeepWithPrevious() {
+        return false;
+    }
+
+    public boolean mustKeepWithNext() {
+        return false;
+    }
+
+    public LinkedList getChangedKnuthElements(List oldList, /*int flaggedPenalty,*/ int alignment) {
+        ListIterator oldListIterator = oldList.listIterator();
+        KnuthElement returnedElement;
+        LinkedList returnedList = new LinkedList();
+        LinkedList returnList = new LinkedList();
+        KnuthElement prevElement = null;
+        KnuthElement currElement = null;
+        int fromIndex = 0;
+
+/*LF*/  //System.out.println("");
+/*LF*/  //System.out.println("FLM.getChangedKnuthElements> prima dell'unwrap, oldList.size() = " + oldList.size() + " da 0 a " + (oldList.size() - 1));
+        // "unwrap" the Positions stored in the elements
+        KnuthElement oldElement;
+        while (oldListIterator.hasNext()) {
+            oldElement = (KnuthElement)oldListIterator.next();
+            if (oldElement.getPosition() instanceof NonLeafPosition) {
+                // oldElement was created by a descendant of this FlowLM
+                oldElement.setPosition(((NonLeafPosition)oldElement.getPosition()).getPosition());
+            } else {
+                // thisElement was created by this FlowLM, remove it
+                oldListIterator.remove();
+            }
+        }
+        // reset the iterator
+        oldListIterator = oldList.listIterator();
+
+/*LF*/  //System.out.println("FLM.getChangedKnuthElements> dopo l'unwrap, oldList.size() = " + oldList.size() + " da 0 a " + (oldList.size() - 1));
+
+        while (oldListIterator.hasNext()) {
+            currElement = (KnuthElement) oldListIterator.next();
+/*LF*/      //System.out.println("elemento n. " + oldListIterator.previousIndex() + " nella oldList");
+            if (prevElement != null
+                && prevElement.getLayoutManager() != currElement.getLayoutManager()) {
+                // prevElement is the last element generated by the same LM
+                BlockLevelLayoutManager prevLM = (BlockLevelLayoutManager)
+                                                 prevElement.getLayoutManager();
+                BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
+                                                 currElement.getLayoutManager();
+/*LF*/          //System.out.println("FLM.getChangedKnuthElements> chiamata da " + fromIndex + " a " + oldListIterator.previousIndex());
+                returnedList.addAll(prevLM.getChangedKnuthElements(oldList.subList(fromIndex, oldListIterator.previousIndex()),
+                                                                   /*flaggedPenalty,*/ alignment));
+                fromIndex = oldListIterator.previousIndex();
+
+                // there is another block after this one
+                if (prevLM.mustKeepWithNext()
+                    || currLM.mustKeepWithPrevious()) {
+                    // add an infinite penalty to forbid a break between blocks
+                    returnedList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, new Position(this), false));
+                } else if (!((KnuthElement) returnedList.getLast()).isGlue()) {
+                    // add a null penalty to allow a break between blocks
+                    returnedList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
+                }
+            }
+            prevElement = currElement;
+        }
+        if (currElement != null) {
+            BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
+                                             currElement.getLayoutManager();
+/*LF*/      //System.out.println("FLM.getChangedKnuthElements> chiamata da " + fromIndex + " a " + oldList.size());
+            returnedList.addAll(currLM.getChangedKnuthElements(oldList.subList(fromIndex, oldList.size()),
+                                                               /*flaggedPenalty,*/ alignment));
+        }
+
+        // "wrap" the Position stored in each element of returnedList
+        // and add elements to returnList
+        ListIterator listIter = returnedList.listIterator();
+        while (listIter.hasNext()) {
+            returnedElement = (KnuthElement)listIter.next();
+            if (returnedElement.getLayoutManager() != this) {
+                returnedElement.setPosition(new NonLeafPosition(this, returnedElement.getPosition()));
+            }
+            returnList.add(returnedElement);
+        }
+
+        return returnList;
     }
 
     /**
      * @see org.apache.fop.layoutmgr.LayoutManager#addAreas(PositionIterator, LayoutContext)
      */
     public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
-
-        LayoutManager childLM;
+        AreaAdditionUtil.addAreas(parentIter, layoutContext);
+        /*
+        LayoutManager childLM = null;
         LayoutContext lc = new LayoutContext(0);
+        LayoutManager firstLM = null;
+        LayoutManager lastLM = null;
+
+        // "unwrap" the NonLeafPositions stored in parentIter
+        // and put them in a new list; 
+        LinkedList positionList = new LinkedList();
+        Position pos;
         while (parentIter.hasNext()) {
-            LeafPosition lfp = (LeafPosition) parentIter.next();
-            // Add the block areas to Area
-            PositionIterator breakPosIter =  new BreakPossPosIter(
-                    blockBreaks, iStartPos, lfp.getLeafPos() + 1);
-            iStartPos = lfp.getLeafPos() + 1;
-            while ((childLM = breakPosIter.getNextChildLM()) != null) {
-                childLM.addAreas(breakPosIter, lc);
+            pos = (Position)parentIter.next();
+            if (pos instanceof NonLeafPosition) {
+                // pos was created by a child of this FlowLM
+                positionList.add(((NonLeafPosition) pos).getPosition());
+                lastLM = ((NonLeafPosition) pos).getPosition().getLM();
+                if (firstLM == null) {
+                    firstLM = lastLM;
+                }
+            } else {
+                // pos was created by this FlowLM, so it must be ignored
             }
         }
 
+        StackingIter childPosIter = new StackingIter(positionList.listIterator());
+        while ((childLM = childPosIter.getNextChildLM()) != null) {
+            // Add the block areas to Area
+            lc.setFlags(LayoutContext.FIRST_AREA, childLM == firstLM);
+            lc.setFlags(LayoutContext.LAST_AREA, childLM == lastLM);
+            // set space before for the first LM, in order to implement
+            // display-align = center or after
+            lc.setSpaceBefore((childLM == firstLM ? layoutContext.getSpaceBefore() : 0));
+            // set space after for each LM, in order to implement
+            // display-align = distribute
+            lc.setSpaceAfter(layoutContext.getSpaceAfter());
+            lc.setStackLimit(layoutContext.getStackLimit());
+            childLM.addAreas(childPosIter, lc);
+        }*/
+
         flush();
     }
 
index c2027067057b72451ac89496774773112676607b..b02e96c98275db6281901c9b1474954e99fe2ca8 100755 (executable)
@@ -179,6 +179,7 @@ public class InlineLayoutManager extends InlineStackingLayoutManager
         return null;
     }
 
+    /*
     public KnuthElement addALetterSpaceTo(KnuthElement element) {
         NonLeafPosition savedPos = (NonLeafPosition) element.getPosition();
         element.setPosition(savedPos.getPosition());
@@ -325,6 +326,6 @@ public class InlineLayoutManager extends InlineStackingLayoutManager
             returnList.add(returnedElement);
         }
         return returnList;
-    }
+    }*/
 }
 
index 767e901b6d37eadbcecde77161d0fe724a516eef..98d7c35e5f8f62a3c1b43192e047f6406e76af4e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2004 The Apache Software Foundation.
+ * Copyright 2004-2005 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@
  
 package org.apache.fop.layoutmgr;
 
-import java.util.LinkedList;
 import java.util.List;
 
 /**
@@ -26,25 +25,15 @@ import java.util.List;
  */
 public interface InlineLevelLayoutManager extends LayoutManager {
 
-    /**
-     * Get a sequence of KnuthElements representing the content 
-     * of the node assigned to the LM
-     * 
-     * @param context   the LayoutContext used to store layout information
-     * @param alignment the desired text alignement
-     * @return          the list of KnuthElements
-     */
-    LinkedList getNextKnuthElements(LayoutContext context, int alignment);
-
     /**
      * Tell the LM to modify its data, adding a letter space 
-     * to the word fragment represented by the given element,
-     * and returning a corrected element
+     * to the word fragment represented by the given elements,
+     * and returning the corrected elements
      *
-     * @param element the element which must be given one more letter space
-     * @return        the new element replacing the old one
+     * @param oldList the elements which must be given one more letter space
+     * @return        the new elements replacing the old ones
      */
-    KnuthElement addALetterSpaceTo(KnuthElement element);
+    List addALetterSpaceTo(List oldList);
 
     /**
      * Get the word chars corresponding to the given position
@@ -70,15 +59,4 @@ public interface InlineLevelLayoutManager extends LayoutManager {
      */
     boolean applyChanges(List oldList);
 
-    /**
-     * Get a sequence of KnuthElements representing the content 
-     * of the node assigned to the LM, after changes have been applied
-     *
-     * @param oldList        the elements to replace
-     * @param flaggedPenalty the penalty value for hyphenated lines
-     * @param alignment      the desired text alignment
-     * @return               the updated list of KnuthElements
-     */
-    LinkedList getChangedKnuthElements(List oldList, int flaggedPenalty,
-                                       int alignment);
 }
index 92fd5d7d6ff259d66f2f49dfd0bf925870c65234..1993145769e427e04f1015157811019ba49774dd 100644 (file)
@@ -20,11 +20,14 @@ package org.apache.fop.layoutmgr;
 
 import java.util.LinkedList;
 import java.util.Iterator;
+import java.util.List;
 import java.util.ListIterator;
 import java.util.HashMap;
 
 import org.apache.fop.fo.FObj;
+import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
 import org.apache.fop.fo.properties.SpaceProperty;
+import org.apache.fop.traits.InlineProps;
 import org.apache.fop.traits.SpaceVal;
 import org.apache.fop.area.Area;
 import org.apache.fop.area.inline.InlineArea;
@@ -37,7 +40,8 @@ import org.apache.fop.traits.MinOptMax;
  * which stack children in the inline direction, such as Inline or
  * Line. It should not be instantiated directly.
  */
-public class InlineStackingLayoutManager extends AbstractLayoutManager {
+public class InlineStackingLayoutManager extends AbstractLayoutManager 
+                                         implements InlineLevelLayoutManager {
 
 
     private static class StackingIter extends PositionIterator {
@@ -69,7 +73,7 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager {
     private Area currentArea; // LineArea or InlineParent
 
     private BreakPoss prevBP;
-    protected LayoutContext childLC ;
+    protected LayoutContext childLC;
 
     private LayoutManager lastChildLM = null; // Set when return last breakposs
     private boolean bAreaCreated = false;
@@ -534,7 +538,7 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager {
 
     // Current child layout context
     protected LayoutContext getContext() {
-        return childLC ;
+        return childLC;
     }
 
     protected void addSpace(Area parentArea, MinOptMax spaceRange,
@@ -558,5 +562,214 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager {
             }
         }
     }
+
+    public LinkedList getNextKnuthElements(LayoutContext lc, int alignment) {
+        InlineLevelLayoutManager curLM;
+
+        // the list returned by child LM
+        LinkedList returnedList;
+        KnuthElement returnedElement;
+
+        // the list which will be returned to the parent LM
+        LinkedList returnList = new LinkedList();
+
+        SpaceSpecifier leadingSpace = lc.getLeadingSpace();
+
+        if (lc.startsNewArea()) {
+            // First call to this LM in new parent "area", but this may
+            // not be the first area created by this inline
+            childLC = new LayoutContext(lc);
+            lc.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart()));
+
+            // Check for "fence"
+            if (hasLeadingFence(!lc.isFirstArea())) {
+                // Reset leading space sequence for child areas
+                leadingSpace = new SpaceSpecifier(false);
+            }
+            // Reset state variables
+            clearPrevIPD(); // Clear stored prev content dimensions
+        }
+
+        while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) {
+            // get KnuthElements from curLM
+            returnedList = curLM.getNextKnuthElements(lc, alignment);
+            if (returnedList != null) {
+                // "wrap" the Position stored in each element of returnedList
+                ListIterator listIter = returnedList.listIterator();
+                while (listIter.hasNext()) {
+                    returnedElement = (KnuthElement) listIter.next();
+                    returnedElement.setPosition
+                        (new NonLeafPosition(this,
+                                             returnedElement.getPosition()));
+                    returnList.add(returnedElement);
+                }
+                return returnList;
+            } else {
+                // curLM returned null because it finished;
+                // just iterate once more to see if there is another child
+            }
+        }
+        setFinished(true);
+        return null;
+    }
+
+    public List addALetterSpaceTo(List oldList) {
+        // old list contains only a box, or the sequence: box penalty glue box
+
+        ListIterator oldListIterator = oldList.listIterator();
+        KnuthElement element = null;
+        // "unwrap" the Position stored in each element of oldList
+        while (oldListIterator.hasNext()) {
+            element = (KnuthElement) oldListIterator.next();
+            element.setPosition(((NonLeafPosition)element.getPosition()).getPosition());
+        }
+
+        oldList = ((InlineLevelLayoutManager)
+                   element.getLayoutManager()).addALetterSpaceTo(oldList);
+
+        // "wrap" againg the Position stored in each element of oldList
+        oldListIterator = oldList.listIterator();
+        while (oldListIterator.hasNext()) {
+            element = (KnuthElement) oldListIterator.next();
+            element.setPosition(new NonLeafPosition(this, element.getPosition()));
+        }
+
+        return oldList;
+    }
+
+    public void getWordChars(StringBuffer sbChars, Position pos) {
+        Position newPos = ((NonLeafPosition) pos).getPosition();
+        ((InlineLevelLayoutManager)
+         newPos.getLM()).getWordChars(sbChars, newPos);
+    }
+
+    public void hyphenate(Position pos, HyphContext hc) {
+        Position newPos = ((NonLeafPosition) pos).getPosition();
+        ((InlineLevelLayoutManager)
+         newPos.getLM()).hyphenate(newPos, hc);
+    }
+
+    public boolean applyChanges(List oldList) {
+        // "unwrap" the Positions stored in the elements
+        ListIterator oldListIterator = oldList.listIterator();
+        KnuthElement oldElement;
+        while (oldListIterator.hasNext()) {
+            oldElement = (KnuthElement) oldListIterator.next();
+            oldElement.setPosition
+                (((NonLeafPosition) oldElement.getPosition()).getPosition());
+        }
+        // reset the iterator
+        oldListIterator = oldList.listIterator();
+
+        InlineLevelLayoutManager prevLM = null;
+        InlineLevelLayoutManager currLM;
+        int fromIndex = 0;
+
+        boolean bSomethingChanged = false;
+        while(oldListIterator.hasNext()) {
+            oldElement = (KnuthElement) oldListIterator.next();
+            currLM = (InlineLevelLayoutManager) oldElement.getLayoutManager();
+            // initialize prevLM
+            if (prevLM == null) {
+                prevLM = currLM;
+            }
+
+            if (currLM != prevLM || !oldListIterator.hasNext()) {
+                if (oldListIterator.hasNext()) {
+                    bSomethingChanged
+                        = prevLM.applyChanges(oldList.subList(fromIndex, oldListIterator.previousIndex()))
+                        || bSomethingChanged;
+                    prevLM = currLM;
+                    fromIndex = oldListIterator.previousIndex();
+                } else if (currLM == prevLM) {
+                    bSomethingChanged
+                        = prevLM.applyChanges(oldList.subList(fromIndex, oldList.size()))
+                        || bSomethingChanged;
+                } else {
+                    bSomethingChanged
+                        = prevLM.applyChanges(oldList.subList(fromIndex, oldListIterator.previousIndex()))
+                        || bSomethingChanged;
+                    bSomethingChanged
+                        = currLM.applyChanges(oldList.subList(oldListIterator.previousIndex(), oldList.size()))
+                        || bSomethingChanged;
+                }
+            }
+        }
+
+        // "wrap" again the Positions stored in the elements
+        oldListIterator = oldList.listIterator();
+        while (oldListIterator.hasNext()) {
+            oldElement = (KnuthElement) oldListIterator.next();
+            oldElement.setPosition
+                (new NonLeafPosition(this, oldElement.getPosition()));
+        }
+        return bSomethingChanged;
+    }
+
+    public LinkedList getChangedKnuthElements(List oldList, /*int flaggedPenalty,*/ int alignment) {
+        // "unwrap" the Positions stored in the elements
+        ListIterator oldListIterator = oldList.listIterator();
+        KnuthElement oldElement;
+        while (oldListIterator.hasNext()) {
+            oldElement = (KnuthElement) oldListIterator.next();
+            oldElement.setPosition
+                (((NonLeafPosition) oldElement.getPosition()).getPosition());
+        }
+        // reset the iterator
+        oldListIterator = oldList.listIterator();
+
+        KnuthElement returnedElement;
+        LinkedList returnedList = new LinkedList();
+        LinkedList returnList = new LinkedList();
+        InlineLevelLayoutManager prevLM = null;
+        InlineLevelLayoutManager currLM;
+        int fromIndex = 0;
+
+        while(oldListIterator.hasNext()) {
+            oldElement = (KnuthElement) oldListIterator.next();
+            currLM = (InlineLevelLayoutManager) oldElement.getLayoutManager();
+            if (prevLM == null) {
+                prevLM = currLM;
+            }
+
+            if (currLM != prevLM || !oldListIterator.hasNext()) {
+                if (oldListIterator.hasNext()) {
+                    returnedList.addAll
+                        (prevLM.getChangedKnuthElements
+                         (oldList.subList(fromIndex,
+                                          oldListIterator.previousIndex()),
+                          /*flaggedPenalty,*/ alignment));
+                    prevLM = currLM;
+                    fromIndex = oldListIterator.previousIndex();
+                } else if (currLM == prevLM) {
+                    returnedList.addAll
+                        (prevLM.getChangedKnuthElements
+                         (oldList.subList(fromIndex, oldList.size()),
+                          /*flaggedPenalty,*/ alignment));
+                } else {
+                    returnedList.addAll
+                        (prevLM.getChangedKnuthElements
+                         (oldList.subList(fromIndex,
+                                          oldListIterator.previousIndex()),
+                          /*flaggedPenalty,*/ alignment));
+                    returnedList.addAll
+                        (currLM.getChangedKnuthElements
+                         (oldList.subList(oldListIterator.previousIndex(),
+                                          oldList.size()),
+                          /*flaggedPenalty,*/ alignment));
+                }
+            }
+        }
+
+        // "wrap" the Position stored in each element of returnedList
+        ListIterator listIter = returnedList.listIterator();
+        while (listIter.hasNext()) {
+            returnedElement = (KnuthElement) listIter.next();
+            returnedElement.setPosition
+                (new NonLeafPosition(this, returnedElement.getPosition()));
+            returnList.add(returnedElement);
+        }
+        return returnList;
+    }
 }
 
diff --git a/src/java/org/apache/fop/layoutmgr/KnuthBlockBox.java b/src/java/org/apache/fop/layoutmgr/KnuthBlockBox.java
new file mode 100644 (file)
index 0000000..896532e
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import org.apache.fop.traits.MinOptMax;
+
+public class KnuthBlockBox extends KnuthBox {
+    
+    private MinOptMax ipdRange;
+    private int bpd;
+
+    public KnuthBlockBox(int w, MinOptMax range, int bpdim, Position pos, boolean bAux) {
+        super(w, pos, bAux);
+        ipdRange = (MinOptMax) range.clone();
+        bpd = bpdim;
+    }
+
+    public MinOptMax getIPDRange() {
+        return (MinOptMax) ipdRange.clone();
+    }
+
+    public int getBPD() {
+        return bpd;
+    }
+}
\ No newline at end of file
index 8479f77d6f5b404bd207d02b17f6daac4a0ae276..4bc7cd24fe160ecadcc19ff8517ad9266a31305d 100644 (file)
@@ -32,49 +32,20 @@ package org.apache.fop.layoutmgr;
  * positioning, and the methods used to get them.
  */
 public class KnuthBox extends KnuthElement {
-    private int lead;
-    private int total;
-    private int middle;
 
     /**
      * Create a new KnuthBox.
      *
      * @param w    the width of this box
-     * @param l    the height of this box above the main baseline
-     * @param t    the total height of this box
-     * @param m    the height of this box above and below the middle baseline
      * @param pos  the Position stored in this box
      * @param bAux is this box auxiliary?
      */
-    public KnuthBox(int w, int l, int t, int m, Position pos, boolean bAux) {
+    public KnuthBox(int w, Position pos, boolean bAux) {
         super(w, pos, bAux);
-        lead = l;
-        total = t;
-        middle = m;
     }
 
     public boolean isBox() {
         return true;
     }
 
-    /**
-     * Return the height of this box above the main baseline.
-     */
-    public int getLead() {
-        return lead;
-    }
-
-    /**
-     * Return the total height of this box.
-     */
-    public int getTotal() {
-        return total;
-    }
-
-    /**
-     * Return the height of this box above and below the middle baseline.
-     */
-    public int getMiddle() {
-        return middle;
-    }
 }
index e377ce351478c3c8d0f19c901d5915d400b9f7e9..9b5f7862753e1b9425e6aaa6e02a4a5e622651ab 100644 (file)
@@ -45,8 +45,10 @@ package org.apache.fop.layoutmgr;
  * to get these values.
  */
 public class KnuthGlue extends KnuthElement {
+    
     private int stretchability;
     private int shrinkability;
+    private int adjustmentClass = -1;
 
     /**
      * Create a new KnuthGlue.
@@ -63,6 +65,14 @@ public class KnuthGlue extends KnuthElement {
         shrinkability = z;
     }
 
+    public KnuthGlue(int w, int y, int z,
+            int iAdjClass, Position pos, boolean bAux) {
+        super(w, pos, bAux);
+        stretchability = y;
+        shrinkability = z;
+        adjustmentClass = iAdjClass;
+    }
+
     public boolean isGlue() {
         return true;
     }
@@ -80,4 +90,9 @@ public class KnuthGlue extends KnuthElement {
     public int getZ() {
         return shrinkability;
     }
+    
+    public int getAdjustmentClass() {
+        return adjustmentClass;
+    }
+    
 }
diff --git a/src/java/org/apache/fop/layoutmgr/KnuthInlineBox.java b/src/java/org/apache/fop/layoutmgr/KnuthInlineBox.java
new file mode 100644 (file)
index 0000000..cca2bbb
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+public class KnuthInlineBox extends KnuthBox {
+    
+    private int lead;
+    private int total;
+    private int middle;
+
+    /**
+     * Create a new KnuthBox.
+     *
+     * @param w    the width of this box
+     * @param l    the height of this box above the main baseline
+     * @param t    the total height of this box
+     * @param m    the height of this box above and below the middle baseline
+     * @param pos  the Position stored in this box
+     * @param bAux is this box auxiliary?
+     */
+    public KnuthInlineBox(int w, int l, int t, int m, Position pos, boolean bAux) {
+        super(w, pos, bAux);
+        lead = l;
+        total = t;
+        middle = m;
+    }
+
+    /**
+     * @return the height of this box above the main baseline.
+     */
+    public int getLead() {
+        return lead;
+    }
+
+    /**
+     * @return the total height of this box.
+     */
+    public int getTotal() {
+        return total;
+    }
+
+    /**
+     * @return the height of this box above and below the middle baseline.
+     */
+    public int getMiddle() {
+        return middle;
+    }
+}
\ No newline at end of file
index baf3492d67e8c720ceb971ef670427d83c8e4a77..cf08b631553109195acd5535ae60586e1a38502c 100644 (file)
  */
 
 /* $Id$ */
+
 package org.apache.fop.layoutmgr;
 
-import java.util.ArrayList;
+import java.util.List;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -60,7 +61,7 @@ public class KnuthParagraph {
     /**
      * The paragraph of KnuthElements.
      */
-    private ArrayList par;
+    private List par;
     
     /**
      * The width of a line.
@@ -114,7 +115,7 @@ public class KnuthParagraph {
     
     protected static Log log = LogFactory.getLog(KnuthParagraph.class);
     
-    public KnuthParagraph(ArrayList par) {
+    public KnuthParagraph(List par) {
         this.best = new BestRecords();
         this.par = par;
     }
index 83b01313205973f08ba22e753a810c219e13d94a..fd88fa76ac5dd1a128bd7ffc003db6e5c7c8a2f0 100644 (file)
@@ -37,8 +37,10 @@ package org.apache.fop.layoutmgr;
  * be chosen as breaking points for consecutive lines.
  */
 public class KnuthPenalty extends KnuthElement {
+
     private int penalty;
     private boolean bFlagged; 
+    private int breakClass = -1;
 
     /**
      * Create a new KnuthPenalty.
@@ -55,6 +57,14 @@ public class KnuthPenalty extends KnuthElement {
         bFlagged = f;
     }
 
+    public KnuthPenalty(int w, int p, boolean f,
+            int iBreakClass, Position pos, boolean bAux) {
+        super(w, pos, bAux);
+        penalty = p;
+        bFlagged = f;
+        breakClass = iBreakClass;
+    }
+
     public boolean isPenalty() {
         return true;
     }
@@ -76,4 +86,9 @@ public class KnuthPenalty extends KnuthElement {
     public boolean isForcedBreak() {
         return penalty == -KnuthElement.INFINITE;
     }
+    
+    public int getBreakClass() {
+        return breakClass;
+    }
+    
 }
diff --git a/src/java/org/apache/fop/layoutmgr/KnuthSequence.java b/src/java/org/apache/fop/layoutmgr/KnuthSequence.java
new file mode 100644 (file)
index 0000000..35fd84d
--- /dev/null
@@ -0,0 +1,36 @@
+
+package org.apache.fop.layoutmgr;
+
+import java.util.LinkedList;
+
+public class KnuthSequence extends LinkedList {
+    // number of KnuthElements added by the LineLayoutManager
+    public int ignoreAtStart = 0;
+    public int ignoreAtEnd = 0;
+
+    public KnuthSequence() {
+    }
+
+    public void startSequence() {
+    }
+
+    public KnuthSequence endSequence() {
+        // remove glue and penalty item at the end of the paragraph
+        while (this.size() > ignoreAtStart
+               && !((KnuthElement)this.get(this.size() - 1)).isBox()) {
+            this.remove(this.size() - 1);
+        }
+        if (this.size() > ignoreAtStart) {
+            // add the elements representing the space at the end of the last line
+            // and the forced break
+/*LF*/      this.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, null, false));
+/*LF*/      this.add(new KnuthGlue(0, 10000000, 0, null, false));
+            this.add(new KnuthPenalty(0, -KnuthElement.INFINITE, false, null, false));
+            ignoreAtEnd = 3;
+            return this;
+        } else {
+            this.clear();
+            return null;
+        }
+    }
+}
index a6f41d5c3dcbf527aaa7b9e798e720cedfadfbaf..6452f75bcb8dd288e9e55cdb061582102d8e0508 100644 (file)
@@ -87,6 +87,10 @@ public class LayoutContext {
     private int iLineHeight;
     private int iBaseline;
     private int iMiddleShift;
+    private int iTopShift; /*LF*/
+    private int iBottomShift; /*LF*/
+    private int iSpaceBefore; /*LF*/
+    private int iSpaceAfter; /*LF*/
 
     public LayoutContext(LayoutContext parentLC) {
         this.flags = parentLC.flags;
@@ -100,6 +104,10 @@ public class LayoutContext {
         this.iLineHeight = parentLC.iLineHeight;
         this.iBaseline = parentLC.iBaseline;
         this.iMiddleShift = parentLC.iMiddleShift;
+/*LF*/  this.iTopShift = parentLC.iTopShift;
+/*LF*/  this.iBottomShift = parentLC.iBottomShift;
+/*LF*/  this.iSpaceBefore = parentLC.iSpaceBefore;
+/*LF*/  this.iSpaceAfter = parentLC.iSpaceAfter;
         // Copy other fields as necessary. Use clone???
     }
 
@@ -235,6 +243,38 @@ public class LayoutContext {
         return iBaseline + iMiddleShift;
     }
     
+    public void setTopShift(int ts) {
+        iTopShift = ts;
+    }
+
+    public int getTopBaseline() {
+        return iBaseline + iTopShift;
+    }
+
+    public void setBottomShift(int bs) {
+        iBottomShift = bs;
+    }
+
+    public int getBottomBaseline() {
+        return iBaseline + iBottomShift;
+    }
+
+    public int getSpaceBefore() {
+        return iSpaceBefore;
+    }
+    
+    public void setSpaceBefore(int sp) {
+        iSpaceBefore = sp;
+    }
+
+    public int getSpaceAfter() {
+        return iSpaceAfter;
+    }
+
+    public void setSpaceAfter(int sp) {
+        iSpaceAfter = sp;
+    }
+    
     public String toString() {
         return "Layout Context:" +
         "\nStack Limit: \t" + (getStackLimit() == null ? "null" : getStackLimit().toString()) +
index 93fd36df572e4db3f4509728ccd0de023abadf8b..9c88651e9f0d08df882b957e22935aa2b291925c 100644 (file)
@@ -18,6 +18,7 @@
  
 package org.apache.fop.layoutmgr;
 
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
@@ -254,4 +255,29 @@ public interface LayoutManager {
      * @param newLMs the list of LMs to be added
      */
     void addChildLMs(List newLMs);
+
+    /**
+     * Get a sequence of KnuthElements representing the content 
+     * of the node assigned to the LM
+     * 
+     * @param context   the LayoutContext used to store layout information
+     * @param alignment the desired text alignement
+     * @return          the list of KnuthElements
+     */
+    LinkedList getNextKnuthElements(LayoutContext context, int alignment);
+
+    /**
+     * Get a sequence of KnuthElements representing the content 
+     * of the node assigned to the LM, after changes have been applied
+     *
+     * @param oldList        the elements to replace
+     * @param flaggedPenalty the penalty value for hyphenated lines
+     * @param alignment      the desired text alignment
+     * @return               the updated list of KnuthElements
+     */
+    LinkedList getChangedKnuthElements(List oldList, /*int flaggedPenalty,*/
+                                       int alignment);
+
+    public static final int FLAGGED_PENALTY = 50;
+
 }
index 4cb87a41955df8e33d957f10445e9f1799d69a59..45734d03039c696820232e09d4407ab45e9f8cbd 100644 (file)
@@ -263,7 +263,7 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
                                 lead, total, middle);
 
         // node is a fo:Leader
-        returnList.add(new KnuthBox(0, areaInfo.lead, areaInfo.total,
+        returnList.add(new KnuthInlineBox(0, areaInfo.lead, areaInfo.total,
                                     areaInfo.middle,
                                     new LeafPosition(this, -1), true));
         returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
@@ -273,7 +273,7 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
                            areaInfo.ipdArea.max - areaInfo.ipdArea.opt,
                            areaInfo.ipdArea.opt - areaInfo.ipdArea.min, 
                            new LeafPosition(this, 0), false));
-        returnList.add(new KnuthBox(0, areaInfo.lead, areaInfo.total,
+        returnList.add(new KnuthInlineBox(0, areaInfo.lead, areaInfo.total,
                                     areaInfo.middle,
                                     new LeafPosition(this, -1), true));
 
@@ -308,7 +308,7 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
 
         LinkedList returnList = new LinkedList();
 
-        returnList.add(new KnuthBox(0, areaInfo.lead, areaInfo.total,
+        returnList.add(new KnuthInlineBox(0, areaInfo.lead, areaInfo.total,
                                     areaInfo.middle,
                                     new LeafPosition(this, -1), true));
         returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
@@ -318,7 +318,7 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
                            areaInfo.ipdArea.max - areaInfo.ipdArea.opt,
                            areaInfo.ipdArea.opt - areaInfo.ipdArea.min, 
                            new LeafPosition(this, 0), false));
-        returnList.add(new KnuthBox(0, areaInfo.lead, areaInfo.total,
+        returnList.add(new KnuthInlineBox(0, areaInfo.lead, areaInfo.total,
                                     areaInfo.middle,
                                     new LeafPosition(this, -1), true));
 
index b81adc6861e2dc6bc824eb207d116abc19b3f59b..a3dcba1114faafbea9c0fd2d828dce416ca73e80 100644 (file)
@@ -77,6 +77,12 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager
         super(node);
     }
 
+    /**
+     * Create a Leaf node layout mananger.
+     */
+    public LeafNodeLayoutManager() {
+    }
+
     /**
      * get the inline area.
      * @param context the context used to create the area
@@ -194,10 +200,10 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager
                 curArea.setOffset(context.getMiddleBaseline() - bpd / 2);
             break;
             case EN_TOP:
-                //curArea.setOffset(0);
+                curArea.setOffset(context.getTopBaseline());
             break;
             case EN_BOTTOM:
-                curArea.setOffset(context.getLineHeight() - bpd);
+                curArea.setOffset(context.getBottomBaseline() - bpd);
             break;
             case EN_BASELINE:
             default:
@@ -269,21 +275,19 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager
 
         // node is a fo:ExternalGraphic, fo:InstreamForeignObject,
         // fo:PageNumber or fo:PageNumberCitation
-        returnList.add(new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
+        returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
                                     areaInfo.total, areaInfo.middle,
                                     new LeafPosition(this, 0), false));
         setFinished(true);
         return returnList;
     }
 
-    public void getWordChars(StringBuffer sbChars, Position bp) {
+    public List addALetterSpaceTo(List oldList) {
+        // return the unchanged elements
+        return oldList;
     }
 
-    public KnuthElement addALetterSpaceTo(KnuthElement element) {
-        // return the unchanged box object
-        return new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
-                            areaInfo.total, areaInfo.middle,
-                            new LeafPosition(this, 0), false);
+    public void getWordChars(StringBuffer sbChars, Position pos) {
     }
 
     public void hyphenate(Position pos, HyphContext hc) {
@@ -295,7 +299,7 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager
     }
 
     public LinkedList getChangedKnuthElements(List oldList,
-                                              int flaggedPenalty,
+                                              /*int flaggedPenalty,*/
                                               int alignment) {
         if (isFinished()) {
             return null;
@@ -305,9 +309,9 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager
 
         // fobj is a fo:ExternalGraphic, fo:InstreamForeignObject,
         // fo:PageNumber or fo:PageNumberCitation
-        returnList.add(new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
-                                    areaInfo.total, areaInfo.middle,
-                                    new LeafPosition(this, 0), true));
+        returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
+                                          areaInfo.total, areaInfo.middle,
+                                          new LeafPosition(this, 0), true));
 
         setFinished(true);
         return returnList;
index e21d2e5b81f3358efe2a2fb3b235aeaca8fbac98..187225c3efdab042a29c3e5d167495526d566ac2 100644 (file)
@@ -46,31 +46,11 @@ import org.apache.fop.traits.MinOptMax;
  * creates a line area to contain the inline areas added by the
  * child layout managers.
  */
-public class LineLayoutManager extends InlineStackingLayoutManager {
+public class LineLayoutManager extends InlineStackingLayoutManager 
+                               implements BlockLevelLayoutManager {
+
     private Block fobj; 
     
-    /**
-     * Create a new Line Layout Manager.
-     * This is used by the block layout manager to create
-     * line managers for handling inline areas flowing into line areas.
-     *
-     * @param lh the default line height
-     * @param l the default lead, from top to baseline
-     * @param f the default follow, from baseline to bottom
-     */
-    public LineLayoutManager(Block node, int lh, int l, int f, int ms) {
-        super(node);
-        fobj = node;
-        // the child FObj are owned by the parent BlockLM
-        // this LM has all its childLMs preloaded
-        fobjIter = null;
-        lineHeight = lh;
-        lead = l;
-        follow = f;
-        middleShift = ms;
-        initialize(); // Normally done when started by parent!
-    }
-
     /**
      * @see org.apache.fop.layoutmgr.AbstractLayoutManager#initProperties()
      */
@@ -96,36 +76,51 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
     private static class LineBreakPosition extends LeafPosition {
         // int iPos;
         int iParIndex; // index of the Paragraph this Position refers to
+/*LF*/  int availableShrink;
+/*LF*/  int availableStretch;
+/*LF*/  int difference;
         double dAdjust; // Percentage to adjust (stretch or shrink)
         double ipdAdjust; // Percentage to adjust (stretch or shrink)
         int startIndent;
         int lineHeight;
+/*LF*/  int lineWidth;
         int baseline;
+/*LF*/  int topShift;
+/*LF*/  int bottomShift;
 
         LineBreakPosition(LayoutManager lm, int index, int iBreakIndex,
-                          double ipdA, double adjust, int ind, int lh, int bl) {
+                          int shrink, int stretch, int diff,
+                          double ipdA, double adjust, int ind,
+                          int lh, int lw, int bl, int ts, int bs) {
             super(lm, iBreakIndex);
-            // iPos = iBreakIndex;
+/*LF*/      availableShrink = shrink;
+/*LF*/      availableStretch = stretch;
+/*LF*/      difference = diff;
             iParIndex = index;
             ipdAdjust = ipdA;
             dAdjust = adjust;
             startIndent = ind;
             lineHeight = lh;
+/*LF*/      lineWidth = lw;
             baseline = bl;
+/*LF*/      topShift = ts;
+/*LF*/      bottomShift = bs;
         }
         
     }
 
 
     /** Break positions returned by inline content. */
-    private List vecInlineBreaks = new ArrayList();
+    private List vecInlineBreaks = new java.util.ArrayList();
 
     private BreakPoss prevBP = null; // Last confirmed break position
     private int bTextAlignment = EN_JUSTIFY;
     private int bTextAlignmentLast;
     private int effectiveAlignment;
     private Length textIndent;
+    private int iIndents = 0;
     private CommonHyphenation hyphProps;
+    //private LayoutProps layoutProps;     /*LF*/
 
     private int lineHeight;
     private int lead;
@@ -133,8 +128,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
     // offset of the middle baseline with respect to the main baseline
     private int middleShift;
 
-    private ArrayList knuthParagraphs = null;
-    private ArrayList breakpoints = null;
+    private List knuthParagraphs = null;
+    private List breakpoints = null;
     private int iReturnedLBP = 0;
     private int iStartElement = 0;
     private int iEndElement = 0;
@@ -143,6 +138,10 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
     // penalty value for flagged penalties
     private int flaggedPenalty = 50;
 
+    private LineLayoutPossibilities lineLayouts;
+    private List lineLayoutsList;
+    private int iLineWidth = 0; /*LF*/
+
     // this constant is used to create elements when text-align is center:
     // every TextLM descendant of LineLM must use the same value, 
     // otherwise the line breaking algorithm does not find the right
@@ -164,20 +163,37 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
     }
 
     // this class represents a paragraph
-    public class Paragraph extends ArrayList {
-        // number of KnuthElements added by the LineLayoutManager
-        public int ignoreAtStart = 0;
-        public int ignoreAtEnd = 0;
-        // minimum space at the end of the last line (in millipoints)
-        public int lineFillerWidth;
-
-        public void startParagraph(int lineWidth) {
+    private class Paragraph extends KnuthSequence {
+        // space at the end of the last line (in millipoints)
+        private MinOptMax lineFiller;
+        private int textAlignment;
+        private int textAlignmentLast;
+        private int textIndent;
+        private int lineWidth;
+        // the LM which created the paragraph
+        private LineLayoutManager layoutManager;
+
+        public Paragraph(LineLayoutManager llm, int alignment, int alignmentLast,
+                         int indent) {
+            super();
+            layoutManager = llm;
+            textAlignment = alignment;
+            textAlignmentLast = alignmentLast;
+            textIndent = indent;
+        }
+
+        public void startParagraph(int lw) {
+            lineWidth = lw;
+            startSequence();
+        }
+
+        public void startSequence() {
             // set the minimum amount of empty space at the end of the
             // last line
             if (bTextAlignment == EN_CENTER) {
-                lineFillerWidth = 0
+                lineFiller = new MinOptMax(0)
             } else {
-                lineFillerWidth = (int)(lineWidth / 12); 
+                lineFiller = new MinOptMax(0, (int)(lineWidth / 12), lineWidth); 
             }
 
             // add auxiliary elements at the beginning of the paragraph
@@ -190,17 +206,24 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
             // add the element representing text indentation
             // at the beginning of the first paragraph
             if (knuthParagraphs.size() == 0
-                && textIndent.getValue() != 0) {
-                this.add(new KnuthBox(textIndent.getValue(), 0, 0, 0,
+                        && fobj.getTextIndent().getValue() != 0) {
+                this.add(new KnuthInlineBox(fobj.getTextIndent().getValue(), 0, 0, 0,
                                       null, false));
                 ignoreAtStart ++;
             }
         }
 
         public void endParagraph() {
+            KnuthSequence finishedPar = this.endSequence();
+            if (finishedPar != null) {
+                knuthParagraphs.add(finishedPar);
+            }
+        }
+
+        public KnuthSequence endSequence() {
             // remove glue and penalty item at the end of the paragraph
             while (this.size() > ignoreAtStart
-                   && !((KnuthElement) this.get(this.size() - 1)).isBox()) {
+                   && !((KnuthElement)this.get(this.size() - 1)).isBox()) {
                 this.remove(this.size() - 1);
             }
             if (this.size() > ignoreAtStart) {
@@ -215,10 +238,11 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
                     // add the elements representing the space
                     // at the end of the last line
                     // and the forced break
-                    this.add(new KnuthPenalty(0, KnuthElement.INFINITE,
+                    this.add(new KnuthPenalty(0, KnuthElement.INFINITE, 
                                               false, null, false));
-                    this.add(new KnuthGlue(lineFillerWidth, 10000000, 0,
-                                           null, false));
+                    this.add(new KnuthGlue(lineFiller.opt, 
+                            lineFiller.max - lineFiller.opt, 
+                            lineFiller.opt - lineFiller.min, null, false));
                     this.add(new KnuthPenalty(0, -KnuthElement.INFINITE,
                                               false, null, false));
                     ignoreAtEnd = 3;
@@ -228,27 +252,249 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
                                               false, null, false));
                     ignoreAtEnd = 1;
                 }
-                knuthParagraphs.add(this);
+                return this;
+            } else {
+                this.clear();
+                return null;
             }
         }
 
-        public KnuthElement getLast() {
-            int idx = size();
-            if (idx == 0) {
-                return null; 
+    }
+
+    private class LineBreakingAlgorithm extends BreakingAlgorithm {
+        private LineLayoutManager thisLLM;
+        private int pageAlignment;
+        private int activePossibility;
+        private int addedPositions;
+        private int textIndent;
+        private int fillerMinWidth;
+        private int lineHeight;
+        private int lead;
+        private int follow;
+        private int middleshift;
+        private int maxDiff;
+        private static final double MAX_DEMERITS = 10e6;
+
+        public LineBreakingAlgorithm (int pageAlign,
+                                      int textAlign, int textAlignLast,
+                                      int indent, int fillerWidth,
+                                      int lh, int ld, int fl, int ms, boolean first,
+                                      LineLayoutManager llm) {
+            super(textAlign, textAlignLast, first);
+            pageAlignment = pageAlign;
+            textIndent = indent;
+            fillerMinWidth = fillerWidth;
+            lineHeight = lh;
+            lead = ld;
+            follow = fl;
+            middleshift = ms;
+            thisLLM = llm;
+            activePossibility = -1;
+            maxDiff = fobj.getWidows() >= fobj.getOrphans() 
+                    ? fobj.getWidows()
+                    : fobj.getOrphans();
+        }
+
+        public void updateData1(int number, double demerits) {
+            lineLayouts.addPossibility(number, demerits);
+            //System.out.println("Possibilita' di layout in " + number + " linee; break alle posizioni:");
+        }
+
+        public void updateData2(KnuthNode bestActiveNode,
+                                KnuthSequence par,
+                                int total) {
+            // compute indent and adjustment ratio, according to
+            // the value of text-align and text-align-last
+            int indent = 0;
+            int difference = (bestActiveNode.line < total) ? bestActiveNode.difference : bestActiveNode.difference + fillerMinWidth;
+            int textAlign = (bestActiveNode.line < total) ? alignment : alignmentLast;
+            indent += (textAlign == Constants.EN_CENTER) ?
+                          difference / 2 :
+                          (textAlign == Constants.EN_END) ? difference : 0;
+            indent += (bestActiveNode.line == 1 && bFirst) ? 
+                          textIndent : 0;
+            double ratio = (textAlign == Constants.EN_JUSTIFY
+                            || bestActiveNode.adjustRatio < 0) ? bestActiveNode.adjustRatio : 0;
+
+            // add nodes at the beginning of the list, as they are found
+            // backwards, from the last one to the first one
+
+            // the first time this method is called, initialize activePossibility
+            // O SEMPLICEMENTE INIZIALIZZARE activePossibility A 0 NELLA DICHIARAZIONE?
+            if (activePossibility == -1) {
+                activePossibility = 0;
+                addedPositions = 0;
             }
-            return (KnuthElement) get(idx - 1);
+
+            if (addedPositions == lineLayouts.getLineNumber(activePossibility)) {
+                activePossibility ++;
+                addedPositions = 0;
+/*LF*/          //System.out.println(" ");
+            }
+
+/*LF*/      //System.out.println("LLM> (" + (lineLayouts.getLineNumber(activePossibility) - addedPositions) + ") difference = " + difference + " ratio = " + ratio);
+            lineLayouts.addBreakPosition(makeLineBreakPosition(par,
+                                                               (bestActiveNode.line > 1 ? bestActiveNode.previous.position + 1: 0),
+                                                               bestActiveNode.position,
+                                                               bestActiveNode.availableShrink - (addedPositions > 0 ? 0 : ((Paragraph)par).lineFiller.opt - ((Paragraph)par).lineFiller.min), bestActiveNode.availableStretch, difference, ratio, indent),
+                                         activePossibility);
+            addedPositions ++;
+        }
+
+        /* reinizializza le variabili in modo da comportarsi come se
+         * non ci fosse stato un tentativo precedente
+         */
+        public void resetAlgorithm() {
+            activePossibility = -1;
+        }
+
+        private LineBreakPosition makeLineBreakPosition(KnuthSequence par,
+                                                        int firstElementIndex,
+                                                        int lastElementIndex,
+                                                        int availableShrink, int availableStretch, int difference,
+                                                        double ratio,
+                                                        int indent) {
+            // line height calculation
+
+            int halfLeading = (lineHeight - lead - follow) / 2;
+            // height before the main baseline
+    /*LF*/  int lineLead = lead;
+            // maximum size of top and bottom alignment
+    /*LF*/  int maxtb = follow;
+            // max size of middle alignment before and after the middle baseline
+            int middlefollow = maxtb;
+
+/*LF*/      // if line-stacking-strategy is "font-height", the line height
+/*LF*/      // is not affected by its content
+/*LF*/      if (fobj.getLineStackingStrategy() != EN_FONT_HEIGHT) {
+                ListIterator inlineIterator
+                    = par.listIterator(firstElementIndex);
+                for (int j = firstElementIndex;
+                     j <= lastElementIndex;
+                     j++) {
+                    KnuthElement element = (KnuthElement) inlineIterator.next();
+                    if (element.isBox()) {
+                        if (((KnuthInlineBox) element).getLead() > lineLead) {
+                            lineLead = ((KnuthInlineBox) element).getLead();
+                        }
+                        if (((KnuthInlineBox) element).getTotal() > maxtb) {
+                            maxtb = ((KnuthInlineBox) element).getTotal();
+                        }
+                        if (((KnuthInlineBox) element).getMiddle() > lineLead + middleShift) {
+                            lineLead += ((KnuthInlineBox) element).getMiddle()
+                                        - lineLead - middleShift;
+                        }
+                        if (((KnuthInlineBox) element).getMiddle() > middlefollow - middleShift) {
+                            middlefollow += ((KnuthInlineBox) element).getMiddle()
+                                            - middlefollow + middleShift;
+                        }
+                    }
+                }
+
+                if (maxtb - lineLead > middlefollow) {
+                    middlefollow = maxtb - lineLead;
+                }
+/*LF*/      }
+
+    /*LF*/  //lineLead += halfLeading;
+    /*LF*/  //middlefollow += lineHeight - lead - follow - halfLeading;
+
+/*LF*/      constantLineHeight = lineLead + middlefollow + (lineHeight - lead - follow);
+/*LF*/      //System.out.println("teorica: " + lineHeight + " reale: " + (lineLead + middlefollow + (lineHeight - lead - follow)) + " halfleading = " + halfLeading + " e " + (lineHeight - lead - follow - halfLeading));
+
+            return new LineBreakPosition(thisLLM,
+                                         knuthParagraphs.indexOf(par),
+                                         lastElementIndex,
+                                         availableShrink, availableStretch, difference, ratio, 0, indent,
+                                         lineLead + middlefollow + (lineHeight - lead - follow), iLineWidth,
+                                         lineLead + halfLeading,
+                                         - lineLead, middlefollow);
         }
 
-        public KnuthElement removeLast() {
-            int idx = size();
-            if (idx == 0) {
-                return null; 
+        public int findBreakingPoints(Paragraph par, int lineWidth,
+                                      double threshold, boolean force,
+                                      boolean hyphenationAllowed) {
+            return super.findBreakingPoints(par, lineWidth, threshold, force, hyphenationAllowed);
+        }
+
+        protected int filterActiveList() {
+            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() + " possibilita'");
+                while (activeListIterator.hasNext()) {
+                    tempNode = (KnuthNode)activeListIterator.next();
+                    //System.out.println("                      + linee= " + tempNode.line + " demeriti= " + tempNode.totalDemerits);
+                    if (bestActiveNode == null
+                        || tempNode.totalDemerits < bestActiveNode.totalDemerits) {
+                        bestActiveNode = tempNode;
+                    }
+                }
+
+                // scan activeList once again and remove some nodes
+                activeListIterator = activeList.listIterator();
+                tempNode = null;
+                //System.out.println("LBA.filterActiveList> cernita");
+                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 linee= " + tempNode.line + " demeriti= " + tempNode.totalDemerits);
+                        activeListIterator.remove();
+                    } else {
+                        //System.out.println("                     ok linee= " + tempNode.line + " demeriti= " + tempNode.totalDemerits);
+                    }
+                }
+            } else {
+                // leave only bestActiveNode in the activeList
+                KnuthNode tempNode = null;
+                //System.out.println("LBA.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;
+                    }
+                }
+                activeList.add(bestActiveNode);
             }
-            return (KnuthElement) remove(idx - 1);
+            //System.out.println("                      migliore " + bestActiveNode.line);
+            return bestActiveNode.line;
         }
     }
 
+/*LF*/
+    private int constantLineHeight = 12000;
+/*LF*/
+
+    /**
+     * Create a new Line Layout Manager.
+     * This is used by the block layout manager to create
+     * line managers for handling inline areas flowing into line areas.
+     *
+     * @param lh the default line height
+     * @param l the default lead, from top to baseline
+     * @param f the default follow, from baseline to bottom
+     */
+    public LineLayoutManager(Block block, int lh, int l, int f, int ms) {
+        super(block);
+        fobj = block;
+        // the child FObj are owned by the parent BlockLM
+        // this LM has all its childLMs preloaded
+        fobjIter = null;
+        lineHeight = lh;
+        lead = l;
+        follow = f;
+/*LF*/  middleShift = ms;
+        initialize(); // Normally done when started by parent!
+    }
 
     /**
      * Call child layout managers to generate content.
@@ -257,7 +503,13 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
      * @param context the layout context for finding breaks
      * @return the next break position
      */
+    // this method is no longer used
     public BreakPoss getNextBreakPoss(LayoutContext context) {
+        setFinished(true);
+        return null;
+    }
+
+    public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
         // Get a break from currently active child LM
         // Set up constraints for inline level managers
         InlineLevelLayoutManager curLM ; // currently active LM
@@ -284,13 +536,16 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
             // here starts Knuth's algorithm
             KnuthElement thisElement = null;
             LinkedList returnedList = null;
+            iLineWidth = context.getStackLimit().opt;
 
             // convert all the text in a sequence of paragraphs made
             // of KnuthBox, KnuthGlue and KnuthPenalty objects
             boolean bPrevWasKnuthBox = false;
             KnuthBox prevBox = null;
 
-            Paragraph knuthPar = new Paragraph();
+            Paragraph knuthPar = new Paragraph(this, 
+                                               bTextAlignment, bTextAlignmentLast, 
+                                               textIndent.getValue());
             knuthPar.startParagraph(availIPD.opt);
             while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) {
                 if ((returnedList
@@ -302,33 +557,30 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
                     if (thisElement.isBox() && !thisElement.isAuxiliary()
                         && bPrevWasKnuthBox) {
                         prevBox = (KnuthBox) knuthPar.removeLast();
+                        LinkedList oldList = new LinkedList();
                         // if there are two consecutive KnuthBoxes the
                         // first one does not represent a whole word,
                         // so it must be given one more letter space
                         if (!prevBox.isAuxiliary()) {
                             // if letter spacing is constant,
                             // only prevBox needs to be replaced;
-                            knuthPar.add(((InlineLevelLayoutManager)
-                                              prevBox.getLayoutManager())
-                                             .addALetterSpaceTo(prevBox));
+                            oldList.add(prevBox);
                         } else {
                             // prevBox is the last element
                             // in the sub-sequence
                             //   <box> <aux penalty> <aux glue> <aux box>
                             // the letter space is added to <aux glue>,
                             // while the other elements are not changed
-                            KnuthBox auxBox = prevBox;
-                            KnuthGlue auxGlue
-                                = (KnuthGlue) knuthPar.removeLast();
-                            KnuthPenalty auxPenalty
-                                = (KnuthPenalty) knuthPar.removeLast();
-                            prevBox = (KnuthBox) knuthPar.getLast();
-                            knuthPar.add(auxPenalty);
-                            knuthPar.add(((InlineLevelLayoutManager)
-                                              prevBox.getLayoutManager())
-                                             .addALetterSpaceTo(prevBox));
-                            knuthPar.add(auxBox);
+                            oldList.add(prevBox);
+                            oldList.addFirst((KnuthGlue) knuthPar.removeLast());
+                            oldList.addFirst((KnuthPenalty) knuthPar.removeLast());
                         }
+                        // adding a letter space could involve, according to the text
+                        // represented by oldList, replacing a glue element or adding
+                        // new elements
+                        knuthPar.addAll(((InlineLevelLayoutManager)
+                                         prevBox.getLayoutManager())
+                                        .addALetterSpaceTo(oldList));
                     }
 
                     // look at the last element
@@ -355,11 +607,13 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
                         if (knuthPar.size() == 0) {
                             //only a forced linefeed on this line 
                             //-> compensate with a zero width box
-                            knuthPar.add(new KnuthBox(0, 0, 0, 0,
+                            knuthPar.add(new KnuthInlineBox(0, 0, 0, 0,
                                     null, false));
                         }
                         knuthPar.endParagraph();
-                        knuthPar = new Paragraph();
+                        knuthPar = new Paragraph(this, 
+                                                 bTextAlignment, bTextAlignmentLast, 
+                                                 textIndent.getValue());
                         knuthPar.startParagraph(availIPD.opt);
                         bPrevWasKnuthBox = false;
                     }
@@ -383,7 +637,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
         }
 
         //PHASE 2: Create line breaks
-
+        return findOptimalLineBreakingPoints(alignment);
+        /*
         LineBreakPosition lbp = null;
         if (breakpoints == null) {
             // find the optimal line breaking points for each paragraph
@@ -395,10 +650,11 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
                 currPar = (Paragraph) paragraphsIterator.previous();
                 findBreakingPoints(currPar, context.getStackLimit().opt);
             }
-        }
+        }*/
 
         //PHASE 3: Return lines
 
+        /*
         // get a break point from the list
         lbp = (LineBreakPosition) breakpoints.get(iReturnedLBP ++);
         if (iReturnedLBP == breakpoints.size()) {
@@ -409,6 +665,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
         curLineBP.setFlag(BreakPoss.ISLAST, isFinished());
         curLineBP.setStackingSize(new MinOptMax(lbp.lineHeight));
         return curLineBP;
+        */
     }
 
     /**
@@ -421,6 +678,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
      *                  into lines
      * @param lineWidth the desired length ot the lines
      */
+    /*
     private void findBreakingPoints(Paragraph par, int lineWidth) {
         // maximum adjustment ratio permitted
         float maxAdjustment = 1;
@@ -508,18 +766,19 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
              j++) {
             KnuthElement element = (KnuthElement) inlineIterator.next();
             if (element.isBox()) {
-                if (((KnuthBox) element).getLead() > lineLead) {
-                    lineLead = ((KnuthBox) element).getLead();
+                KnuthInlineBox box = (KnuthInlineBox)element;
+                if (box.getLead() > lineLead) {
+                    lineLead = box.getLead();
                 }
-                if (((KnuthBox) element).getTotal() > maxtb) {
-                    maxtb = ((KnuthBox) element).getTotal();
+                if (box.getTotal() > maxtb) {
+                    maxtb = box.getTotal();
                 }
-                if (((KnuthBox) element).getMiddle() > lineLead + middleShift) {
-                    lineLead += ((KnuthBox) element).getMiddle()
+                if (box.getMiddle() > lineLead + middleShift) {
+                    lineLead += box.getMiddle()
                                 - lineLead - middleShift;
                 }
-                if (((KnuthBox) element).getMiddle() > middlefollow - middleShift) {
-                    middlefollow += ((KnuthBox) element).getMiddle()
+                if (box.getMiddle() > middlefollow - middleShift) {
+                    middlefollow += box.getMiddle()
                                     - middlefollow + middleShift;
                 }
             }
@@ -536,9 +795,483 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
                                               ratio, 0, indent,
                                               lineLead + middlefollow,
                                               lineLead));
+    }*/
+
+    
+    private LinkedList findOptimalLineBreakingPoints(int alignment) {
+
+        // find the optimal line breaking points for each paragraph
+        ListIterator paragraphsIterator
+            = knuthParagraphs.listIterator(knuthParagraphs.size());
+        Paragraph currPar = null;
+/*LF*/  LineBreakingAlgorithm alg;
+/*LF*/  lineLayoutsList = new ArrayList(knuthParagraphs.size());
+        while (paragraphsIterator.hasPrevious()) {
+/*LF*/      lineLayouts = new LineLayoutPossibilities();
+            currPar = (Paragraph) paragraphsIterator.previous();
+            double maxAdjustment = 1;
+            int iBPcount = 0;
+            alg = new LineBreakingAlgorithm(alignment,
+                                            bTextAlignment, bTextAlignmentLast,
+                                            textIndent.getValue(), currPar.lineFiller.opt,
+                                            lineHeight, lead, follow, middleShift,
+                                            (knuthParagraphs.indexOf(currPar) == 0),
+                                            this);
+    
+            if (hyphProps.hyphenate == EN_TRUE) {
+                findHyphenationPoints(currPar);
+            }
+    
+            // first try
+            boolean bHyphenationAllowed = false;
+            iBPcount = alg.findBreakingPoints(currPar,
+                                              iLineWidth,
+                                              maxAdjustment, false, bHyphenationAllowed);
+            if (iBPcount == 0 || alignment == EN_JUSTIFY) {
+                // if the first try found a set of breaking points, save them
+                if (iBPcount > 0) {
+                    alg.resetAlgorithm();
+                    lineLayouts.savePossibilities(false);
+                } else {
+                    // the first try failed
+                    log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment);
+                }
+    
+                // now try something different
+                log.debug("Hyphenation possible? " + (hyphProps.hyphenate == EN_TRUE));
+                if (hyphProps.hyphenate == EN_TRUE) {
+                    // consider every hyphenation point as a legal break
+                    bHyphenationAllowed = true;
+                } else {
+                    // try with a higher threshold
+                    maxAdjustment = 5;
+                }
+    
+                if ((iBPcount
+                     = alg.findBreakingPoints(currPar,
+                                              iLineWidth,
+                                              maxAdjustment, false, bHyphenationAllowed)) == 0) {
+                    // the second try failed too, try with a huge threshold
+                    // and force the algorithm to find
+                    // a set of breaking points
+                    log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment
+                                     + (hyphProps.hyphenate == EN_TRUE ? " and hyphenation" : ""));
+                    maxAdjustment = 20;
+                    iBPcount
+                        = alg.findBreakingPoints(currPar,
+                                                 iLineWidth,
+                                                 maxAdjustment, true, bHyphenationAllowed);
+                }
+    
+                // use non-hyphenated breaks, when possible
+                lineLayouts.restorePossibilities();
+    
+    /* *** *** estensione *** *** */
+                if (false && alignment == EN_JUSTIFY && bTextAlignment == EN_JUSTIFY) {
+/*LF*/              //System.out.println("LLM.getNextKnuthElements> soluzioni con piu' righe? " + lineLayouts.canUseMoreLines());
+/*LF*/              //System.out.println("                          soluzioni con meno righe? " + lineLayouts.canUseLessLines());
+                    if (!lineLayouts.canUseMoreLines()) {
+                        alg.resetAlgorithm();
+                        lineLayouts.savePossibilities(true);
+                        // try with shorter lines
+                        int savedLineWidth = iLineWidth;
+                        iLineWidth = (int) (iLineWidth * 0.95);
+                        iBPcount
+                            = alg.findBreakingPoints(currPar,
+                                                     iLineWidth,
+                                                     maxAdjustment, true, bHyphenationAllowed);
+                         // use normal lines, when possible
+                         lineLayouts.restorePossibilities();
+                         iLineWidth = savedLineWidth;
+                    }
+                    if (!lineLayouts.canUseLessLines()) {
+                        alg.resetAlgorithm();
+                        lineLayouts.savePossibilities(true);
+                        // try with longer lines
+                        int savedLineWidth = iLineWidth;
+                        iLineWidth = (int) (iLineWidth * 1.05);
+                        iBPcount
+                            = alg.findBreakingPoints(currPar,
+                                                     iLineWidth,
+                                                     maxAdjustment, true, bHyphenationAllowed);
+                         // use normal lines, when possible
+                         lineLayouts.restorePossibilities();
+                         iLineWidth = savedLineWidth;
+                    }
+/*LF*/              //System.out.println("LLM.getNextKnuthElements> ora, soluzioni con piu' righe? " + lineLayouts.canUseMoreLines());
+/*LF*/              //System.out.println("                          ora, soluzioni con meno righe? " + lineLayouts.canUseLessLines());
+                }
+    /* *** *** estensione *** *** */
+            }
+/*LF*/      lineLayoutsList.add(0, lineLayouts);
+        }
+        
+        
+/*LF*/  setFinished(true);
+    
+        //Post-process the line breaks found
+        return postProcessLineBreaks(alignment);
+    }
+
+    private LinkedList postProcessLineBreaks(int alignment) {
+    
+        LinkedList returnList = new LinkedList();
+        
+        for (int p = 0; p < knuthParagraphs.size(); p ++) {
+            // null penalty between paragraphs
+            if (p > 0
+                && !((BlockLevelLayoutManager) parentLM).mustKeepTogether()) {
+                returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
+            }
+        
+            lineLayouts = (LineLayoutPossibilities)lineLayoutsList.get(p);
+        
+            if (alignment == EN_JUSTIFY) {
+                /* ALLINEAMENTO GIUSTIFICATO, elementi con Position simboliche */
+                Position returnPosition = new LeafPosition(this, p);
+        /*LF*/          createElements(returnList, lineLayouts, returnPosition);
+        /* inizio commento 
+                if (layoutProps.orphans + layoutProps.widows <= lineLayouts.getMinLineNumber()
+                    && !((BlockLevelLayoutManager) parentLM).mustKeepTogether()) {
+                    // the lines of this paragraph can be parted;
+                    // for example, if widows = orphans = 2
+                    // and the paragraph has 8(-1,+2) lines:
+                    //   line
+                    //   line
+                    // ---- the page can end here ----
+                    //   line
+                    // ---- or here ----
+                    //   line
+                    // ---- or here ----
+                    //   line
+                    // ---- or here ----
+                    //   [optional line, if more than 8 are needed]
+                    // ---- or here ----
+                    //   [optional line, if more than 8 are needed]
+                    // ---- or here ----
+                    //   line [optionally disappearing]
+                    // ---- or here
+                    //   line
+                    //   line
+        
+                    // the first (layoutProps.orphans) lines
+                    returnList.add(new KnuthBox(layoutProps.orphans * constantLineHeight, returnPosition, false));
+                    returnList.add(new KnuthPenalty(0, 0, false, returnPosition, false));
+        
+                    // non-optional lines
+                    for (int i = 0;
+                         i < (lineLayouts.getMinLineNumber() - layoutProps.orphans - layoutProps.widows);
+                         i ++) {
+                        returnList.add(new KnuthBox(1 * constantLineHeight, returnPosition, false));
+                        returnList.add(new KnuthPenalty(0, 0, false, returnPosition, false));
+                    }
+        
+                    // optional lines
+                    for (int i = 0;
+                         i < (lineLayouts.getMaxLineNumber() - lineLayouts.getOptLineNumber());
+                         i ++) {
+                        returnList.add(new KnuthBox(0, returnPosition, false));
+                        returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, returnPosition, false));
+                        returnList.add(new KnuthGlue(0, 1 * constantLineHeight, 0,
+                                                     LINE_NUMBER_ADJUSTMENT, returnPosition, false));
+                        returnList.add(new KnuthBox(0, returnPosition, false));
+                        returnList.add(new KnuthPenalty(0, 0, false, returnPosition, false));
+                    }
+        
+                    // optionally disappearing lines
+                    for (int i = 0;
+                         i < (lineLayouts.getOptLineNumber() - lineLayouts.getMinLineNumber());
+                         i ++) {
+                        returnList.add(new KnuthBox(1 * constantLineHeight, returnPosition, false));
+                        returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, returnPosition, false));
+                        returnList.add(new KnuthGlue(0, 0, 1 * constantLineHeight,
+                                                     LINE_NUMBER_ADJUSTMENT, returnPosition, false));
+                        returnList.add(new KnuthBox(0, returnPosition, false));
+                        returnList.add(new KnuthPenalty(0, 0, false, returnPosition, false));
+                    }
+        
+                    // the last (layoutProps.widows) lines
+                    // the boolean parameter is true, so it's easy to see where a sequence
+                    // representing a paragraph ends
+                    returnList.add(new KnuthBox(layoutProps.widows * constantLineHeight, returnPosition, true));
+                } else if (layoutProps.orphans + layoutProps.widows <= lineLayouts.getMaxLineNumber()
+                           && !((BlockLevelLayoutManager) parentLM).mustKeepTogether()) {
+                    // the lines can be parted according to the line number
+                    int optLines;
+                    int moreLines;
+                    int lessLines;
+                    if (layoutProps.orphans + layoutProps.widows <= lineLayouts.getOptLineNumber()) {
+                        optLines = lineLayouts.getMinLineNumber();
+                        moreLines = lineLayouts.getMaxLineNumber() - optLines;
+                        lessLines = 0;
+                    } else {
+                        optLines = lineLayouts.getOptLineNumber();
+                        moreLines = lineLayouts.getMaxLineNumber() - optLines;
+                        lessLines = optLines - lineLayouts.getMinLineNumber();
+                    }
+        
+                    returnList.add(new KnuthBox((optLines - layoutProps.widows) * constantLineHeight,
+                                                returnPosition, false));
+                    returnList.add(new KnuthPenalty((layoutProps.orphans + layoutProps.widows - optLines) * constantLineHeight,
+                                                    0, false, returnPosition, false));
+                    for (int i = 0;
+                         i < (optLines + moreLines - layoutProps.orphans - layoutProps.widows);
+                         i ++) {
+                        returnList.add(new KnuthGlue(0, 1 * constantLineHeight, 0,
+                                                     LINE_NUMBER_ADJUSTMENT, returnPosition, false));
+                        returnList.add(new KnuthBox(0,
+                                                    returnPosition, false));
+                        returnList.add(new KnuthPenalty((layoutProps.orphans + layoutProps.widows - optLines) * constantLineHeight,
+                                                        0, false, returnPosition, false));
+                    }
+                    returnList.add(new KnuthGlue(0, (layoutProps.orphans + layoutProps.widows - optLines) * constantLineHeight, lessLines * constantLineHeight,
+                                                 LINE_NUMBER_ADJUSTMENT, returnPosition, false));
+                    // the boolean parameter is true, so it's easy to see where a sequence
+                    // representing a paragraph ends
+                    returnList.add(new KnuthBox(layoutProps.widows * constantLineHeight,
+                                                returnPosition, true));
+                } else {
+                    // the lines of this paragraph cannot be parted;
+                    int optLines = lineLayouts.getOptLineNumber();
+                    int moreLines = lineLayouts.getMaxLineNumber() - optLines;
+                    int lessLines = optLines - lineLayouts.getMinLineNumber();
+        
+                    returnList.add(new KnuthBox(optLines * constantLineHeight,
+                                                returnPosition , (moreLines != 0 || lessLines != 0) ? false : true));
+                    if (moreLines != 0 || lessLines != 0) {
+                        returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
+                                                        returnPosition, false));
+                        returnList.add(new KnuthGlue(0, moreLines * constantLineHeight, lessLines * constantLineHeight,
+                                                     LINE_NUMBER_ADJUSTMENT, returnPosition, false));
+                        // the boolean parameter is true, so it's easy to see where a sequence
+                        // representing a paragraph ends
+                        returnList.add(new KnuthBox(0, returnPosition, true));
+                    }
+                }
+        fine commento */
+            } else {
+                /* ALLINEAMENTO NON GIUSTIFICATO, elementi con Position effettive */
+                Position returnPosition = new LeafPosition(this, p);
+                for (int i = 0;
+                     i < lineLayouts.getChosenLineNumber();
+                     i ++) {
+                    if (!((BlockLevelLayoutManager) parentLM).mustKeepTogether()
+                        && i >= fobj.getOrphans()
+                        && i <= lineLayouts.getChosenLineNumber() - fobj.getWidows()) {
+                        // null penalty allowing a page break between lines
+                        returnList.add(new KnuthPenalty(0, 0, false, returnPosition, false));
+                    }
+                    returnList.add(new KnuthBox(1 * constantLineHeight,
+                                                lineLayouts.getChosenPosition(i), false));
+                }
+            }
+        }
+        
+/*LF*/  return returnList;
+    }
+
+
+    /*LF inizio nuova procedura di creazione degli elementi */
+    private void createElements(List list, LineLayoutPossibilities lineLayouts,
+                                Position elementPosition) {
+        /* number of normal, inner lines */
+        int nInnerLines = 0;
+        /* number of lines that can be used in order to fill more space */
+        int nOptionalLines = 0;
+        /* number of lines that can be used in order to fill more space
+           only if the paragraph is not parted */
+        int nConditionalOptionalLines = 0;
+        /* number of lines that can be omitted in order to fill less space */
+        int nEliminableLines = 0;
+        /* number of lines that can be omitted in order to fill less space
+           only if the paragraph is not parted */
+        int nConditionalEliminableLines = 0;
+        /* number of the first unbreakable lines */
+        int nFirstLines = fobj.getOrphans();
+        /* number of the last unbreakable lines */
+        int nLastLines = fobj.getWidows();
+        /* sub-sequence used to separate the elements representing different lines */
+        List breaker = new LinkedList();
+
+/* commentare via per testare layout particolari */
+        if (fobj.getOrphans() + fobj.getWidows() <= lineLayouts.getMinLineNumber()) {
+            nInnerLines = lineLayouts.getMinLineNumber() - (fobj.getOrphans() + fobj.getWidows());
+            nOptionalLines = lineLayouts.getMaxLineNumber() - lineLayouts.getOptLineNumber();
+            nEliminableLines = lineLayouts.getOptLineNumber() - lineLayouts.getMinLineNumber();
+        } else if (fobj.getOrphans() + fobj.getWidows() <= lineLayouts.getOptLineNumber()) {
+            nOptionalLines = lineLayouts.getMaxLineNumber() - lineLayouts.getOptLineNumber();
+            nEliminableLines = lineLayouts.getOptLineNumber() - (fobj.getOrphans() + fobj.getWidows());
+            nConditionalEliminableLines = (fobj.getOrphans() + fobj.getWidows()) - lineLayouts.getMinLineNumber();
+        } else if (fobj.getOrphans() + fobj.getWidows() <= lineLayouts.getMaxLineNumber()) {
+            nOptionalLines = lineLayouts.getMaxLineNumber() - (fobj.getOrphans() + fobj.getWidows());
+            nConditionalOptionalLines = (fobj.getOrphans() + fobj.getWidows()) - lineLayouts.getOptLineNumber();
+            nConditionalEliminableLines = lineLayouts.getOptLineNumber() - lineLayouts.getMinLineNumber();
+            nFirstLines -= nConditionalOptionalLines;
+        } else {
+            nConditionalOptionalLines = lineLayouts.getMaxLineNumber() - lineLayouts.getOptLineNumber();
+            nConditionalEliminableLines = lineLayouts.getOptLineNumber() - lineLayouts.getMinLineNumber();
+            nFirstLines = lineLayouts.getOptLineNumber();
+            nLastLines = 0;
+        }
+/* commentare via per testare layout particolari */
+
+/* inizio caso particolare da testare 
+        nInnerLines = 0;
+        nOptionalLines = 1;
+        nConditionalOptionalLines = 2;
+        nEliminableLines = 0;
+        nConditionalEliminableLines = 0;
+        nFirstLines = 1;
+        nLastLines = 3;
+    fine caso particolare da testare  */
+
+        if (nLastLines != 0
+            && (nConditionalOptionalLines > 0 || nConditionalEliminableLines > 0)) {
+            breaker.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
+            breaker.add(new KnuthGlue(0, -nConditionalOptionalLines * constantLineHeight,
+                                        -nConditionalEliminableLines * constantLineHeight,
+                                        LINE_NUMBER_ADJUSTMENT, elementPosition, false));
+            breaker.add(new KnuthPenalty(nConditionalOptionalLines * constantLineHeight,
+                                           0, false, elementPosition, false));
+            breaker.add(new KnuthGlue(0, nConditionalOptionalLines * constantLineHeight,
+                                        nConditionalEliminableLines * constantLineHeight,
+                                        LINE_NUMBER_ADJUSTMENT, elementPosition, false));
+        } else if (nLastLines != 0) {
+            breaker.add(new KnuthPenalty(0, 0, false, elementPosition, false));
+        }
+
+/*LF*/  //System.out.println("first=" + nFirstLines + " inner=" + nInnerLines
+        //                   + " optional=" + nOptionalLines + " eliminable=" + nEliminableLines
+        //                   + " last=" + nLastLines
+        //                   + " (condOpt=" + nConditionalOptionalLines + " condEl=" + nConditionalEliminableLines + ")");
+
+        // creation of the elements:
+        // first group of lines
+        list.add(new KnuthBox(nFirstLines * constantLineHeight, elementPosition,
+                              (nLastLines == 0
+                               && nConditionalOptionalLines == 0
+                               && nConditionalEliminableLines == 0 ? true : false)));
+        if (nConditionalOptionalLines > 0
+            || nConditionalEliminableLines > 0) {
+            list.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
+            list.add(new KnuthGlue(0, nConditionalOptionalLines * constantLineHeight,
+                                   nConditionalEliminableLines * constantLineHeight,
+                                   LINE_NUMBER_ADJUSTMENT, elementPosition, false));
+            list.add(new KnuthBox(0, elementPosition,
+                                  (nLastLines == 0 ? true : false)));
+        }
+
+        // optional lines
+        for (int i = 0; i < nOptionalLines; i++) {
+            list.addAll(breaker);
+            list.add(new KnuthBox(0, elementPosition, false));
+            list.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
+            list.add(new KnuthGlue(0, 1 * constantLineHeight, 0,
+                                   LINE_NUMBER_ADJUSTMENT, elementPosition, false));
+            list.add(new KnuthBox(0, elementPosition, false));
+        }
+
+        // eliminable lines
+        for (int i = 0; i < nEliminableLines; i++) {
+            list.addAll(breaker);
+            list.add(new KnuthBox(1 * constantLineHeight, elementPosition, false));
+            list.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
+            list.add(new KnuthGlue(0, 0, 1 * constantLineHeight,
+                                   LINE_NUMBER_ADJUSTMENT, elementPosition, false));
+            list.add(new KnuthBox(0, elementPosition, false));
+        }
+
+        // inner lines
+        for (int i = 0; i < nInnerLines; i++) {
+            list.addAll(breaker);
+            list.add(new KnuthBox(1 * constantLineHeight, elementPosition, false));
+        }
+
+        // last group of lines
+        if (nLastLines > 0) {
+            list.addAll(breaker);
+            list.add(new KnuthBox(nLastLines * constantLineHeight,
+                                  elementPosition, true));
+        }
     }
+/*LF fine nuova procedura di creazione degli elementi*/
 
+    public boolean mustKeepTogether() {
+        return false;
+    }
+
+    public boolean mustKeepWithPrevious() {
+        return false;
+    }
+
+    public boolean mustKeepWithNext() {
+        return false;
+    }
+
+    public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
+        LeafPosition pos = (LeafPosition)lastElement.getPosition();
+        int totalAdj = adj;
+        //if (lastElement.isPenalty()) {
+        //    totalAdj += lastElement.getW();
+        //}
+        //int lineNumberDifference = (int)((double) totalAdj / constantLineHeight);
+        int lineNumberDifference = (int) Math.round((double) totalAdj / constantLineHeight + (adj > 0 ? - 0.4 : 0.4));
+/*LF*/  //System.out.println("   LLM> variazione calcolata = " + ((double) totalAdj / constantLineHeight) + " variazione applicata = " + lineNumberDifference);
+        lineLayouts = (LineLayoutPossibilities)lineLayoutsList.get(pos.getLeafPos());
+        lineNumberDifference = lineLayouts.applyLineNumberAdjustment(lineNumberDifference);
+        return lineNumberDifference * constantLineHeight;
+    }
+
+    public void discardSpace(KnuthGlue spaceGlue) {
+    }
 
+    public LinkedList getChangedKnuthElements(List oldList, int alignment) {
+        LinkedList returnList = new LinkedList();
+        for (int p = 0;
+             p < knuthParagraphs.size();
+             p ++) {
+            lineLayouts = (LineLayoutPossibilities)lineLayoutsList.get(p);
+/*LF*/      //System.out.println("demeriti definitivi: " + lineLayouts.getChosenDemerits());
+            for (int i = 0;
+                 i < lineLayouts.getChosenLineNumber();
+                 i ++) {
+                if (!((BlockLevelLayoutManager) parentLM).mustKeepTogether()
+                    && i >= fobj.getOrphans()
+                    && i <= lineLayouts.getChosenLineNumber() - fobj.getWidows()) {
+                    // null penalty allowing a page break between lines
+                    returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
+                }
+                LineBreakPosition lbp = (LineBreakPosition) lineLayouts.getChosenPosition(i);
+/*LF*/          //System.out.println("LLM.getChangedKnuthElements> lineWidth= " + lbp.lineWidth + " difference= " + lbp.difference);
+/*LF*/          //System.out.println("                             shrink= " + lbp.availableShrink + " stretch= " + lbp.availableStretch);
+
+                /* nello shrink dell'ultima riga va ignorato quello dovuto al lineFiller */
+                /* ma e' meglio fare queste direttamente quando si creano le 
+                   linebreakposition, cosi' non c'e' bisogno di distinguere
+                   a questo punto ne' quando si ricalcola */
+                /*int fillerShrink = ((Paragraph)knuthParagraphs.get(p)).lineFiller.opt - ((Paragraph)knuthParagraphs.get(p)).lineFiller.min;*/
+/*LF*/          //System.out.println("linewidth= " + lbp.lineWidth + " difference= " + lbp.difference + " indent= " + lbp.startIndent);
+                MinOptMax contentIPD;
+                if (alignment == EN_JUSTIFY) {
+                    contentIPD = new MinOptMax(
+                        lbp.lineWidth - lbp.difference - lbp.availableShrink, 
+                        lbp.lineWidth - lbp.difference, 
+                        lbp.lineWidth - lbp.difference + lbp.availableStretch);
+                } else if (alignment == EN_CENTER) {
+                    contentIPD = new MinOptMax(lbp.lineWidth - 2 * lbp.startIndent);
+                } else if (alignment == EN_END) {
+                    contentIPD = new MinOptMax(lbp.lineWidth - lbp.startIndent);
+                } else {
+                    contentIPD = new MinOptMax(lbp.lineWidth - lbp.difference + lbp.startIndent);
+                }
+                returnList.add(new KnuthBlockBox(1 * constantLineHeight,
+                                                 contentIPD,
+                                                 (lbp.ipdAdjust != 0 ? lbp.lineWidth - lbp.difference : 0),
+                                                 lbp, false));
+            }
+        }
+        return returnList;
+    }
 
     /**
      * find hyphenation points for every word int the current paragraph
@@ -658,7 +1391,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
                     = currUpdate.inlineLM.getChangedKnuthElements
                     (currPar.subList(fromIndex + iAddedElements,
                                      toIndex + iAddedElements),
-                     flaggedPenalty, effectiveAlignment);
+                     /*flaggedPenalty,*/ effectiveAlignment);
                 // remove the old elements
                 currPar.subList(fromIndex + iAddedElements,
                                 toIndex + iAddedElements).clear();
@@ -823,7 +1556,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
                 setFinished(false);
                 iReturnedLBP--;
             }
-            while ((LineBreakPosition) breakpoints.get(iReturnedLBP)
+            while ((LineBreakPosition) lineLayouts.getChosenPosition(iReturnedLBP)
                    != (LineBreakPosition) resetPos) {
                 iReturnedLBP--;
             }
@@ -839,100 +1572,118 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
      */
     public void addAreas(PositionIterator parentIter,
                          LayoutContext context) {
-        addAreas(parentIter, 0.0);
-
-        //vecInlineBreaks.clear();
-        prevBP = null;
-    }
-
-    // Generate and add areas to parent area
-    // Set size etc
-    // dSpaceAdjust should reference extra space in the BPD
-    /**
-     * Add the areas with the associated space adjustment.
-     *
-     * @param parentIter the iterator of breaks positions
-     * @param dSpaceAdjust the space adjustment
-     */
-    public void addAreas(PositionIterator parentIter, double dSpaceAdjust) {
         LayoutManager childLM;
         LayoutContext lc = new LayoutContext(0);
         int iCurrParIndex;
         while (parentIter.hasNext()) {
-            ListIterator paragraphIterator = null;
-            KnuthElement tempElement = null;
-            // the TLM which created the last KnuthElement in this line
-            LayoutManager lastLM = null;
-
-            LineBreakPosition lbp = (LineBreakPosition) parentIter.next();
-            LineArea lineArea = new LineArea();
-            lineArea.setStartIndent(lbp.startIndent);
-            lineArea.setBPD(lbp.lineHeight);
-            lc.setBaseline(lbp.baseline);
-            lc.setLineHeight(lbp.lineHeight);
-            lc.setMiddleShift(middleShift);
-            setCurrentArea(lineArea);
-
-            iCurrParIndex = lbp.iParIndex;
-            Paragraph currPar = (Paragraph) knuthParagraphs.get(iCurrParIndex);
-            iEndElement = lbp.getLeafPos();
-
-            // ignore the first elements added by the LineLayoutManager
-            iStartElement += (iStartElement == 0) ? currPar.ignoreAtStart : 0;
-
-            // ignore the last elements added by the LineLayoutManager
-            iEndElement -= (iEndElement == (currPar.size() - 1))
-                ? currPar.ignoreAtEnd : 0;
-
-            // ignore the last element in the line if it is a KnuthGlue object
-            paragraphIterator = currPar.listIterator(iEndElement);
-            tempElement = (KnuthElement) paragraphIterator.next();
-            if (tempElement.isGlue()) {
-                iEndElement --;
-                // this returns the same KnuthElement
-                paragraphIterator.previous();
-                tempElement = (KnuthElement) paragraphIterator.previous();
-            }
-            lastLM = tempElement.getLayoutManager();
-
-            // ignore KnuthGlue and KnuthPenalty objects
-            // at the beginning of the line
-            paragraphIterator = currPar.listIterator(iStartElement);
-            tempElement = (KnuthElement) paragraphIterator.next();
-            while (!tempElement.isBox() && paragraphIterator.hasNext()) {
+            Position pos = (Position) parentIter.next();
+            if (pos instanceof LineBreakPosition) {
+                ListIterator paragraphIterator = null;
+                KnuthElement tempElement = null;
+                // the TLM which created the last KnuthElement in this line
+                LayoutManager lastLM = null;
+    
+                LineBreakPosition lbp = (LineBreakPosition) pos;
+                LineArea lineArea = new LineArea();
+                lineArea.setStartIndent(lbp.startIndent);
+                lineArea.setBPD(lbp.lineHeight);
+                lc.setBaseline(lbp.baseline);
+                lc.setLineHeight(lbp.lineHeight);
+                lc.setMiddleShift(middleShift);
+/*LF*/          lc.setTopShift(lbp.topShift);
+/*LF*/          lc.setBottomShift(lbp.bottomShift);
+
+                iCurrParIndex = lbp.iParIndex;
+                Paragraph currPar = (Paragraph) knuthParagraphs.get(iCurrParIndex);
+                iEndElement = lbp.getLeafPos();
+    
+                // ignore the first elements added by the LineLayoutManager
+                iStartElement += (iStartElement == 0) ? currPar.ignoreAtStart : 0;
+    
+                // ignore the last elements added by the LineLayoutManager
+                iEndElement -= (iEndElement == (currPar.size() - 1))
+                    ? currPar.ignoreAtEnd : 0;
+    
+                // ignore the last element in the line if it is a KnuthGlue object
+                paragraphIterator = currPar.listIterator(iEndElement);
                 tempElement = (KnuthElement) paragraphIterator.next();
-                iStartElement ++;
-            }
-
-            // Add the inline areas to lineArea
-            PositionIterator inlinePosIter
-                = new KnuthPossPosIter(currPar, iStartElement,
-                                       iEndElement + 1);
-
-            iStartElement = lbp.getLeafPos() + 1;
-            if (iStartElement == currPar.size()) {
-                // advance to next paragraph
-                iStartElement = 0;
-            }
-
-            lc.setSpaceAdjust(lbp.dAdjust);
-            lc.setIPDAdjust(lbp.ipdAdjust);
-            lc.setLeadingSpace(new SpaceSpecifier(true));
-            lc.setTrailingSpace(new SpaceSpecifier(false));
-            lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
-            setChildContext(lc);
-            while ((childLM = inlinePosIter.getNextChildLM()) != null) {
-                lc.setFlags(LayoutContext.LAST_AREA, (childLM == lastLM));
-                childLM.addAreas(inlinePosIter, lc);
-                lc.setLeadingSpace(lc.getTrailingSpace());
+                if (tempElement.isGlue()) {
+                    iEndElement --;
+                    // this returns the same KnuthElement
+                    paragraphIterator.previous();
+                    tempElement = (KnuthElement) paragraphIterator.previous();
+                }
+                lastLM = tempElement.getLayoutManager();
+    
+                // ignore KnuthGlue and KnuthPenalty objects
+                // at the beginning of the line
+                paragraphIterator = currPar.listIterator(iStartElement);
+                tempElement = (KnuthElement) paragraphIterator.next();
+                while (!tempElement.isBox() && paragraphIterator.hasNext()) {
+                    tempElement = (KnuthElement) paragraphIterator.next();
+                    iStartElement ++;
+                }
+    
+                // Add the inline areas to lineArea
+                PositionIterator inlinePosIter
+                    = new KnuthPossPosIter(currPar, iStartElement,
+                                           iEndElement + 1);
+    
+                iStartElement = lbp.getLeafPos() + 1;
+                if (iStartElement == currPar.size()) {
+                    // advance to next paragraph
+                    iStartElement = 0;
+                }
+    
+                lc.setSpaceAdjust(lbp.dAdjust);
+                lc.setIPDAdjust(lbp.ipdAdjust);
+                lc.setLeadingSpace(new SpaceSpecifier(true));
                 lc.setTrailingSpace(new SpaceSpecifier(false));
+                lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
+
+/* *** *** extension *** *** */
+                if (false && bTextAlignment == EN_JUSTIFY) {
+                    // re-compute space adjust ratio
+                    int updatedDifference = context.getStackLimit().opt - lbp.lineWidth + lbp.difference;
+                    double updatedRatio = 0.0;
+                    if (updatedDifference > 0) {
+                        updatedRatio = (float) updatedDifference / lbp.availableStretch;
+                    } else if (updatedDifference < 0) {
+                        updatedRatio = (float) updatedDifference / lbp.availableShrink;
+                    }
+                    lc.setIPDAdjust(updatedRatio);
+/*LF*/              //System.out.println("LLM.addAreas> vecchia differenza= " + lbp.difference + " differenza ricalcolata= " + updatedDifference);
+/*LF*/              //System.out.println("              vecchio coeff= " + lbp.ipdAdjust + " differenza ricalcolata= " + updatedRatio);
+                } else if (false && bTextAlignment == EN_CENTER) {
+                    // re-compute indent
+                    int updatedIndent = lbp.startIndent + (context.getStackLimit().opt - lbp.lineWidth) / 2;
+                    lineArea.setStartIndent(updatedIndent);
+                } else if (false && bTextAlignment == EN_END) {
+                    // re-compute indent
+                    int updatedIndent = lbp.startIndent + (context.getStackLimit().opt - lbp.lineWidth);
+                    lineArea.setStartIndent(updatedIndent);
+                }
+/* *** *** extension *** *** */
+
+                setCurrentArea(lineArea); // spostata da prima
+                setChildContext(lc);
+                while ((childLM = inlinePosIter.getNextChildLM()) != null) {
+                    lc.setFlags(LayoutContext.LAST_AREA, (childLM == lastLM));
+                    childLM.addAreas(inlinePosIter, lc);
+                    lc.setLeadingSpace(lc.getTrailingSpace());
+                    lc.setTrailingSpace(new SpaceSpecifier(false));
+                }
+                
+                // when can this be null?
+                // if display-align is distribute, add space after 
+                if (context.getSpaceAfter() > 0
+                    && (!context.isLastArea() || parentIter.hasNext())) {
+                    lineArea.setBPD(lineArea.getBPD() + context.getSpaceAfter());
+                }
+                parentLM.addChildArea(lineArea);
+            } else {
+                // pos was the Position inside a penalty item, nothing to do
             }
-            // when can this be null?
-            if (lc.getTrailingSpace() != null) {
-                addSpace(lineArea, lc.getTrailingSpace().resolve(true),
-                         lc.getSpaceAdjust());
-            }
-            parentLM.addChildArea(lineArea);
         }
         setCurrentArea(null); // ?? necessary
     }
diff --git a/src/java/org/apache/fop/layoutmgr/LineLayoutPossibilities.java b/src/java/org/apache/fop/layoutmgr/LineLayoutPossibilities.java
new file mode 100644 (file)
index 0000000..87d7eb4
--- /dev/null
@@ -0,0 +1,210 @@
+
+package org.apache.fop.layoutmgr;
+
+import java.util.ArrayList;
+
+public class LineLayoutPossibilities {
+
+    private class Possibility {
+        private int lineNumber;
+        private double demerits;
+        private ArrayList breakPositions;
+
+        private Possibility(int ln, double dem) {
+            lineNumber = ln;
+            demerits = dem;
+            breakPositions = new ArrayList(ln);
+        }
+
+        private int getLineNumber() {
+            return lineNumber;
+        }
+
+        private double getDemerits() {
+            return demerits;
+        }
+
+        private void addBreakPosition(Position pos) {
+            // Positions are always added with index 0 because 
+            // they are created backward, from the last one to 
+            // the first one
+            breakPositions.add(0, pos);
+        }
+
+        private Position getBreakPosition(int i) {
+            return (Position)breakPositions.get(i);
+        }
+    }
+
+    private ArrayList possibilitiesList;
+    private ArrayList savedPossibilities;
+    private int minimumIndex;
+    private int optimumIndex;
+    private int maximumIndex;
+    private int chosenIndex;
+    private int savedOptLineNumber;
+
+    public LineLayoutPossibilities() {
+        possibilitiesList = new ArrayList();
+        savedPossibilities = new ArrayList();
+        optimumIndex = -1;
+    }
+    public void addPossibility(int ln, double dem) {
+        possibilitiesList.add(new Possibility(ln, dem));
+        if (possibilitiesList.size() == 1) {
+            // first Possibility added
+            minimumIndex = 0;
+            optimumIndex = 0;
+            maximumIndex = 0;
+            chosenIndex = 0;
+        } else {
+            if (dem < ((Possibility)possibilitiesList.get(optimumIndex)).getDemerits()) {
+                optimumIndex = possibilitiesList.size() - 1;
+                chosenIndex = optimumIndex;
+            }
+            if (ln < ((Possibility)possibilitiesList.get(minimumIndex)).getLineNumber()) {
+                minimumIndex = possibilitiesList.size() - 1;
+            }
+            if (ln > ((Possibility)possibilitiesList.get(maximumIndex)).getLineNumber()) {
+                maximumIndex = possibilitiesList.size() - 1;
+            }
+        }
+    }
+
+    /* save in a different array the computed Possibilities,
+     * so possibilitiesList is ready to store different Possibilities
+     */
+    public void savePossibilities(boolean bSaveOptLineNumber) {
+        if (bSaveOptLineNumber) {
+            savedOptLineNumber = getOptLineNumber();
+        } else {
+            savedOptLineNumber = 0;
+        }
+        savedPossibilities = possibilitiesList;
+        possibilitiesList = new ArrayList();
+    }
+
+    /* replace the Possibilities stored in possibilitiesList with
+     * the ones stored in savedPossibilities and having the same line number
+     */
+    public void restorePossibilities() {
+        int index = 0;
+        while (savedPossibilities.size() > 0) {
+            Possibility restoredPossibility = (Possibility) savedPossibilities.remove(0);
+            if (restoredPossibility.getLineNumber() < getMinLineNumber()) {
+                // if the line number of restoredPossibility is less than the minimum one,
+                // add restoredPossibility at the beginning of the list
+                possibilitiesList.add(0, restoredPossibility);
+                // update minimumIndex
+                minimumIndex = 0;
+                // shift the other indexes;
+                optimumIndex ++;
+                maximumIndex ++;
+                chosenIndex ++;
+            } else if (restoredPossibility.getLineNumber() > getMaxLineNumber()) {
+                // if the line number of restoredPossibility is greater than the maximum one,
+                // add restoredPossibility at the end of the list
+                possibilitiesList.add(possibilitiesList.size(), restoredPossibility);
+                // update maximumIndex
+                maximumIndex = possibilitiesList.size() - 1;
+                index = maximumIndex;
+            } else {
+                // find the index of the Possibility that will be replaced
+                while (index < maximumIndex
+                       && getLineNumber(index) < restoredPossibility.getLineNumber()) {
+                    index ++;
+                }
+                if (getLineNumber(index) == restoredPossibility.getLineNumber()) {
+                    possibilitiesList.set(index, restoredPossibility);
+                } else {
+                    // this should not happen
+/*LF*/              System.out.println("ERRORE: LineLayoutPossibilities restorePossibilities(), min= " + getMinLineNumber() + " max= " + getMaxLineNumber() + " restored= " + restoredPossibility.getLineNumber());
+                    return;
+                }
+            }
+            // update optimumIndex and chosenIndex
+            if (savedOptLineNumber == 0 && getDemerits(optimumIndex) > restoredPossibility.getDemerits()
+                || savedOptLineNumber != 0 && restoredPossibility.getLineNumber() == savedOptLineNumber) {
+                optimumIndex = index;
+                chosenIndex = optimumIndex;
+            }
+        }
+/*LF*/  //System.out.println(">> minLineNumber = " + getMinLineNumber() + " optLineNumber = " + getOptLineNumber() + " maxLineNumber() = " + getMaxLineNumber());
+    }
+
+    public void addBreakPosition(Position pos, int i) {
+        ((Possibility)possibilitiesList.get(i)).addBreakPosition(pos);
+    }
+
+    public boolean canUseMoreLines() {
+        return (getOptLineNumber() < getMaxLineNumber());
+    }
+
+    public boolean canUseLessLines() {
+        return (getMinLineNumber() < getOptLineNumber());
+    }
+
+    public int getMinLineNumber() {
+        return getLineNumber(minimumIndex);
+    }
+
+    public int getOptLineNumber() {
+        return getLineNumber(optimumIndex);
+    }
+
+    public int getMaxLineNumber() {
+        return getLineNumber(maximumIndex);
+    }
+
+    public int getChosenLineNumber() {
+        return getLineNumber(chosenIndex);
+    }
+
+    public int getLineNumber(int i) {
+        return ((Possibility)possibilitiesList.get(i)).getLineNumber();
+    }
+
+    public double getChosenDemerits() {
+        return getDemerits(chosenIndex);
+    }
+
+    public double getDemerits(int i) {
+        return ((Possibility)possibilitiesList.get(i)).getDemerits();
+    }
+
+    public int getPossibilitiesNumber() {
+        return possibilitiesList.size();
+    }
+
+    public Position getChosenPosition(int i) {
+        return ((Possibility)possibilitiesList.get(chosenIndex)).getBreakPosition(i);
+    }
+
+    public int applyLineNumberAdjustment(int adj) {
+        if (adj >= (getMinLineNumber() - getChosenLineNumber())
+            && adj <= (getMaxLineNumber() - getChosenLineNumber())
+            && getLineNumber(chosenIndex + adj) == getChosenLineNumber() + adj) {
+            chosenIndex += adj;
+            System.out.println("chosenLineNumber= " + (getChosenLineNumber() - adj) + " variazione= " + adj
+                               + " => chosenLineNumber= " + getLineNumber(chosenIndex));
+            return adj;
+        } else {
+            // this should not happen!
+            System.out.println("Cannot apply the desired line number adjustment");
+            return 0;
+        }
+    }
+
+    public void printAll() {
+        System.out.println("++++++++++");
+        System.out.println(" " + possibilitiesList.size() + " possibilita':");
+        for (int i = 0; i < possibilitiesList.size(); i ++) {
+            System.out.println("   " + ((Possibility)possibilitiesList.get(i)).getLineNumber()
+                               + (i == optimumIndex ? " *" : "")
+                               + (i == minimumIndex ? " -" : "")
+                               + (i == maximumIndex ? " +" : ""));
+        }
+        System.out.println("++++++++++");
+    }
+}
diff --git a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
new file mode 100644 (file)
index 0000000..463e0d0
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import java.util.LinkedList;
+
+import org.apache.fop.layoutmgr.AbstractBreaker.PageBreakPosition;
+
+class PageBreakingAlgorithm extends BreakingAlgorithm {
+    private LayoutManager topLevelLM;
+    private LinkedList pageBreaks = null;
+
+    public PageBreakingAlgorithm(LayoutManager topLevelLM,
+                                  int alignment, int alignmentLast) {
+        super(alignment, alignmentLast, true);
+        this.topLevelLM = topLevelLM;
+    }
+    
+    public LinkedList getPageBreaks() {
+        return pageBreaks;
+    }
+
+    public void insertPageBreakAsFirst(PageBreakPosition pageBreak) {
+        if (pageBreaks == null) {
+            pageBreaks = new LinkedList();
+        }
+        pageBreaks.addFirst(pageBreak);
+    }
+    
+    public void updateData1(int total, double demerits) {
+    }
+
+    public void updateData2(KnuthNode bestActiveNode,
+                            KnuthSequence sequence,
+                            int total) {
+        //int difference = (bestActiveNode.line < total) ? bestActiveNode.difference : bestActiveNode.difference + fillerMinWidth;
+        int difference = bestActiveNode.difference;
+        int blockAlignment = (bestActiveNode.line < total) ? alignment : alignmentLast;
+        double ratio = (blockAlignment == org.apache.fop.fo.Constants.EN_JUSTIFY
+                        || bestActiveNode.adjustRatio < 0) ? bestActiveNode.adjustRatio : 0;
+
+
+        // add nodes at the beginning of the list, as they are found
+        // backwards, from the last one to the first one
+        System.out.println("BBA> difference= " + difference + " ratio= " + ratio 
+                           + " posizione= " + bestActiveNode.position);
+        insertPageBreakAsFirst(new PageBreakPosition(this.topLevelLM, 
+                bestActiveNode.position, ratio, difference));
+    }
+
+    protected int filterActiveList() {
+        // leave only bestActiveNode in the activeList
+        KnuthNode tempNode = null;
+        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;
+            }
+        }
+        activeList.add(bestActiveNode);
+        System.out.println("                      migliore " + bestActiveNode.line);
+        return bestActiveNode.line;
+    }
+
+}
\ No newline at end of file
index d0cc95e46ffc264378f9d52eb45c2325a7fb93c7..448e7cef5d0c6a64943e836c17e921912131161b 100644 (file)
@@ -50,7 +50,9 @@ import org.apache.fop.fo.pagination.SimplePageMaster;
 import org.apache.fop.fo.pagination.StaticContent;
 import org.apache.fop.fo.properties.CommonMarginBlock;
 
+import java.util.LinkedList;
 import java.util.List;
+import java.util.ListIterator;
 import java.util.Map;
 import java.awt.Rectangle;
 import java.util.Iterator;
@@ -72,6 +74,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
         }
     }
 
+
     private int startPageNum = 0;
     private int currentPageNum = 0;
     private String pageNumberString;
@@ -107,11 +110,19 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
      */
     private SimplePageMaster currentSimplePageMaster;
 
+    /**
+     * The collection of StaticContentLayoutManager objects that are associated
+     * with this Page Sequence, keyed by flow-name.
+     */
+    //private HashMap staticContentLMs = new HashMap(4);
+
+    private FlowLayoutManager childFLM = null;
+
     /**
      * Constructor - activated by AreaTreeHandler for each
      * fo:page-sequence in the input FO stream
      *
-     * @param pageseq the page-sequence formatting object
+     * @param pageSeq the page-sequence formatting object
      */
     public PageSequenceLayoutManager(PageSequence pageSeq) {
         super(pageSeq);
@@ -148,8 +159,8 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
         LineArea title = null;
 
         if (pageSeq.getTitleFO() != null) {
-            ContentLayoutManager clm = 
-                new ContentLayoutManager(pageSeq.getTitleFO(), this);
+            ContentLayoutManager clm = new ContentLayoutManager(pageSeq
+                    .getTitleFO(), this);
             title = (LineArea) clm.getParentArea(null); // can improve
         }
 
@@ -157,27 +168,69 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
         log.debug("Starting layout");
 
         makeNewPage(false, false);
+        isFirstPage = true;
         flowIPD = curFlow.getIPD();
 
-        BreakPoss bp;
-        LayoutContext childLC = new LayoutContext(0);
-        while (!isFinished()) {
-            if ((bp = getNextBreakPoss(childLC)) != null) {
-                addAreas((BlockBreakPosition)bp.getPosition());
-                // add static areas and resolve any new id areas
-                // finish page and add to area tree
-                finishPage();
-                currentPageNum++;
-                pageNumberString = pageSeq.makeFormattedPageNumber(currentPageNum);
-            }
-        }
+        PageBreaker breaker = new PageBreaker(this);
+        breaker.doLayout(flowBPD);
+        
         // TODO: Don't decrement currentPageNum when no pages are generated
         currentPageNum--;
         finishPage();
-        pageSeq.getRoot().notifyPageSequenceFinished(currentPageNum, (currentPageNum - startPageNum) + 1);
+        pageSeq.getRoot().notifyPageSequenceFinished(currentPageNum,
+                (currentPageNum - startPageNum) + 1);
         log.debug("Ending layout");
     }
 
+    private class PageBreaker extends AbstractBreaker {
+        
+        private PageSequenceLayoutManager pslm;
+        
+        public PageBreaker(PageSequenceLayoutManager pslm) {
+            this.pslm = pslm;
+        }
+        
+        protected LayoutManager getTopLevelLM() {
+            return pslm;
+        }
+        
+        protected LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+            return pslm.getNextKnuthElements(context, alignment);
+        }
+        
+        protected int getCurrentDisplayAlign() {
+            return currentSimplePageMaster.getRegion(Constants.FO_REGION_BODY).getDisplayAlign();
+        }
+        
+        protected boolean hasMoreContent() {
+            return !isFinished();
+        }
+        
+        protected void addAreas(PositionIterator posIter, LayoutContext context) {
+            getCurrentChildLM().addAreas(posIter, context);    
+        }
+        
+        protected void doPhase3(PageBreakingAlgorithm alg, int partCount, 
+                KnuthSequence originalList, KnuthSequence effectiveList) {
+            //Directly add areas after finding the breaks
+            addAreas(alg, partCount, originalList, effectiveList);
+        }
+        
+        protected void finishPart() {
+            // add static areas and resolve any new id areas
+            // finish page and add to area tree
+            finishPage();
+            currentPageNum++;
+            pageNumberString = pageSeq
+                    .makeFormattedPageNumber(currentPageNum);
+        }
+        
+        protected LayoutManager getCurrentChildLM() {
+            return childFLM;
+        }
+        
+    }
+    
     /** @see org.apache.fop.layoutmgr.LayoutManager#isBogus() */
     public boolean isBogus() {
         return false;
@@ -191,12 +244,19 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
      * @param context the layout context for finding breaks
      * @return the break for the page
      */
-    public BreakPoss getNextBreakPoss(LayoutContext context) {
+    public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
 
         LayoutManager curLM; // currently active LM
 
         while ((curLM = getChildLM()) != null) {
-            BreakPoss bp = null;
+/*LF*/      LinkedList returnedList = null;
+/*LF*/      if (childFLM == null && (curLM instanceof FlowLayoutManager)) {
+/*LF*/          childFLM = (FlowLayoutManager)curLM;
+/*LF*/      } else {
+/*LF*/          if (curLM != childFLM) {
+/*LF*/              System.out.println("PLM> figlio sconosciuto (invalid child LM)");
+/*LF*/          }
+/*LF*/      }
 
             LayoutContext childLC = new LayoutContext(0);
             childLC.setStackLimit(new MinOptMax(flowBPD));
@@ -205,11 +265,10 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
             if (!curLM.isFinished()) {
                 pageSeq.setLayoutDimension(PercentBase.REFERENCE_AREA_IPD, flowIPD);
                 pageSeq.setLayoutDimension(PercentBase.REFERENCE_AREA_BPD, flowBPD);
-                bp = curLM.getNextBreakPoss(childLC);
+/*LF*/          returnedList = curLM.getNextKnuthElements(childLC, alignment);
             }
-            if (bp != null) {
-                return new BreakPoss(
-                         new BlockBreakPosition(curLM, bp));
+            if (returnedList != null) {
+                return returnedList;
             }
         }
         setFinished(true);
@@ -250,20 +309,6 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
         return null;
     }
 
-    /**
-     * Add the areas to the current page.
-     * Given the page break position this adds the areas to the current
-     * page.
-     *
-     * @param bbp the block break position
-     */
-    public void addAreas(BlockBreakPosition bbp) {
-        List list = new java.util.ArrayList();
-        list.add(bbp.breakps);
-        bbp.getLM().addAreas(new BreakPossPosIter(list, 0,
-                              1), null);
-    }
-
     /**
      * Add an ID reference to the current page.
      * When adding areas the area adds its ID reference.
@@ -603,7 +648,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
         }
         else {
             /* IF we are on the kind of page we need, we'll need a new page. */
-            if (currentPageNum%2 != 0) {
+            if (currentPageNum % 2 != 0) {
                 // Current page is odd
                 return (breakValue == Constants.EN_ODD_PAGE);
             }
@@ -628,8 +673,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
             else {
                 return (breakValue == Constants.EN_ODD_PAGE);
             }
-        }
-        else {
+        } else {
             return true;
         }
     }
index 4d5fe4130f2e2928acd7a5dd9d19b40d78cd01b0..53871939f2094fd59d2d3d1d144f2f1b19c7ceaf 100644 (file)
@@ -37,8 +37,7 @@ import org.apache.fop.traits.MinOptMax;
  * LayoutManager for text (a sequence of characters) which generates one
  * or more inline areas.
  */
-public class TextLayoutManager extends AbstractLayoutManager
-                               implements InlineLevelLayoutManager {
+public class TextLayoutManager extends LeafNodeLayoutManager {
 
     /**
      * Store information about each potential text area.
@@ -131,6 +130,7 @@ public class TextLayoutManager extends AbstractLayoutManager
      * @param node The FOText object to be rendered
      */
     public TextLayoutManager(FOText node) {
+        super();
         foText = node;
         
         textArray = new char[node.endIndex - node.startIndex];
@@ -645,10 +645,10 @@ public class TextLayoutManager extends AbstractLayoutManager
                 textArea.setOffset(context.getMiddleBaseline() + fs.getXHeight() / 2);
             break;
             case EN_TOP:
-                textArea.setOffset(fs.getAscender());
+                textArea.setOffset(context.getTopBaseline() + fs.getAscender());
             break;
             case EN_BOTTOM:
-                textArea.setOffset(context.getLineHeight() - bpd + fs.getAscender());
+                textArea.setOffset(context.getBottomBaseline() - bpd + fs.getAscender());
             break;
             case EN_BASELINE:
             default:
@@ -706,7 +706,7 @@ public class TextLayoutManager extends AbstractLayoutManager
                                        - 6 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
                                        new LeafPosition(this, -1), true));
                     returnList.add
-                        (new KnuthBox(0, 0, 0, 0,
+                        (new KnuthInlineBox(0, 0, 0, 0,
                                       new LeafPosition(this, -1), true));
                     returnList.add
                         (new KnuthPenalty(0, KnuthElement.INFINITE, false,
@@ -724,14 +724,14 @@ public class TextLayoutManager extends AbstractLayoutManager
                                       (short) 1, (short) 0,
                                       wordSpaceIPD, false));
                     returnList.add
-                        (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
+                        (new KnuthGlue(0, 3 * wordSpaceIPD.opt, 0,
                                        new LeafPosition(this, vecAreaInfo.size() - 1), false));
                     returnList.add
                         (new KnuthPenalty(0, 0, false,
                                           new LeafPosition(this, -1), true));
                     returnList.add
                         (new KnuthGlue(wordSpaceIPD.opt,
-                                       - 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
+                                       - 3 * wordSpaceIPD.opt, 0,
                                        new LeafPosition(this, -1), true));
                     iNextStart ++;
                     break;
@@ -791,42 +791,64 @@ public class TextLayoutManager extends AbstractLayoutManager
                 for (; iTempStart < textArray.length
                         && textArray[iTempStart] != SPACE
                         && textArray[iTempStart] != NBSPACE
-                        && textArray[iTempStart] != NEWLINE;
+                     && textArray[iTempStart] != NEWLINE
+                     && !(iTempStart > iNextStart
+                          && alignment == EN_JUSTIFY
+                          && BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0);
                         iTempStart++) {
                     wordIPD.add(fs.getCharWidth(textArray[iTempStart]));
                 }
-                wordIPD.add(MinOptMax.multiply(letterSpaceIPD, (iTempStart - iThisStart - 1)));
+                int iLetterSpaces = iTempStart - iThisStart - 1;
+                wordIPD.add(MinOptMax.multiply(letterSpaceIPD, iLetterSpaces));
                 vecAreaInfo.add
                     (new AreaInfo(iThisStart, iTempStart, (short) 0,
-                                  (short) (iTempStart - iThisStart - 1),
+                                  (short) iLetterSpaces,
                                   wordIPD, false));
                 if (letterSpaceIPD.min == letterSpaceIPD.max) {
                     // constant letter space; simply return a box
                     // whose width includes letter spaces
                     returnList.add
-                        (new KnuthBox(wordIPD.opt, lead, total, middle,
+                        (new KnuthInlineBox(wordIPD.opt, lead, total, middle,
                                       new LeafPosition(this, vecAreaInfo.size() - 1), false));
-                    iNextStart = iTempStart;
                 } else {
                     // adjustable letter space;
                     // some other KnuthElements are needed
                     returnList.add
-                        (new KnuthBox(wordIPD.opt - (iTempStart - iThisStart - 1) * letterSpaceIPD.opt,
+                        (new KnuthInlineBox(wordIPD.opt - iLetterSpaces * letterSpaceIPD.opt,
                                       lead, total, middle,
                                       new LeafPosition(this, vecAreaInfo.size() - 1), false));
                     returnList.add
                         (new KnuthPenalty(0, KnuthElement.INFINITE, false,
                                           new LeafPosition(this, -1), true));
                     returnList.add
-                        (new KnuthGlue((iTempStart - iThisStart - 1) * letterSpaceIPD.opt,
-                                       (iTempStart - iThisStart - 1) * (letterSpaceIPD.max - letterSpaceIPD.opt),
-                                       (iTempStart - iThisStart - 1) * (letterSpaceIPD.opt - letterSpaceIPD.min),
+                        (new KnuthGlue(iLetterSpaces * letterSpaceIPD.opt,
+                                       iLetterSpaces * (letterSpaceIPD.max - letterSpaceIPD.opt),
+                                       iLetterSpaces * (letterSpaceIPD.opt - letterSpaceIPD.min),
                                        new LeafPosition(this, -1), true));
                     returnList.add
-                        (new KnuthBox(0, lead, total, middle,
-                                      new LeafPosition(this, -1), true));
-                    iNextStart = iTempStart;
+                        (new KnuthInlineBox(0, lead, total, middle,
+                                            new LeafPosition(this, -1), true));
                 }
+                // if the last character is '-' or '/', it could be used as a line end;
+                // add a flagged penalty element and glue element representing a suppressible 
+                // letter space if the next character is not a space
+                if (BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0
+                    && iTempStart < textArray.length
+                    && textArray[iTempStart] != SPACE
+                    && textArray[iTempStart] != NBSPACE) {
+                    returnList.add
+                        (new KnuthPenalty(0, LayoutManager.FLAGGED_PENALTY, true,
+                                          new LeafPosition(this, -1), false));
+                    returnList.add
+                        (new KnuthGlue(letterSpaceIPD.opt,
+                                       letterSpaceIPD.max - letterSpaceIPD.opt,
+                                       letterSpaceIPD.opt - letterSpaceIPD.min,
+                                       new LeafPosition(this, -1), false));
+                    // update the information in the AreaInfo, adding one more letter space
+                    AreaInfo ai = (AreaInfo) vecAreaInfo.get(vecAreaInfo.size() - 1);
+                    ai.iLScount ++;
+                }
+                iNextStart = iTempStart;
             }
         } // end of while
         setFinished(true);
@@ -837,19 +859,38 @@ public class TextLayoutManager extends AbstractLayoutManager
         }
     }
 
-    public KnuthElement addALetterSpaceTo(KnuthElement element) {
-        LeafPosition pos = (LeafPosition) element.getPosition();
+    public List addALetterSpaceTo(List oldList) {
+        // old list contains only a box, or the sequence: box penalty glue box;
+        // look at the Position stored in the first element in oldList
+        // which is always a box
+        ListIterator oldListIterator = oldList.listIterator();
+        LeafPosition pos = (LeafPosition) ((KnuthBox) oldListIterator.next()).getPosition();
         AreaInfo ai = (AreaInfo) vecAreaInfo.get(pos.getLeafPos());
         ai.iLScount ++;
         ai.ipdArea.add(letterSpaceIPD);
-        if (letterSpaceIPD.min == letterSpaceIPD.max) {
-            return new KnuthBox(ai.ipdArea.opt, lead, total, middle, pos, false);
+        if (BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0) {
+            // the last character could be used as a line break
+            // append new elements to oldList
+            oldListIterator = oldList.listIterator(oldList.size());
+            oldListIterator.add(new KnuthPenalty(0, LayoutManager.FLAGGED_PENALTY, true,
+                                                 new LeafPosition(this, -1), false));
+            oldListIterator.add(new KnuthGlue(letterSpaceIPD.opt,
+                                       letterSpaceIPD.max - letterSpaceIPD.opt,
+                                       letterSpaceIPD.opt - letterSpaceIPD.min,
+                                       new LeafPosition(this, -1), false));
+        } else if (letterSpaceIPD.min == letterSpaceIPD.max) {
+            // constant letter space: replace the box
+            oldListIterator.set(new KnuthInlineBox(ai.ipdArea.opt, lead, total, middle, pos, false));
         } else {
-            return new KnuthGlue(ai.iLScount * letterSpaceIPD.opt,
-                                 ai.iLScount * (letterSpaceIPD.max - letterSpaceIPD.opt),
-                                 ai.iLScount * (letterSpaceIPD.opt - letterSpaceIPD.min),
-                                 new LeafPosition(this, -1), true);
+            // adjustable letter space: replace the glue
+            oldListIterator.next(); // this would return the penalty element
+            oldListIterator.next(); // this would return the glue element
+            oldListIterator.set(new KnuthGlue(ai.iLScount * letterSpaceIPD.opt,
+                                              ai.iLScount * (letterSpaceIPD.max - letterSpaceIPD.opt),
+                                              ai.iLScount * (letterSpaceIPD.opt - letterSpaceIPD.min),
+                                              new LeafPosition(this, -1), true));
         }
+        return oldList;
     }
 
     public void hyphenate(Position pos, HyphContext hc) {
@@ -948,7 +989,7 @@ public class TextLayoutManager extends AbstractLayoutManager
     }
 
     public LinkedList getChangedKnuthElements(List oldList,
-                                              int flaggedPenalty,
+                                              /*int flaggedPenalty,*/
                                               int alignment) {
         if (isFinished()) {
             return null;
@@ -960,13 +1001,21 @@ public class TextLayoutManager extends AbstractLayoutManager
             AreaInfo ai = (AreaInfo) vecAreaInfo.get(iReturnedIndex);
             if (ai.iWScount == 0) {
                 // ai refers either to a word or a word fragment
+
+                // if the last character is '-' or '/' and the next character is not a space
+                // one of the letter spaces must be represented using a penalty and a glue,
+                // and its width must be subtracted
+                if (BREAK_CHARS.indexOf(textArray[ai.iBreakIndex - 1]) >= 0
+                    && ai.iLScount == (ai.iBreakIndex - ai.iStartIndex)) {
+                    ai.ipdArea.add(new MinOptMax(-letterSpaceIPD.min, -letterSpaceIPD.opt, -letterSpaceIPD.max));
+                }
                 if (letterSpaceIPD.min == letterSpaceIPD.max) {
                     returnList.add
-                        (new KnuthBox(ai.ipdArea.opt, lead, total, middle,
+                        (new KnuthInlineBox(ai.ipdArea.opt, lead, total, middle,
                                       new LeafPosition(this, iReturnedIndex), false));
                 } else {
                     returnList.add
-                        (new KnuthBox(ai.ipdArea.opt
+                        (new KnuthInlineBox(ai.ipdArea.opt
                                       - ai.iLScount * letterSpaceIPD.opt,
                                       lead, total, middle, 
                                       new LeafPosition(this, iReturnedIndex), false));
@@ -979,17 +1028,37 @@ public class TextLayoutManager extends AbstractLayoutManager
                                        ai.iLScount * (letterSpaceIPD.opt - letterSpaceIPD.min),
                                        new LeafPosition(this, -1), true));
                     returnList.add
-                        (new KnuthBox(0, 0, 0, 0,
+                        (new KnuthInlineBox(0, 0, 0, 0,
                                       new LeafPosition(this, -1), true));
                 }
                 if (ai.bHyphenated) {
                     returnList.add
-                        (new KnuthPenalty(hyphIPD, flaggedPenalty, true,
+                        (new KnuthPenalty(hyphIPD, LayoutManager.FLAGGED_PENALTY, true,
                                           new LeafPosition(this, -1), false));
                 }
+                // if the last character is '-' or '/', it could be used as a line end;
+                // add a flagged penalty element and a glue element representing a suppressible 
+                // letter space if the next character is not a space
+                if (BREAK_CHARS.indexOf(textArray[ai.iBreakIndex - 1]) >= 0
+                    && ai.iLScount == (ai.iBreakIndex - ai.iStartIndex)) {
+                    returnList.add
+                        (new KnuthPenalty(0, LayoutManager.FLAGGED_PENALTY, true,
+                                          new LeafPosition(this, -1), false));
+                    returnList.add
+                        (new KnuthGlue(letterSpaceIPD.opt,
+                                       letterSpaceIPD.max - letterSpaceIPD.opt,
+                                       letterSpaceIPD.opt - letterSpaceIPD.min,
+                                       new LeafPosition(this, -1), false));
+                }
                 iReturnedIndex ++;
             } else {
                 // ai refers to a space
+                if (textArray[ai.iStartIndex] == NBSPACE) {
+                    returnList.add
+                        (new KnuthPenalty(0, KnuthElement.INFINITE, false,
+                                          new LeafPosition(this, -1),
+                                          false));
+                }
                 switch (alignment) {
                 case EN_CENTER :
                     returnList.add
@@ -1003,7 +1072,7 @@ public class TextLayoutManager extends AbstractLayoutManager
                                        - 6 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
                                        new LeafPosition(this, -1), true));
                     returnList.add
-                        (new KnuthBox(0, 0, 0, 0,
+                        (new KnuthInlineBox(0, 0, 0, 0,
                                       new LeafPosition(this, -1), true));
                     returnList.add
                         (new KnuthPenalty(0, KnuthElement.INFINITE, false,
index d4dee26533b6ceba46e99321c851fa2167a16a73..b0129f632772e7673c7ad653d8c023b5caba3c91 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
  
 package org.apache.fop.traits;
 
+import org.apache.fop.datatypes.KeepValue;
 import org.apache.fop.fo.Constants;
 
 /**
@@ -29,6 +30,13 @@ public class LayoutProps {
 
     public int breakBefore; // enum constant BreakBefore.xxx
     public int breakAfter; // enum constant BreakAfter.xxx
+    public KeepValue keepWithPrevious;  /*LF*/
+    public KeepValue keepWithNext;      /*LF*/
+    public KeepValue keepTogether;      /*LF*/
+    public int orphans;                 /*LF*/
+    public int widows;                  /*LF*/
+    public int blockProgressionUnit;    /*LF*/
+    public int lineStackingStrategy;    /*LF*/
     public boolean bIsSpan;
     public SpaceVal spaceBefore;
     public SpaceVal spaceAfter;