]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Merged back ChangingIPDHack branch into Trunk
authorVincent Hennebert <vhennebert@apache.org>
Wed, 26 Aug 2009 18:50:10 +0000 (18:50 +0000)
committerVincent Hennebert <vhennebert@apache.org>
Wed, 26 Aug 2009 18:50:10 +0000 (18:50 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@808157 13f79535-47bb-0310-9956-ffa450edef68

32 files changed:
src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java
src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java
src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java
src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java
src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java
src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java
src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java
src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java
src/java/org/apache/fop/layoutmgr/InlineKnuthSequence.java
src/java/org/apache/fop/layoutmgr/LayoutContext.java
src/java/org/apache/fop/layoutmgr/LayoutManager.java
src/java/org/apache/fop/layoutmgr/LeafPosition.java
src/java/org/apache/fop/layoutmgr/PageBreaker.java
src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
src/java/org/apache/fop/layoutmgr/PageProvider.java
src/java/org/apache/fop/layoutmgr/Position.java
src/java/org/apache/fop/layoutmgr/SpaceResolver.java
src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java
src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java
src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java
src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java
src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java
src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java
src/java/org/apache/fop/render/pdf/PDFEventProducer.xml
test/layoutengine/standard-testcases/flow_changing-ipd_1.xml [new file with mode: 0644]
test/layoutengine/standard-testcases/flow_changing-ipd_2.xml [new file with mode: 0644]
test/layoutengine/standard-testcases/flow_changing-ipd_3.xml [new file with mode: 0644]
test/layoutengine/standard-testcases/flow_changing-ipd_4.xml [new file with mode: 0644]
test/layoutengine/standard-testcases/flow_changing-ipd_block-container_1.xml [new file with mode: 0644]
test/layoutengine/standard-testcases/flow_changing-ipd_block-container_2.xml [new file with mode: 0644]

index f7acf3eb557e66ce233fbf549f5e26a093226c77..8c213d7d5d832c29d5ba236c2d7dde9fd52f8883 100644 (file)
 
 package org.apache.fop.layoutmgr;
 
+import java.util.List;
+import java.util.Stack;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+
 import org.apache.fop.datatypes.LengthBase;
 import org.apache.fop.datatypes.PercentBaseContext;
 import org.apache.fop.fo.FObj;
@@ -253,4 +257,20 @@ public abstract class AbstractBaseLayoutManager
         return fobj;
     }
 
+    /** {@inheritDoc} */
+    public void reset() {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    /** {@inheritDoc} */
+    public boolean isRestartable() {
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack,
+            Position positionAtIPDChange, LayoutManager restartAtLM) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
 }
index 6393935aeacd34cbe4d616afb68093341697807a..1a6f7cfb9838eef4a0bd06e6a6da457e98331a17 100644 (file)
@@ -19,6 +19,8 @@
 
 package org.apache.fop.layoutmgr;
 
+import java.util.Collections;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.ListIterator;
@@ -27,6 +29,7 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 import org.apache.fop.fo.Constants;
+import org.apache.fop.layoutmgr.BreakingAlgorithm.KnuthNode;
 import org.apache.fop.traits.MinOptMax;
 import org.apache.fop.util.ListUtil;
 
@@ -247,6 +250,11 @@ public abstract class AbstractBreaker {
      */
     protected abstract List getNextKnuthElements(LayoutContext context, int alignment);
 
+    protected List getNextKnuthElements(LayoutContext context, int alignment,
+            Position positionAtIPDChange, LayoutManager restartAtLM) {
+        throw new UnsupportedOperationException("TODO: implement acceptable fallback");
+    }
+
     /** @return true if there's no content that could be handled. */
     public boolean isEmpty() {
         return (this.blockLists.isEmpty());
@@ -290,14 +298,6 @@ public abstract class AbstractBreaker {
         ElementListObserver.observe(elementList, "breaker", null);
     }
 
-    /**
-     * Starts the page breaking process.
-     * @param flowBPD the constant available block-progression-dimension (used for every part)
-     */
-    public void doLayout(int flowBPD) {
-        doLayout(flowBPD, false);
-    }
-
     /**
      * Starts the page breaking process.
      * @param flowBPD the constant available block-progression-dimension (used for every part)
@@ -354,7 +354,6 @@ public abstract class AbstractBreaker {
                         getPageProvider(), createLayoutListener(),
                         alignment, alignmentLast, footnoteSeparatorLength,
                         isPartOverflowRecoveryActivated(), autoHeight, isSinglePartFavored());
-                int iOptPageCount;
 
                 BlockSequence effectiveList;
                 if (getCurrentDisplayAlign() == Constants.EN_X_FILL) {
@@ -365,21 +364,107 @@ public abstract class AbstractBreaker {
                     effectiveList = blockList;
                 }
 
-                //iOptPageCount = alg.firstFit(effectiveList, flowBPD, 1, true);
                 alg.setConstantLineWidth(flowBPD);
-                iOptPageCount = alg.findBreakingPoints(effectiveList, /*flowBPD,*/
-                            1, true, BreakingAlgorithm.ALL_BREAKS);
-                log.debug("PLM> iOptPageCount= " + iOptPageCount
-                        + " pageBreaks.size()= " + alg.getPageBreaks().size());
+                int optimalPageCount = alg.findBreakingPoints(effectiveList, 1, true,
+                        BreakingAlgorithm.ALL_BREAKS);
+                if (alg.ipdChanged()) {
+                    KnuthNode optimalBreak = alg.getBestNodeBeforeIPDChange();
+                    int positionIndex = optimalBreak.position;
+                    KnuthElement elementAtBreak = alg.getElement(positionIndex);
+                    Position positionAtBreak = elementAtBreak.getPosition();
+                    if (!(positionAtBreak instanceof SpaceResolver.SpaceHandlingBreakPosition)) {
+                        throw new UnsupportedOperationException(
+                                "Don't know how to restart at position" + positionAtBreak);
+                    }
+                    /* Retrieve the original position wrapped into this space position */
+                    positionAtBreak = positionAtBreak.getPosition();
+                    LayoutManager restartAtLM = null;
+                    List firstElements = Collections.EMPTY_LIST;
+                    if (containsNonRestartableLM(positionAtBreak)) {
+                        firstElements = new LinkedList();
+                        boolean boxFound = false;
+                        Iterator iter = effectiveList.listIterator(++positionIndex);
+                        Position position = null;
+                        while (iter.hasNext()
+                                && (position == null || containsNonRestartableLM(position))) {
+                            KnuthElement element = (KnuthElement) iter.next();
+                            positionIndex++;
+                            position = element.getPosition();
+                            if (element.isBox()) {
+                                boxFound = true;
+                                firstElements.add(element);
+                            } else if (boxFound) {
+                                firstElements.add(element);
+                            }
+                        }
+                        if (position instanceof SpaceResolver.SpaceHandlingBreakPosition) {
+                            /* Retrieve the original position wrapped into this space position */
+                            positionAtBreak = position.getPosition();
+                        }
+                    }
+                    if (positionAtBreak.getIndex() == -1) {
+                        /*
+                         * This is an indication that we are between two blocks
+                         * (possibly surrounded by another block), not inside a
+                         * paragraph.
+                         */
+                        Position position;
+                        Iterator iter = effectiveList.listIterator(positionIndex + 1);
+                        do {
+                            KnuthElement nextElement = (KnuthElement) iter.next();
+                            position = nextElement.getPosition();
+                        } while (position == null
+                                || position instanceof SpaceResolver.SpaceHandlingPosition
+                                || position instanceof SpaceResolver.SpaceHandlingBreakPosition
+                                    && position.getPosition().getIndex() == -1);
+                        LayoutManager surroundingLM = positionAtBreak.getLM();
+                        while (position.getLM() != surroundingLM) {
+                            position = position.getPosition();
+                        }
+                        restartAtLM = position.getPosition().getLM();
+                    }
+                    log.trace("IPD changes after page " + optimalPageCount + " at index "
+                            + optimalBreak.position);
+                    doPhase3(alg, optimalPageCount, blockList, effectiveList);
+
+                    blockLists.clear();
+                    blockListIndex = -1;
+                    nextSequenceStartsOn = getNextBlockList(childLC, Constants.EN_COLUMN,
+                            positionAtBreak, restartAtLM, firstElements);
+                } else {
+                    log.debug("PLM> iOptPageCount= " + optimalPageCount
+                            + " pageBreaks.size()= " + alg.getPageBreaks().size());
 
 
-                //*** Phase 3: Add areas ***
-                doPhase3(alg, iOptPageCount, blockList, effectiveList);
+                    //*** Phase 3: Add areas ***
+                    doPhase3(alg, optimalPageCount, blockList, effectiveList);
+                }
             }
         }
 
     }
 
+    /**
+     * Returns {@code true} if the given position or one of its descendants
+     * corresponds to a non-restartable LM.
+     *
+     * @param position a position
+     * @return {@code true} if there is a non-restartable LM in the hierarchy
+     */
+    private boolean containsNonRestartableLM(Position position) {
+        LayoutManager lm = position.getLM();
+        if (lm != null && !lm.isRestartable()) {
+            return true;
+        } else {
+            Position subPosition = position.getPosition();
+            if (subPosition == null) {
+                return false;
+            } else {
+                return containsNonRestartableLM(subPosition);
+            }
+        }
+    }
+
     /**
      * Phase 3 of Knuth algorithm: Adds the areas
      * @param alg PageBreakingAlgorithm instance which determined the breaks
@@ -559,6 +644,7 @@ public abstract class AbstractBreaker {
     protected int handleSpanChange(LayoutContext childLC, int nextSequenceStartsOn) {
         return nextSequenceStartsOn;
     }
+
     /**
      * 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
@@ -567,12 +653,38 @@ public abstract class AbstractBreaker {
      */
     protected int getNextBlockList(LayoutContext childLC,
             int nextSequenceStartsOn) {
+        return getNextBlockList(childLC, nextSequenceStartsOn, null, null, null);
+    }
+
+    /**
+     * 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 positionAtIPDChange last element on the part before an IPD change
+     * @param restartAtLM the layout manager from which to restart, if IPD
+     * change occurs between two LMs
+     * @param firstElements elements from non-restartable LMs on the new page
+     * @return the page on which the next content should appear after a hard
+     * break
+     */
+    protected int getNextBlockList(LayoutContext childLC, int nextSequenceStartsOn,
+            Position positionAtIPDChange, LayoutManager restartAtLM, List firstElements) {
         updateLayoutContext(childLC);
         //Make sure the span change signal is reset
         childLC.signalSpanChange(Constants.NOT_SET);
 
         BlockSequence blockList;
-        List returnedList = getNextKnuthElements(childLC, alignment);
+        List returnedList;
+        if (positionAtIPDChange == null) {
+            returnedList = getNextKnuthElements(childLC, alignment);
+        } else {
+            returnedList = getNextKnuthElements(childLC, alignment, positionAtIPDChange,
+                    restartAtLM);
+            returnedList.addAll(0, firstElements);
+        }
         if (returnedList != null) {
             if (returnedList.isEmpty()) {
                 nextSequenceStartsOn = handleSpanChange(childLC, nextSequenceStartsOn);
index 8dca1c74942ba4d7deb013ec53370d884595b17c..82f0599eb12fb9ffe4158a86298235a88588c635 100644 (file)
@@ -47,22 +47,22 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager
     private static Log log = LogFactory.getLog(AbstractLayoutManager.class);
 
     /** Parent LayoutManager for this LayoutManager */
-    protected LayoutManager parentLM = null;
+    protected LayoutManager parentLM;
     /** List of child LayoutManagers */
-    protected List childLMs = null;
+    protected List childLMs;
     /** Iterator for child LayoutManagers */
-    protected ListIterator fobjIter = null;
+    protected ListIterator fobjIter;
     /** Marker map for markers related to this LayoutManager */
-    private Map markers = null;
+    private Map markers;
 
     /** True if this LayoutManager has handled all of its content. */
-    private boolean isFinished = false;
+    private boolean isFinished;
 
     /** child LM during getNextKnuthElement phase */
-    protected LayoutManager curChildLM = null;
+    protected LayoutManager curChildLM;
 
     /** child LM iterator during getNextKnuthElement phase */
-    protected ListIterator childLMiter = null;
+    protected ListIterator childLMiter;
 
     private int lastGeneratedPosition = -1;
     private int smallestPosNumberChecked = Integer.MAX_VALUE;
@@ -122,6 +122,14 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager
         return null;
     }
 
+    protected void setCurrentChildLM(LayoutManager childLM) {
+        curChildLM = childLM;
+        childLMiter = new LMiter(this);
+        do {
+            curChildLM = (LayoutManager) childLMiter.next();
+        } while (curChildLM != childLM);
+    }
+
     /**
      * Return indication if getChildLM will return another LM.
      * @return true if another child LM is still available
@@ -450,4 +458,22 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager
         return (super.toString() + (fobj != null ? "[fobj=" + fobj.toString() + "]" : ""));
     }
 
+    /** {@inheritDoc} */
+    public void reset() {
+        isFinished = false;
+        curChildLM = null;
+        childLMiter = new LMiter(this);
+        /*
+         * Reset the children LM. Can't rely on childLMiter since it may have
+         * been set to null in checkEndOfLayout.
+         */
+        for (LMiter iter = new LMiter(this); iter.hasNext();) {
+            ((LayoutManager) iter.next()).reset();
+        }
+        if (fobj != null) {
+            markers = fobj.getMarkers();
+        }
+        lastGeneratedPosition = -1;
+    }
+
 }
index 7587613031e6035f1a517efd36d1b8e9eeec684a..0fa046aee9d9686570f13fe8fa03e5fb1e2a1c78 100644 (file)
@@ -382,4 +382,9 @@ public abstract class AbstractPageSequenceLayoutManager extends AbstractLayoutMa
         }
     }
 
+    /** {@inheritDoc} */
+    public void reset() {
+        throw new IllegalStateException();
+    }
+
 }
index a429359ade596b280c290ae6ff73ad1de423373f..14183c52ea3de22ac75dfa34140b4e51a6205474 100644 (file)
@@ -117,7 +117,7 @@ public class AreaAdditionUtil {
             // set space after for each LM, in order to implement
             // display-align = distribute
             lc.setSpaceAfter(layoutContext.getSpaceAfter());
-            lc.setStackLimitsFrom(layoutContext);
+            lc.setStackLimitBP(layoutContext.getStackLimitBP());
             childLM.addAreas(childPosIter, lc);
         }
 
index 4a4becebfb4a45110d965e3e1c18a18cb0eb4bf4..e86c5feaf5fa36c62a2d2319059d13a2c792887c 100644 (file)
@@ -24,6 +24,7 @@ import java.awt.geom.Rectangle2D;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.ListIterator;
+import java.util.Stack;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -397,6 +398,311 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager
         return returnList;
     }
 
+    /** {@inheritDoc} */
+    public List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack,
+            Position restartPosition, LayoutManager restartAtLM) {
+        resetSpaces();
+        if (isAbsoluteOrFixed()) {
+            return getNextKnuthElementsAbsolute(context, alignment);
+        }
+
+        autoHeight = false;
+        //boolean rotated = (getBlockContainerFO().getReferenceOrientation() % 180 != 0);
+        int maxbpd = context.getStackLimitBP().opt;
+        int allocBPD;
+        if (height.getEnum() == EN_AUTO
+                || (!height.isAbsolute() && getAncestorBlockAreaBPD() <= 0)) {
+            //auto height when height="auto" or "if that dimension is not specified explicitly
+            //(i.e., it depends on content's block-progression-dimension)" (XSL 1.0, 7.14.1)
+            allocBPD = maxbpd;
+            autoHeight = true;
+            if (getBlockContainerFO().getReferenceOrientation() == 0) {
+                //Cannot easily inline element list when ref-or="180"
+                inlineElementList = true;
+            }
+        } else {
+            allocBPD = height.getValue(this); //this is the content-height
+            allocBPD += getBPIndents();
+        }
+        vpContentBPD = allocBPD - getBPIndents();
+
+        referenceIPD = context.getRefIPD();
+        if (width.getEnum() == EN_AUTO) {
+            updateContentAreaIPDwithOverconstrainedAdjust();
+        } else {
+            int contentWidth = width.getValue(this);
+            updateContentAreaIPDwithOverconstrainedAdjust(contentWidth);
+        }
+
+        double contentRectOffsetX = 0;
+        contentRectOffsetX += getBlockContainerFO()
+                .getCommonMarginBlock().startIndent.getValue(this);
+        double contentRectOffsetY = 0;
+        contentRectOffsetY += getBlockContainerFO()
+                .getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
+        contentRectOffsetY += getBlockContainerFO()
+                .getCommonBorderPaddingBackground().getPaddingBefore(false, this);
+
+        updateRelDims(contentRectOffsetX, contentRectOffsetY, autoHeight);
+
+        int availableIPD = referenceIPD - getIPIndents();
+        if (getContentAreaIPD() > availableIPD) {
+            BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
+                    getBlockContainerFO().getUserAgent().getEventBroadcaster());
+            eventProducer.objectTooWide(this, getBlockContainerFO().getName(),
+                    getContentAreaIPD(), context.getRefIPD(),
+                    getBlockContainerFO().getLocator());
+        }
+
+        MinOptMax stackLimit = new MinOptMax(relDims.bpd);
+
+        List returnedList;
+        List contentList = new LinkedList();
+        List returnList = new LinkedList();
+
+        if (!breakBeforeServed) {
+            breakBeforeServed = true;
+            if (!context.suppressBreakBefore()) {
+                if (addKnuthElementsForBreakBefore(returnList, context)) {
+                    return returnList;
+                }
+            }
+        }
+
+        if (!firstVisibleMarkServed) {
+            addKnuthElementsForSpaceBefore(returnList, alignment);
+            context.updateKeepWithPreviousPending(getKeepWithPrevious());
+        }
+
+        addKnuthElementsForBorderPaddingBefore(returnList, !firstVisibleMarkServed);
+        firstVisibleMarkServed = true;
+
+        if (autoHeight && inlineElementList) {
+            //Spaces, border and padding to be repeated at each break
+            addPendingMarks(context);
+
+            BlockLevelLayoutManager curLM; // currently active LM
+            BlockLevelLayoutManager prevLM = null; // previously active LM
+
+            LayoutContext childLC = new LayoutContext(0);
+            if (lmStack.isEmpty()) {
+                assert restartAtLM != null && restartAtLM.getParent() == this;
+                curLM = (BlockLevelLayoutManager) restartAtLM;
+                curLM.reset();
+                setCurrentChildLM(curLM);
+
+                childLC.copyPendingMarksFrom(context);
+                childLC.setStackLimitBP(MinOptMax.subtract(context.getStackLimitBP(), stackLimit));
+                childLC.setRefIPD(relDims.ipd);
+                childLC.setWritingMode(getBlockContainerFO().getWritingMode());
+                if (curLM == this.childLMs.get(0)) {
+                    childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE);
+                    //Handled already by the parent (break collapsing, see above)
+                }
+
+                // get elements from curLM
+                returnedList = curLM.getNextKnuthElements(childLC, alignment);
+            } else {
+                curLM = (BlockLevelLayoutManager) lmStack.pop();
+                setCurrentChildLM(curLM);
+
+                childLC.copyPendingMarksFrom(context);
+                childLC.setStackLimitBP(MinOptMax.subtract(context.getStackLimitBP(), stackLimit));
+                childLC.setRefIPD(relDims.ipd);
+                childLC.setWritingMode(getBlockContainerFO().getWritingMode());
+                if (curLM == this.childLMs.get(0)) {
+                    childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE);
+                    //Handled already by the parent (break collapsing, see above)
+                }
+
+                // get elements from curLM
+                returnedList = curLM.getNextKnuthElements(childLC, alignment, lmStack,
+                        restartPosition, restartAtLM);
+            }
+            if (contentList.isEmpty() && childLC.isKeepWithPreviousPending()) {
+                //Propagate keep-with-previous up from the first child
+                context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
+                childLC.clearKeepWithPreviousPending();
+            }
+            if (returnedList.size() == 1
+                    && ((ListElement)returnedList.get(0)).isForcedBreak()) {
+                // 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
+                    addInBetweenBreak(contentList, context, childLC);
+                }
+                contentList.addAll(returnedList);
+                if (!returnedList.isEmpty()) {
+                    if (((ListElement) ListUtil.getLast(returnedList))
+                            .isForcedBreak()) {
+                        // 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;
+                    }
+                }
+            }
+            // propagate and clear
+            context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
+            childLC.clearKeepsPending();
+            prevLM = curLM;
+
+            while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) {
+                curLM.reset();
+                childLC = new LayoutContext(0);
+                childLC.copyPendingMarksFrom(context);
+                // curLM is a ?
+                childLC.setStackLimitBP(MinOptMax.subtract(context.getStackLimitBP(), stackLimit));
+                childLC.setRefIPD(relDims.ipd);
+                childLC.setWritingMode(getBlockContainerFO().getWritingMode());
+                if (curLM == this.childLMs.get(0)) {
+                    childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE);
+                    //Handled already by the parent (break collapsing, see above)
+                }
+
+                // get elements from curLM
+                returnedList = curLM.getNextKnuthElements(childLC, alignment);
+                if (contentList.isEmpty() && childLC.isKeepWithPreviousPending()) {
+                    //Propagate keep-with-previous up from the first child
+                    context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
+                    childLC.clearKeepWithPreviousPending();
+                }
+                if (returnedList.size() == 1
+                        && ((ListElement)returnedList.get(0)).isForcedBreak()) {
+                    // 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
+                        addInBetweenBreak(contentList, context, childLC);
+                    }
+                    contentList.addAll(returnedList);
+                    if (returnedList.isEmpty()) {
+                        //Avoid NoSuchElementException below (happens with empty blocks)
+                        continue;
+                    }
+                    if (((ListElement) ListUtil.getLast(returnedList))
+                            .isForcedBreak()) {
+                        // 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;
+                    }
+                }
+                // propagate and clear
+                context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
+                childLC.clearKeepsPending();
+                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, autoHeight);
+            boolean contentOverflows = breaker.isOverflow();
+            if (autoHeight) {
+                //Update content BPD now that it is known
+                int newHeight = breaker.deferredAlg.totalWidth;
+                boolean switchedProgressionDirection
+                    = (getBlockContainerFO().getReferenceOrientation() % 180 != 0);
+                if (switchedProgressionDirection) {
+                    setContentAreaIPD(newHeight);
+                } else {
+                    vpContentBPD = newHeight;
+                }
+                updateRelDims(contentRectOffsetX, contentRectOffsetY, false);
+            }
+
+            Position bcPosition = new BlockContainerPosition(this, breaker);
+            returnList.add(new KnuthBox(vpContentBPD, notifyPos(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) {
+                BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get(
+                        getBlockContainerFO().getUserAgent().getEventBroadcaster());
+                boolean canRecover = (getBlockContainerFO().getOverflow() != EN_ERROR_IF_OVERFLOW);
+                eventProducer.viewportOverflow(this, getBlockContainerFO().getName(),
+                        breaker.getOverflowAmount(), needClip(), canRecover,
+                        getBlockContainerFO().getLocator());
+            }
+        }
+        addKnuthElementsForBorderPaddingAfter(returnList, true);
+        addKnuthElementsForSpaceAfter(returnList, alignment);
+
+        //All child content is processed. Only break-after can occur now, so...
+        context.clearPendingMarks();
+        addKnuthElementsForBreakAfter(returnList, context);
+
+        context.updateKeepWithNextPending(getKeepWithNext());
+
+        setFinished(true);
+        return returnList;
+    }
+
+    /** {@inheritDoc} */
+    public boolean isRestartable() {
+        return true;
+    }
+
     private List getNextKnuthElementsAbsolute(LayoutContext context, int alignment) {
         autoHeight = false;
 
index 20895a38e708a75b36d85cde5c80a89105b62f02..126ab379695b87b3f4fdbeb5a5c9523eeaa84f1f 100644 (file)
@@ -22,6 +22,7 @@ package org.apache.fop.layoutmgr;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.ListIterator;
+import java.util.Stack;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -67,9 +68,6 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
     private MinOptMax effSpaceBefore;
     private MinOptMax effSpaceAfter;
 
-    /** The list of child BreakPoss instances. */
-    protected List childBreaks = new java.util.ArrayList();
-
     /**
      * Creates a new BlockLayoutManager.
      * @param inBlock the block FO object to create the layout manager for.
@@ -114,8 +112,19 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
 
     /** {@inheritDoc} */
     public List getNextKnuthElements(LayoutContext context, int alignment) {
+        return getNextKnuthElements(context, alignment, null, null, null);
+    }
+
+    /** {@inheritDoc} */
+    public List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack,
+            Position restartPosition, LayoutManager restartAtLM) {
         resetSpaces();
-        return super.getNextKnuthElements(context, alignment);
+        if (lmStack == null) {
+            return super.getNextKnuthElements(context, alignment);
+        } else {
+            return super.getNextKnuthElements(context, alignment, lmStack, restartPosition,
+                    restartAtLM);
+        }
     }
 
     private void resetSpaces() {
@@ -249,8 +258,8 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
         // and put them in a new list;
         LinkedList positionList = new LinkedList();
         Position pos;
-        boolean bSpaceBefore = false;
-        boolean bSpaceAfter = false;
+        boolean spaceBefore = false;
+        boolean spaceAfter = false;
         Position firstPos = null;
         Position lastPos = null;
         while (parentIter.hasNext()) {
@@ -273,11 +282,11 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
                 // this means the space was not discarded
                 if (positionList.size() == 0) {
                     // pos was in the element representing space-before
-                    bSpaceBefore = true;
+                    spaceBefore = true;
                     //log.trace(" space before");
                 } else {
                     // pos was in the element representing space-after
-                    bSpaceAfter = true;
+                    spaceAfter = true;
                     //log.trace(" space-after");
                 }
             } else if (innerPosition.getLM() == this
@@ -302,7 +311,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
             // the Positions in positionList were inside the elements
             // created by the LineLM
             childPosIter = new StackingIter(positionList.listIterator());
-            } else {
+        } else {
             // the Positions in positionList were inside the elements
             // created by the BlockLM in the createUnitElements() method
             //if (((Position) positionList.getLast()) instanceof
@@ -341,7 +350,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
             //                   + " spacing");
             // add space before and / or after the paragraph
             // to reach a multiple of bpUnit
-            if (bSpaceBefore && bSpaceAfter) {
+            if (spaceBefore && spaceAfter) {
                 foSpaceBefore = new SpaceVal(getBlockFO()
                                     .getCommonMarginBlock().spaceBefore, this).getSpace();
                 foSpaceAfter = new SpaceVal(getBlockFO()
@@ -354,7 +363,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
                         + foSpaceBefore.min
                         + foSpaceAfter.min)
                         * bpUnit - splitLength - adjustedSpaceBefore;
-                } else if (bSpaceBefore) {
+                } else if (spaceBefore) {
                 adjustedSpaceBefore = neededUnits(splitLength
                         + foSpaceBefore.min)
                         * bpUnit - splitLength;
@@ -547,5 +556,10 @@ public class BlockLayoutManager extends BlockStackingLayoutManager
         }
     }
 
+    /** {@inheritDoc} */
+    public boolean isRestartable() {
+        return true;
+    }
+
 }
 
index 73c8eb00d1f61c6025e694e57c4f7a32cfd6372d..53c529eaa48e1a0434550273f188b3e5416939c4 100644 (file)
@@ -23,6 +23,7 @@ import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.ListIterator;
+import java.util.Stack;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -30,12 +31,12 @@ import org.apache.commons.logging.LogFactory;
 import org.apache.fop.area.Area;
 import org.apache.fop.area.Block;
 import org.apache.fop.area.BlockParent;
-import org.apache.fop.fo.FObj;
 import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FObj;
 import org.apache.fop.fo.properties.BreakPropertySet;
 import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
-import org.apache.fop.fo.properties.SpaceProperty;
 import org.apache.fop.fo.properties.KeepProperty;
+import org.apache.fop.fo.properties.SpaceProperty;
 import org.apache.fop.layoutmgr.inline.InlineLayoutManager;
 import org.apache.fop.layoutmgr.inline.LineLayoutManager;
 import org.apache.fop.traits.MinOptMax;
@@ -54,31 +55,26 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager
      */
     private static Log log = LogFactory.getLog(BlockStackingLayoutManager.class);
 
-    /**
-     * Reference to FO whose areas it's managing or to the traits
-     * of the FO.
-     */
-    //protected LayoutManager curChildLM = null; AbstractLayoutManager also defines this!
-    protected BlockParent parentArea = null;
+    protected BlockParent parentArea;
 
     /** Value of the block-progression-unit (non-standard property) */
-    protected int bpUnit = 0;
+    protected int bpUnit;
     /** space-before value adjusted for block-progression-unit handling */
-    protected int adjustedSpaceBefore = 0;
+    protected int adjustedSpaceBefore;
     /** space-after value adjusted for block-progression-unit handling */
-    protected int adjustedSpaceAfter = 0;
+    protected int adjustedSpaceAfter;
     /** Only used to store the original list when createUnitElements is called */
-    protected List storedList = null;
+    protected List storedList;
     /** Indicates whether break before has been served or not */
-    protected boolean breakBeforeServed = false;
+    protected boolean breakBeforeServed;
     /** Indicates whether the first visible mark has been returned by this LM, yet */
-    protected boolean firstVisibleMarkServed = false;
+    protected boolean firstVisibleMarkServed;
     /** Reference IPD available */
-    protected int referenceIPD = 0;
+    protected int referenceIPD;
     /** the effective start-indent value */
-    protected int startIndent = 0;
+    protected int startIndent;
     /** the effective end-indent value */
-    protected int endIndent = 0;
+    protected int endIndent;
     /**
      * Holds the (one-time use) fo:block space-before
      * and -after properties.  Large fo:blocks are split
@@ -88,13 +84,13 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager
      * Block and space-after at the end of the last Block
      * used in rendering the fo:block.
      */
-    protected MinOptMax foSpaceBefore = null;
+    protected MinOptMax foSpaceBefore;
     /** see foSpaceBefore */
-    protected MinOptMax foSpaceAfter = null;
+    protected MinOptMax foSpaceAfter;
 
     private Position auxiliaryPosition;
 
-    private int contentAreaIPD = 0;
+    private int contentAreaIPD;
 
     /**
      * @param node the fo this LM deals with
@@ -248,38 +244,27 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager
 
     /** {@inheritDoc} */
     public List getNextKnuthElements(LayoutContext context, int alignment) {
-        //log.debug("BLM.getNextKnuthElements> keep-together = "
-              // + layoutProps.keepTogether.getType());
-        //log.debug(" keep-with-previous = " +
-              // layoutProps.keepWithPrevious.getType());
-        //log.debug(" keep-with-next = " +
-              // layoutProps.keepWithNext.getType());
-        BlockLevelLayoutManager curLM; // currently active LM
-        BlockLevelLayoutManager prevLM = null; // previously active LM
-
         referenceIPD = context.getRefIPD();
-
         updateContentAreaIPDwithOverconstrainedAdjust();
 
-        List returnedList = null;
         List contentList = new LinkedList();
-        List returnList = new LinkedList();
+        List elements = new LinkedList();
 
         if (!breakBeforeServed) {
             breakBeforeServed = true;
             if (!context.suppressBreakBefore()) {
-                if (addKnuthElementsForBreakBefore(returnList, context)) {
-                    return returnList;
+                if (addKnuthElementsForBreakBefore(elements, context)) {
+                    return elements;
                 }
             }
         }
 
         if (!firstVisibleMarkServed) {
-            addKnuthElementsForSpaceBefore(returnList, alignment);
+            addKnuthElementsForSpaceBefore(elements, alignment);
             context.updateKeepWithPreviousPending(getKeepWithPrevious());
         }
 
-        addKnuthElementsForBorderPaddingBefore(returnList, !firstVisibleMarkServed);
+        addKnuthElementsForBorderPaddingBefore(elements, !firstVisibleMarkServed);
         firstVisibleMarkServed = true;
 
         //Spaces, border and padding to be repeated at each break
@@ -288,142 +273,311 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager
         //Used to indicate a special break-after case when all content has already been generated.
         BreakElement forcedBreakAfterLast = null;
 
-        while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) {
+        LayoutManager currentChildLM;
+        while ((currentChildLM = (LayoutManager) getChildLM()) != null) {
             LayoutContext childLC = new LayoutContext(0);
-            childLC.copyPendingMarksFrom(context);
-            if (curLM instanceof LineLayoutManager) {
-                // curLM is a LineLayoutManager
-                // set stackLimit for lines (stack limit is now i-p-direction, not b-p-direction!)
-                childLC.setStackLimitBP(context.getStackLimitBP());
-                childLC.setStackLimitIP(new MinOptMax(getContentAreaIPD()));
-                childLC.setRefIPD(getContentAreaIPD());
-            } else {
-                // curLM is a ?
-                //childLC.setStackLimit(MinOptMax.subtract(context
-                //        .getStackLimit(), stackSize));
-                childLC.setStackLimitBP(context.getStackLimitBP());
-                childLC.setRefIPD(referenceIPD);
-            }
-            if (curLM == this.childLMs.get(0)) {
-                childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE);
-                //Handled already by the parent (break collapsing, see above)
-            }
 
-            // get elements from curLM
-            returnedList = curLM.getNextKnuthElements(childLC, alignment);
-            if (contentList.isEmpty() && childLC.isKeepWithPreviousPending()) {
+            List childrenElements = getNextChildElements(currentChildLM, context, childLC,
+                    alignment);
+
+            if (contentList.isEmpty()) {
                 //Propagate keep-with-previous up from the first child
                 context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
-                childLC.clearKeepWithPreviousPending();
             }
-            if (returnedList != null
-                    && returnedList.size() == 1
-                    && ((ListElement) returnedList.get(0)).isForcedBreak()) {
+            if (childrenElements != null && !childrenElements.isEmpty()) {
+                if (!contentList.isEmpty()
+                        && !ElementListUtils.startsWithForcedBreak(childrenElements)) {
+                    // there is a block handled by prevLM before the one
+                    // handled by curLM, and the one handled
+                    // by the current LM does not begin with a break
+                    addInBetweenBreak(contentList, context, childLC);
+                }
+                if (childrenElements.size() == 1
+                        && ElementListUtils.startsWithForcedBreak(childrenElements)) {
+
+                    if (currentChildLM.isFinished() && !hasNextChildLM()) {
+                        // a descendant of this block has break-before
+                        forcedBreakAfterLast = (BreakElement) childrenElements.get(0);
+                        context.clearPendingMarks();
+                        break;
+                    }
 
-                if (curLM.isFinished() && !hasNextChildLM()) {
+                    if (contentList.isEmpty()) {
+                        // Empty fo:block, zero-length box makes sure the IDs and/or markers
+                        // are registered and borders/padding are painted.
+                        elements.add(new KnuthBox(0, notifyPos(new Position(this)), false));
+                    }
                     // a descendant of this block has break-before
-                    forcedBreakAfterLast = (BreakElement) returnedList.get(0);
+                    contentList.addAll(childrenElements);
+
+                    wrapPositionElements(contentList, elements);
+
+                    return elements;
+                } else {
+                    contentList.addAll(childrenElements);
+                    if (ElementListUtils.endsWithForcedBreak(childrenElements)) {
+                        // a descendant of this block has break-after
+                        if (currentChildLM.isFinished() && !hasNextChildLM()) {
+                            forcedBreakAfterLast = (BreakElement) ListUtil.removeLast(contentList);
+                            context.clearPendingMarks();
+                            break;
+                        }
+
+                        wrapPositionElements(contentList, elements);
+
+                        return elements;
+                    }
+                }
+                context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
+            }
+        }
+
+        if (!contentList.isEmpty()) {
+            wrapPositionElements(contentList, elements);
+        } else if (forcedBreakAfterLast == null) {
+            // Empty fo:block, zero-length box makes sure the IDs and/or markers
+            // are registered.
+            elements.add(new KnuthBox(0, notifyPos(new Position(this)), true));
+        }
+
+        addKnuthElementsForBorderPaddingAfter(elements, true);
+        addKnuthElementsForSpaceAfter(elements, alignment);
+
+        //All child content is processed. Only break-after can occur now, so...
+        context.clearPendingMarks();
+        if (forcedBreakAfterLast == null) {
+            addKnuthElementsForBreakAfter(elements, context);
+        } else {
+            forcedBreakAfterLast.clearPendingMarks();
+            elements.add(forcedBreakAfterLast);
+        }
+
+        context.updateKeepWithNextPending(getKeepWithNext());
+
+        setFinished(true);
+
+        return elements;
+    }
+
+    /** {@inheritDoc} */
+    public List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack,
+            Position restartPosition, LayoutManager restartAtLM) {
+        referenceIPD = context.getRefIPD();
+        updateContentAreaIPDwithOverconstrainedAdjust();
+
+        List contentList = new LinkedList();
+        List elements = new LinkedList();
+
+        if (!breakBeforeServed) {
+            breakBeforeServed = true;
+            if (!context.suppressBreakBefore()) {
+                if (addKnuthElementsForBreakBefore(elements, context)) {
+                    return elements;
+                }
+            }
+        }
+
+        if (!firstVisibleMarkServed) {
+            addKnuthElementsForSpaceBefore(elements, alignment);
+            context.updateKeepWithPreviousPending(getKeepWithPrevious());
+        }
+
+        addKnuthElementsForBorderPaddingBefore(elements, !firstVisibleMarkServed);
+        firstVisibleMarkServed = true;
+
+        //Spaces, border and padding to be repeated at each break
+        addPendingMarks(context);
+
+        //Used to indicate a special break-after case when all content has already been generated.
+        BreakElement forcedBreakAfterLast = null;
+
+        LayoutContext childLC = new LayoutContext(0);
+        List childrenElements;
+        LayoutManager currentChildLM;
+        if (lmStack.isEmpty()) {
+            assert restartAtLM != null && restartAtLM.getParent() == this;
+            currentChildLM = restartAtLM;
+            currentChildLM.reset();
+            setCurrentChildLM(currentChildLM);
+
+            childrenElements = getNextChildElements(currentChildLM, context, childLC,
+                    alignment);
+        } else {
+            currentChildLM = (BlockLevelLayoutManager) lmStack.pop();
+            setCurrentChildLM(currentChildLM);
+            childrenElements = getNextChildElements(currentChildLM, context, childLC, alignment,
+                    lmStack, restartPosition, restartAtLM);
+        }
+
+        if (contentList.isEmpty()) {
+            //Propagate keep-with-previous up from the first child
+            context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
+        }
+        if (childrenElements != null && !childrenElements.isEmpty()) {
+            if (!contentList.isEmpty()
+                    && !ElementListUtils.startsWithForcedBreak(childrenElements)) {
+                // there is a block handled by prevLM before the one
+                // handled by curLM, and the one handled
+                // by the current LM does not begin with a break
+                addInBetweenBreak(contentList, context, childLC);
+            }
+            if (childrenElements.size() == 1
+                    && ElementListUtils.startsWithForcedBreak(childrenElements)) {
+
+                if (currentChildLM.isFinished() && !hasNextChildLM()) {
+                    // a descendant of this block has break-before
+                    forcedBreakAfterLast = (BreakElement) childrenElements.get(0);
                     context.clearPendingMarks();
-                    break;
+//                    break; TODO
                 }
 
                 if (contentList.isEmpty()) {
                     // Empty fo:block, zero-length box makes sure the IDs and/or markers
                     // are registered and borders/padding are painted.
-                    returnList.add(new KnuthBox(0, notifyPos(new Position(this)), false));
+                    elements.add(new KnuthBox(0, notifyPos(new Position(this)), false));
                 }
                 // a descendant of this block has break-before
-                contentList.addAll(returnedList);
-
-                /* extension: conversione di tutta la sequenza fin'ora ottenuta */
-                if (bpUnit > 0) {
-                    storedList = contentList;
-                    contentList = createUnitElements(contentList);
-                }
-                /* end of extension */
+                contentList.addAll(childrenElements);
 
-                // "wrap" the Position inside each element
-                // moving the elements from contentList to returnList
-                returnedList = new LinkedList();
-                wrapPositionElements(contentList, returnList);
+                wrapPositionElements(contentList, elements);
 
-                return returnList;
+                return elements;
             } else {
-                if (returnedList == null || returnedList.isEmpty()) {
-                    //Avoid NoSuchElementException below (happens with empty blocks)
-                    continue;
+                contentList.addAll(childrenElements);
+                if (ElementListUtils.endsWithForcedBreak(childrenElements)) {
+                    // a descendant of this block has break-after
+                    if (currentChildLM.isFinished() && !hasNextChildLM()) {
+                        forcedBreakAfterLast = (BreakElement) ListUtil.removeLast(contentList);
+                        context.clearPendingMarks();
+//                        break; TODO
+                    }
+
+                    wrapPositionElements(contentList, elements);
+
+                    return elements;
                 }
-                if (prevLM != null
-                        && !ElementListUtils.startsWithForcedBreak(returnedList)) {
+            }
+            context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
+        }
+
+        while ((currentChildLM = (LayoutManager) getChildLM()) != null) {
+            currentChildLM.reset(); // TODO won't work with forced breaks
+
+            childLC = new LayoutContext(0);
+
+            childrenElements = getNextChildElements(currentChildLM, context, childLC,
+                    alignment);
+
+            if (contentList.isEmpty()) {
+                //Propagate keep-with-previous up from the first child
+                context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
+            }
+            if (childrenElements != null && !childrenElements.isEmpty()) {
+                if (!contentList.isEmpty()
+                        && !ElementListUtils.startsWithForcedBreak(childrenElements)) {
                     // there is a block handled by prevLM before the one
                     // handled by curLM, and the one handled
                     // by the current LM does not begin with a break
                     addInBetweenBreak(contentList, context, childLC);
                 }
-                contentList.addAll(returnedList);
-                if (ElementListUtils.endsWithForcedBreak(returnedList)) {
-                    // a descendant of this block has break-after
-                    if (curLM.isFinished() && !hasNextChildLM()) {
-                        forcedBreakAfterLast = (BreakElement) ListUtil
-                                .removeLast(contentList);
+                if (childrenElements.size() == 1
+                        && ElementListUtils.startsWithForcedBreak(childrenElements)) {
+
+                    if (currentChildLM.isFinished() && !hasNextChildLM()) {
+                        // a descendant of this block has break-before
+                        forcedBreakAfterLast = (BreakElement) childrenElements.get(0);
                         context.clearPendingMarks();
                         break;
                     }
 
-                    /* extension: conversione di tutta la sequenza fin'ora ottenuta */
-                    if (bpUnit > 0) {
-                        storedList = contentList;
-                        contentList = createUnitElements(contentList);
+                    if (contentList.isEmpty()) {
+                        // Empty fo:block, zero-length box makes sure the IDs and/or markers
+                        // are registered and borders/padding are painted.
+                        elements.add(new KnuthBox(0, notifyPos(new Position(this)), false));
                     }
-                    /* end of extension */
+                    // a descendant of this block has break-before
+                    contentList.addAll(childrenElements);
 
-                    returnedList = new LinkedList();
-                    wrapPositionElements(contentList, returnList);
+                    wrapPositionElements(contentList, elements);
 
-                    return returnList;
+                    return elements;
+                } else {
+                    contentList.addAll(childrenElements);
+                    if (ElementListUtils.endsWithForcedBreak(childrenElements)) {
+                        // a descendant of this block has break-after
+                        if (currentChildLM.isFinished() && !hasNextChildLM()) {
+                            forcedBreakAfterLast = (BreakElement) ListUtil.removeLast(contentList);
+                            context.clearPendingMarks();
+                            break;
+                        }
+
+                        wrapPositionElements(contentList, elements);
+
+                        return elements;
+                    }
                 }
+                context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
             }
-            // propagate and clear
-            context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
-            childLC.clearKeepsPending();
-            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();
         if (!contentList.isEmpty()) {
-            wrapPositionElements(contentList, returnList);
+            wrapPositionElements(contentList, elements);
         } else if (forcedBreakAfterLast == null) {
             // Empty fo:block, zero-length box makes sure the IDs and/or markers
             // are registered.
-            returnList.add(new KnuthBox(0, notifyPos(new Position(this)), true));
+            elements.add(new KnuthBox(0, notifyPos(new Position(this)), true));
         }
 
-        addKnuthElementsForBorderPaddingAfter(returnList, true);
-        addKnuthElementsForSpaceAfter(returnList, alignment);
+        addKnuthElementsForBorderPaddingAfter(elements, true);
+        addKnuthElementsForSpaceAfter(elements, alignment);
 
         //All child content is processed. Only break-after can occur now, so...
         context.clearPendingMarks();
         if (forcedBreakAfterLast == null) {
-            addKnuthElementsForBreakAfter(returnList, context);
-        }
-
-        if (forcedBreakAfterLast != null) {
+            addKnuthElementsForBreakAfter(elements, context);
+        } else {
             forcedBreakAfterLast.clearPendingMarks();
-            returnList.add(forcedBreakAfterLast);
+            elements.add(forcedBreakAfterLast);
         }
 
         context.updateKeepWithNextPending(getKeepWithNext());
 
         setFinished(true);
 
-        return returnList;
+        return elements;
+    }
+
+    private List getNextChildElements(LayoutManager childLM, LayoutContext context,
+            LayoutContext childLC, int alignment) {
+        return getNextChildElements(childLM, context, childLC, alignment, null, null, null);
+    }
+
+    private List getNextChildElements(LayoutManager childLM, LayoutContext context,
+            LayoutContext childLC, int alignment, Stack lmStack, Position restartPosition,
+            LayoutManager restartAtLM) {
+        childLC.copyPendingMarksFrom(context);
+        childLC.setStackLimitBP(context.getStackLimitBP());
+        if (childLM instanceof LineLayoutManager) {
+            childLC.setRefIPD(getContentAreaIPD());
+        } else {
+            childLC.setRefIPD(referenceIPD);
+        }
+        if (childLM == this.childLMs.get(0)) {
+            childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE);
+            //Handled already by the parent (break collapsing, see above)
+        }
+
+        if (lmStack == null) {
+            return childLM.getNextKnuthElements(childLC, alignment);
+        } else {
+            if (childLM instanceof LineLayoutManager) {
+                return ((LineLayoutManager) childLM).getNextKnuthElements(childLC, alignment,
+                        (LeafPosition) restartPosition);
+            } else {
+                return childLM.getNextKnuthElements(childLC, alignment,
+                        lmStack, restartPosition, restartAtLM);
+            }
+        }
     }
 
     /**
@@ -1654,5 +1808,13 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager
         return -1;
     }
 
+    /** {@inheritDoc} */
+    public void reset() {
+        super.reset();
+        breakBeforeServed = false;
+        firstVisibleMarkServed = false;
+        // TODO startIndent, endIndent
+    }
+
 }
 
index c6bd7bcecdf12f314b5c6dfb3b68d61bf2d79166..3a688cce889cfb8dc2ccc32389d9a525ebb79c66 100644 (file)
@@ -491,6 +491,9 @@ public abstract class BreakingAlgorithm {
                     elementIndex, previousIsBox, allowedBreaks).isBox();
 
             if (activeNodeCount == 0) {
+                if (ipdChanged()) {
+                    return handleIpdChange();
+                }
                 if (!force) {
                     log.debug("Could not find a set of breaking points " + threshold);
                     return 0;
@@ -535,6 +538,14 @@ public abstract class BreakingAlgorithm {
         return line;
     }
 
+    protected boolean ipdChanged() {
+        return false;
+    }
+
+    protected int handleIpdChange() {
+        throw new IllegalStateException();
+    }
+
     /**
      * Recover from a {@link KnuthNode} leading to a line that is too long.
      * The default implementation creates a new node corresponding to a break
@@ -1283,12 +1294,8 @@ public abstract class BreakingAlgorithm {
      * @return the width/length in millipoints
      */
     protected int getLineWidth(int line) {
-        if (this.lineWidth < 0) {
-            throw new IllegalStateException("lineWidth must be set"
-                    + (this.lineWidth != 0 ? " and positive, but it is: " + this.lineWidth : ""));
-        } else {
-            return this.lineWidth;
-        }
+        assert lineWidth >= 0;
+        return this.lineWidth;
     }
 
     /** @return the constant line/part width or -1 if there is no such value */
@@ -1321,7 +1328,7 @@ public abstract class BreakingAlgorithm {
      * @param par the corresponding paragraph
      * @param total the number of lines into which the paragraph will be broken
      */
-    private void calculateBreakPoints(KnuthNode node, KnuthSequence par,
+    protected void calculateBreakPoints(KnuthNode node, KnuthSequence par,
                                       int total) {
         KnuthNode bestActiveNode = node;
         // use bestActiveNode to determine the optimum breakpoints
index 5a80c331894e9c86ee78a75ed34052ef03654159..67b9b42543b875b019c25db2868efefaff0e4777 100644 (file)
@@ -22,6 +22,7 @@ package org.apache.fop.layoutmgr;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.ListIterator;
+import java.util.Stack;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -29,8 +30,6 @@ import org.apache.commons.logging.LogFactory;
 import org.apache.fop.area.Area;
 import org.apache.fop.area.BlockParent;
 import org.apache.fop.fo.pagination.Flow;
-import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager;
-import org.apache.fop.layoutmgr.inline.WrapperLayoutManager;
 
 /**
  * LayoutManager for an fo:flow object.
@@ -64,108 +63,151 @@ public class FlowLayoutManager extends BlockStackingLayoutManager
     /** {@inheritDoc} */
     public List getNextKnuthElements(LayoutContext context, int alignment) {
 
-        // set layout dimensions
-        int flowIPD = getCurrentPV().getCurrentSpan().getColumnWidth();
-        int flowBPD = getCurrentPV().getBodyRegion().getBPD();
+        List elements = new LinkedList();
 
-        // currently active LM
-        LayoutManager curLM;
-        List returnedList;
-        List returnList = new LinkedList();
-
-        while ((curLM = getChildLM()) != null) {
-            if (!(curLM instanceof WrapperLayoutManager)
-                && curLM instanceof InlineLevelLayoutManager) {
-                log.error("inline area not allowed under flow - ignoring");
-                curLM.setFinished(true);
-                continue;
+        LayoutManager currentChildLM;
+        while ((currentChildLM = getChildLM()) != null) {
+            if (addChildElements(elements, currentChildLM, context, alignment) != null) {
+                return elements;
             }
+        }
+
+        SpaceResolver.resolveElementList(elements);
+        setFinished(true);
+
+        assert !elements.isEmpty();
+        return elements;
+    }
+
+    /** {@inheritDoc} */
+    public List getNextKnuthElements(LayoutContext context, int alignment,
+            Position positionAtIPDChange, LayoutManager restartAtLM) {
+
+        List elements = new LinkedList();
 
-            int span = EN_NONE;
-            int disableColumnBalancing = EN_FALSE;
-            if (curLM instanceof BlockLayoutManager) {
-                span = ((BlockLayoutManager)curLM).getBlockFO().getSpan();
-                disableColumnBalancing = ((BlockLayoutManager) curLM).getBlockFO()
-                        .getDisableColumnBalancing();
-            } else if (curLM instanceof BlockContainerLayoutManager) {
-                span = ((BlockContainerLayoutManager)curLM).getBlockContainerFO().getSpan();
-                disableColumnBalancing = ((BlockContainerLayoutManager) curLM).getBlockContainerFO()
-                        .getDisableColumnBalancing();
+        LayoutManager currentChildLM = positionAtIPDChange.getLM();
+        if (currentChildLM == null) {
+            throw new IllegalStateException(
+                    "Cannot find layout manager from where to re-start layout after IPD change");
+        }
+        if (restartAtLM != null && restartAtLM.getParent() == this) {
+            currentChildLM = restartAtLM;
+            setCurrentChildLM(currentChildLM);
+            currentChildLM.reset();
+            if (addChildElements(elements, currentChildLM, context, alignment) != null) {
+                return elements;
+            }
+        } else {
+            Stack lmStack = new Stack();
+            while (currentChildLM.getParent() != this) {
+                lmStack.push(currentChildLM);
+                currentChildLM = currentChildLM.getParent();
             }
+            setCurrentChildLM(currentChildLM);
+            if (addChildElements(elements, currentChildLM, context, alignment, lmStack,
+                    positionAtIPDChange, restartAtLM) != null) {
+                return elements;
+            }
+        }
 
-            int currentSpan = context.getCurrentSpan();
-            if (currentSpan != span) {
-                if (span == EN_ALL) {
-                    context.setDisableColumnBalancing(disableColumnBalancing);
-                }
-                log.debug("span change from " + currentSpan + " to " + span);
-                context.signalSpanChange(span);
-                SpaceResolver.resolveElementList(returnList);
-                return returnList;
+        while ((currentChildLM = getChildLM()) != null) {
+            currentChildLM.reset(); // TODO won't work with forced breaks
+            if (addChildElements(elements, currentChildLM, context, alignment) != null) {
+                return elements;
             }
+        }
 
-            // Set up a LayoutContext
-            //MinOptMax bpd = context.getStackLimit();
+        SpaceResolver.resolveElementList(elements);
+        setFinished(true);
 
-            LayoutContext childLC = new LayoutContext(0);
-            childLC.setStackLimitBP(context.getStackLimitBP());
-            childLC.setRefIPD(context.getRefIPD());
-            childLC.setWritingMode(getCurrentPage().getSimplePageMaster().getWritingMode());
+        assert !elements.isEmpty();
+        return elements;
+    }
 
-            // get elements from curLM
-            returnedList = curLM.getNextKnuthElements(childLC, alignment);
-            //log.debug("FLM.getNextKnuthElements> returnedList.size() = " + returnedList.size());
-            if (returnList.size() == 0 && childLC.isKeepWithPreviousPending()) {
-                context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
-                childLC.clearKeepWithPreviousPending();
-            }
+    private List addChildElements(List elements, LayoutManager childLM, LayoutContext context,
+            int alignment) {
+        return addChildElements(elements, childLM, context, alignment, null, null, null);
+    }
 
-            // "wrap" the Position inside each element
-            List tempList = returnedList;
-            returnedList = new LinkedList();
-            wrapPositionElements(tempList, returnedList);
-
-            if (returnedList.size() == 1
-                && ElementListUtils.endsWithForcedBreak(returnedList)) {
-                // a descendant of this flow has break-before
-                returnList.addAll(returnedList);
-                SpaceResolver.resolveElementList(returnList);
-                return returnList;
-            } else if (returnedList.size() > 0) {
-                if (returnList.size() > 0
-                        && !ElementListUtils.startsWithForcedBreak(returnedList)) {
-                    addInBetweenBreak(returnList, context, childLC);
-                }
-                returnList.addAll(returnedList);
-                if (ElementListUtils.endsWithForcedBreak(returnList)) {
-                    if (curLM.isFinished() && !hasNextChildLM()) {
-                        //If the layout manager is finished at this point, the pending
-                        //marks become irrelevant.
-                        childLC.clearPendingMarks();
-                        //setFinished(true);
-                        break;
-                    }
-                    // a descendant of this flow has break-after
-                    SpaceResolver.resolveElementList(returnList);
-                    return returnList;
-                }
+    private List addChildElements(List elements, LayoutManager childLM, LayoutContext context,
+            int alignment, Stack lmStack, Position position, LayoutManager restartAtLM) {
+        if (handleSpanChange(childLM, elements, context)) {
+            SpaceResolver.resolveElementList(elements);
+            return elements;
+        }
+
+        LayoutContext childLC = new LayoutContext(0);
+        List childrenElements = getNextChildElements(childLM, context, childLC, alignment, lmStack,
+                position, restartAtLM);
+        if (elements.isEmpty()) {
+            context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending());
+        }
+        if (!elements.isEmpty()
+                && !ElementListUtils.startsWithForcedBreak(childrenElements)) {
+            addInBetweenBreak(elements, context, childLC);
+        }
+        context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
+
+        elements.addAll(childrenElements);
+
+        if (ElementListUtils.endsWithForcedBreak(elements)) {
+            // a descendant of this flow has break-before or break-after
+            if (childLM.isFinished() && !hasNextChildLM()) {
+                setFinished(true);
             }
+            SpaceResolver.resolveElementList(elements);
+            return elements;
+        }
+        return null;
+    }
 
-            //Propagate and clear
-            context.updateKeepWithNextPending(childLC.getKeepWithNextPending());
-            childLC.clearKeepWithNextPending();
+    private boolean handleSpanChange(LayoutManager childLM, List elements, LayoutContext context) {
+        int span = EN_NONE;
+        int disableColumnBalancing = EN_FALSE;
+        if (childLM instanceof BlockLayoutManager) {
+            span = ((BlockLayoutManager)childLM).getBlockFO().getSpan();
+            disableColumnBalancing = ((BlockLayoutManager) childLM).getBlockFO()
+                    .getDisableColumnBalancing();
+        } else if (childLM instanceof BlockContainerLayoutManager) {
+            span = ((BlockContainerLayoutManager)childLM).getBlockContainerFO().getSpan();
+            disableColumnBalancing = ((BlockContainerLayoutManager) childLM).getBlockContainerFO()
+                    .getDisableColumnBalancing();
+        }
 
-            context.updateKeepWithNextPending(getKeepWithNext());
+        int currentSpan = context.getCurrentSpan();
+        if (currentSpan != span) {
+            if (span == EN_ALL) {
+                context.setDisableColumnBalancing(disableColumnBalancing);
+            }
+            log.debug("span change from " + currentSpan + " to " + span);
+            context.signalSpanChange(span);
+            return true;
+        } else {
+            return false;
         }
+    }
 
-        SpaceResolver.resolveElementList(returnList);
-        setFinished(true);
+    private List getNextChildElements(LayoutManager childLM, LayoutContext context,
+            LayoutContext childLC, int alignment, Stack lmStack, Position restartPosition,
+            LayoutManager restartLM) {
+        childLC.setStackLimitBP(context.getStackLimitBP());
+        childLC.setRefIPD(context.getRefIPD());
+        childLC.setWritingMode(getCurrentPage().getSimplePageMaster().getWritingMode());
 
-        if (returnList.size() > 0) {
-            return returnList;
+        List childrenElements;
+        if (lmStack == null) {
+            childrenElements = childLM.getNextKnuthElements(childLC, alignment);
         } else {
-            return null;
+            childrenElements = childLM.getNextKnuthElements(childLC,
+                    alignment, lmStack, restartPosition, restartLM);
         }
+        assert !childrenElements.isEmpty();
+
+        // "wrap" the Position inside each element
+        List tempList = childrenElements;
+        childrenElements = new LinkedList();
+        wrapPositionElements(tempList, childrenElements);
+        return childrenElements;
     }
 
     /**
@@ -353,5 +395,10 @@ public class FlowLayoutManager extends BlockStackingLayoutManager
         return getCurrentPV().getBodyRegion().getBPD();
     }
 
+    /** {@inheritDoc} */
+    public boolean isRestartable() {
+        return true;
+    }
+
 }
 
index 6d11a3c247fe93dd1da16686b234dab348f98980..104c7113130a9c421a6e14326d68be83107742fe 100644 (file)
@@ -57,16 +57,12 @@ public class InlineKnuthSequence extends KnuthSequence  {
         return true;
     }
 
-    /* (non-Javadoc)
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     public boolean canAppendSequence(KnuthSequence sequence) {
         return sequence.isInlineSequence() && !isClosed;
     }
 
-    /* (non-Javadoc)
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     public boolean appendSequence(KnuthSequence sequence) {
         if (!canAppendSequence(sequence)) {
             return false;
@@ -83,18 +79,14 @@ public class InlineKnuthSequence extends KnuthSequence  {
         return true;
     }
 
-    /* (non-Javadoc)
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     public boolean appendSequence(KnuthSequence sequence, boolean keepTogether,
                                   BreakElement breakElement) {
         return appendSequence(sequence);
     }
 
 
-    /* (non-Javadoc)
-     * {@inheritDoc}
-     */
+    /** {@inheritDoc} */
     public KnuthSequence endSequence() {
         if (!isClosed) {
             add(new KnuthPenalty(0, -KnuthElement.INFINITE, false, null, false));
index 41d4c5cfcbeb9419059ff26e1374657eb1cb822a..81726e57bdf04cf6452ea8c38fc4310f06e7915a 100644 (file)
@@ -78,15 +78,6 @@ public class LayoutContext {
      * level LM to allow them to optimize returned break possibilities.
      */
     private MinOptMax stackLimitBP;
-    /**
-     * Total available stacking dimension for a "galley-level" layout
-     * manager in inline-progression-direction. It is passed by the
-     * parent LM. For LineLM, the block LM determines this based on
-     * indent properties.
-     * These LM <b>may</b> wish to pass this information down to lower
-     * level LM to allow them to optimize returned break possibilities.
-     */
-    private MinOptMax stackLimitIP;
 
     /** to keep track of spanning in multi-column layout */
     private int currentSpan = Constants.NOT_SET;
@@ -158,7 +149,7 @@ public class LayoutContext {
         this.flags = parentLC.flags;
         this.refIPD = parentLC.refIPD;
         this.writingMode = parentLC.writingMode;
-        setStackLimitsFrom(parentLC);
+        setStackLimitBP(parentLC.getStackLimitBP());
         this.leadingSpace = parentLC.leadingSpace; //???
         this.trailingSpace = parentLC.trailingSpace; //???
         this.hyphContext = parentLC.hyphContext;
@@ -183,7 +174,6 @@ public class LayoutContext {
         this.flags = flags;
         this.refIPD = 0;
         stackLimitBP = new MinOptMax(0);
-        stackLimitIP = new MinOptMax(0);
         leadingSpace = null;
         trailingSpace = null;
     }
@@ -397,31 +387,6 @@ public class LayoutContext {
         return stackLimitBP;
     }
 
-    /**
-     * Sets the stack limit in inline-progression-dimension.
-     * @param limit the stack limit
-     */
-    public void setStackLimitIP(MinOptMax limit) {
-        stackLimitIP = limit;
-    }
-
-    /**
-     * Returns the stack limit in inline-progression-dimension.
-     * @return the stack limit
-     */
-    public MinOptMax getStackLimitIP() {
-        return stackLimitIP;
-    }
-
-    /**
-     * Sets (Copies) the stack limits in both directions from another layout context.
-     * @param context the layout context to take the values from
-     */
-    public void setStackLimitsFrom(LayoutContext context) {
-        setStackLimitBP(context.getStackLimitBP());
-        setStackLimitIP(context.getStackLimitIP());
-    }
-
     /**
      * Sets the inline-progression-dimension of the nearest ancestor reference area.
      */
@@ -662,8 +627,6 @@ public class LayoutContext {
         return "Layout Context:"
         + "\nStack Limit BPD: \t"
             + (getStackLimitBP() == null ? "null" : getStackLimitBP().toString())
-        + "\nStack Limit IPD: \t"
-            + (getStackLimitIP() == null ? "null" : getStackLimitIP().toString())
         + "\nTrailing Space: \t"
             + (getTrailingSpace() == null ? "null" : getTrailingSpace().toString())
         + "\nLeading Space: \t"
index f19588a77e190b7593d39f6b460e8c35442206dd..454b8b3665dc6c21528af8bcab017dcf2477d210 100644 (file)
@@ -20,6 +20,7 @@
 package org.apache.fop.layoutmgr;
 
 import java.util.List;
+import java.util.Stack;
 
 import org.apache.fop.area.Area;
 import org.apache.fop.datatypes.PercentBaseContext;
@@ -219,4 +220,36 @@ public interface LayoutManager extends PercentBaseContext {
      * @return the same Position but with a position index
      */
     Position notifyPos(Position pos);
+
+    /**
+     * Re-initializes this layout manager in order to re-generate its Knuth
+     * elements according to a new IPD value.
+     */
+    void reset();
+
+    /**
+     * Returns {@code true} if this layout manager is able to re-generate its
+     * Knuth elements after an IPD change.
+     *
+     * @return {@code true} if this layout manager can be restarted after an IPD
+     * change
+     */
+    boolean isRestartable();
+
+    /**
+     * Returns an updated list of Knuth elements corresponding to this layout
+     * manager, after a change of IPD has been detected.
+     *
+     * @param context the layout context
+     * @param alignment the alignment
+     * @param lmStack the stack of LMs that are active at the IPD change
+     * @param positionAtIPDChange the position corresponding to the element
+     * finishing the page before the IPD change
+     * @param restartAtLM if not null, the layout manager from which to restart.
+     * That is, the IPD change occurs between two block elements and not inside
+     * a paragraph
+     * @return an updated list of elements, taking the new IPD into account
+     */
+    List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack,
+            Position positionAtIPDChange, LayoutManager restartAtLM);
 }
index ed8cc94e29721110477c51ab7cd8678beb904f45..8b2a5f4bc8e9ff727d620ef45af04497a909738a 100644 (file)
@@ -21,15 +21,20 @@ package org.apache.fop.layoutmgr;
 
 public class LeafPosition extends Position {
 
-    private int iLeafPos;
+    private int leafPos;
 
     public LeafPosition(LayoutManager lm, int pos) {
         super(lm);
-        iLeafPos = pos;
+        leafPos = pos;
+    }
+
+    public LeafPosition(LayoutManager layoutManager, int pos, int index) {
+        super(layoutManager, index);
+        leafPos = pos;
     }
 
     public int getLeafPos() {
-        return iLeafPos;
+        return leafPos;
     }
 
     public boolean generatesAreas() {
index ed0b37602e4efa431e239e502a4027640c55c70c..86c3fccf8778f18fc20a2887941d8cf7f0a32f82 100644 (file)
@@ -77,6 +77,14 @@ public class PageBreaker extends AbstractBreaker {
         return pslm.getPageProvider();
     }
 
+    /**
+     * Starts the page breaking process.
+     * @param flowBPD the constant available block-progression-dimension (used for every part)
+     */
+    void doLayout(int flowBPD) {
+        doLayout(flowBPD, false);
+    }
+
     /** {@inheritDoc} */
     protected PageBreakingLayoutListener createLayoutListener() {
         return new PageBreakingLayoutListener() {
@@ -121,6 +129,12 @@ public class PageBreaker extends AbstractBreaker {
     /** {@inheritDoc} */
     protected int getNextBlockList(LayoutContext childLC,
             int nextSequenceStartsOn) {
+        return getNextBlockList(childLC, nextSequenceStartsOn, null, null, null);
+    }
+
+    /** {@inheritDoc} */
+    protected int getNextBlockList(LayoutContext childLC, int nextSequenceStartsOn,
+            Position positionAtIPDChange, LayoutManager restartLM, List firstElements) {
         if (!firstPart) {
             // if this is the first page that will be created by
             // the current BlockSequence, it could have a break
@@ -132,7 +146,8 @@ public class PageBreaker extends AbstractBreaker {
         pageBreakHandled = true;
         pageProvider.setStartOfNextElementList(pslm.getCurrentPageNum(),
                 pslm.getCurrentPV().getCurrentSpan().getCurrentFlowIndex());
-        return super.getNextBlockList(childLC, nextSequenceStartsOn);
+        return super.getNextBlockList(childLC, nextSequenceStartsOn, positionAtIPDChange,
+                restartLM, firstElements);
     }
 
     private boolean containsFootnotes(List contentList, LayoutContext context) {
@@ -209,6 +224,24 @@ public class PageBreaker extends AbstractBreaker {
         return contentList;
     }
 
+    /** {@inheritDoc} */
+    protected List getNextKnuthElements(LayoutContext context, int alignment,
+            Position positionAtIPDChange, LayoutManager restartAtLM) {
+        List contentList = null;
+
+        do {
+            contentList = childFLM.getNextKnuthElements(context, alignment, positionAtIPDChange,
+                    restartAtLM);
+        } while (!childFLM.isFinished() && contentList == null);
+
+        // scan contentList, searching for footnotes
+        if (containsFootnotes(contentList, context)) {
+            // handle the footnote separator
+            handleFootnoteSeparator();
+        }
+        return contentList;
+    }
+
     /**
      * @return current display alignment
      */
index fc0226cecf4a8780db6a1fa517a2a7e08826058a..52238e9be20a9252346fbaa9c3bb4ce7dff60f7e 100644 (file)
@@ -96,6 +96,9 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
     //Controls whether a single part should be forced if possible (ex. block-container)
     private boolean favorSinglePart = false;
 
+    private boolean ipdChange;
+    private KnuthNode bestNodeForIPDChange;
+
     //Used to keep track of switches in keep-context
     private int currentKeepContext = Constants.EN_AUTO;
     private KnuthNode lastBeforeKeepContextSwitch;
@@ -1073,4 +1076,63 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
 
     }
 
+    /** {@inheritDoc} */
+    protected boolean ipdChanged() {
+        return ipdChange;
+    }
+
+    /** {@inheritDoc} */
+    protected int handleIpdChange() {
+        log.trace("Best node for ipd change:" + bestNodeForIPDChange);
+        // TODO finish()
+        /*
+         * The third parameter is used to determine if this is the last page, so
+         * if the content must be vertically justified or not. If we are here
+         * this means that there is further content and the next page has a
+         * different ipd. So tweak the parameter to fall into the non-last-page
+         * case.
+         */
+        calculateBreakPoints(bestNodeForIPDChange, par, bestNodeForIPDChange.line + 1);
+        activeLines = null;
+        return bestNodeForIPDChange.line;
+    }
+
+    /**
+     * Add a node at the end of the given line's existing active nodes.
+     * If this is the first node in the line, adjust endLine accordingly.
+     * @param line number of the line ending at the node's corresponding breakpoint
+     * @param node the active node to add
+     */
+    protected void addNode(int line, KnuthNode node) {
+        if (node.position < par.size() - 1 && line > 0 && ipdChange(line - 1)) {
+            log.trace("IPD changes at page " + line);
+            ipdChange = true;
+            if (bestNodeForIPDChange == null
+                    || node.totalDemerits < bestNodeForIPDChange.totalDemerits) {
+                bestNodeForIPDChange = node;
+            }
+        } else {
+            if (node.position == par.size() - 1) {
+                /*
+                 * The whole sequence could actually fit on the last page before
+                 * the IPD change. No need to do any special handling.
+                 */
+                ipdChange = false;
+            }
+            super.addNode(line, node);
+        }
+    }
+
+    KnuthNode getBestNodeBeforeIPDChange() {
+        return bestNodeForIPDChange;
+    }
+
+    /** {@inheritDoc} */
+    protected boolean ipdChange(int line) {
+        if (pageProvider == null) {
+            return false;
+        }
+        return pageProvider.ipdChange(line);
+    }
+
 }
index c03aea0912fcfa5934f020339a83d9e8a3223788..bd556366a687fdc42069331fb42641467c520739 100644 (file)
@@ -160,6 +160,33 @@ public class PageProvider implements Constants {
         return new int[] {colIndex, columnCount};
     }
 
+    /**
+     * Returns true if the part following the given one has a different IPD.
+     *
+     * @param index index of the current part
+     * @return true if the following part has a different IPD, false otherwise
+     */
+    public boolean ipdChange(int index) {
+        int columnCount = 0;
+        int colIndex = startColumnOfCurrentElementList + index;
+        int pageIndex = -1;
+        Page page;
+        do {
+            colIndex -= columnCount;
+            pageIndex++;
+            page = getPage(false, pageIndex, RELTO_CURRENT_ELEMENT_LIST);
+            columnCount = page.getPageViewport().getCurrentSpan().getColumnCount();
+        } while (colIndex >= columnCount);
+        if (colIndex + 1 < columnCount) {
+            // Next part is a column on same page => same IPD
+            return false;
+        } else {
+            Page nextPage = getPage(false, pageIndex + 1, RELTO_CURRENT_ELEMENT_LIST);
+            return page.getPageViewport().getBodyRegion().getIPD()
+                    != nextPage.getPageViewport().getBodyRegion().getIPD();
+        }
+    }
+
     /**
      * Checks if a break at the passed index would start a new page
      * @param index the index of the element before the break
index 42034cab4cf2e9d3d32e56a2a948681a869c1fd9..10ad5878e5773d79c1a6b7810b5a0fff5807ade6 100644 (file)
@@ -28,6 +28,11 @@ public class Position {
         layoutManager = lm;
     }
 
+   public Position(LayoutManager lm, int index) {
+        this(lm);
+        setIndex(index);
+    }
+
     public LayoutManager getLM() {
         return layoutManager;
     }
index 416ee924d16863eeac6e3164209a333bec696c3b..ff464ce6ad1c2618136faba8dc000b0bf333b639 100644 (file)
@@ -19,7 +19,6 @@
 
 package org.apache.fop.layoutmgr;
 
-import java.util.LinkedList;
 import java.util.List;
 import java.util.ListIterator;
 
@@ -565,6 +564,11 @@ public class SpaceResolver {
         public Position getOriginalBreakPosition() {
             return this.originalPosition;
         }
+
+        public Position getPosition() {
+            return originalPosition;
+        }
+
     }
 
     /**
index 4bfbd46f58d12f5d9d6f84a28700ce8bbaeb56cc..d5949f4a2bce6dd8a9fb88576c24da8409e40f1b 100644 (file)
@@ -21,10 +21,6 @@ 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.area.Area;
 import org.apache.fop.area.Block;
@@ -35,10 +31,7 @@ import org.apache.fop.fo.pagination.PageSequence;
 import org.apache.fop.fo.pagination.SideRegion;
 import org.apache.fop.fo.pagination.StaticContent;
 import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener;
-import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager;
 import org.apache.fop.layoutmgr.inline.TextLayoutManager;
-import org.apache.fop.traits.MinOptMax;
-import org.apache.fop.util.ListUtil;
 
 /**
  * LayoutManager for an fo:flow object.
@@ -48,11 +41,6 @@ import org.apache.fop.util.ListUtil;
  */
 public class StaticContentLayoutManager extends BlockStackingLayoutManager {
 
-    /**
-     * logging instance
-     */
-    private static Log log = LogFactory.getLog(StaticContentLayoutManager.class);
-
     private RegionReference targetRegion;
     private Block targetBlock;
     private SideRegion regionFO;
@@ -89,96 +77,7 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager {
 
     /** {@inheritDoc} */
     public List getNextKnuthElements(LayoutContext context, int alignment) {
-        if (true) {
-            throw new UnsupportedOperationException(
-                "Shouldn't this method be emptied because it's never called at all?");
-        }
-        //TODO Empty this method?!?
-        // set layout dimensions
-        setContentAreaIPD(context.getRefIPD());
-        setContentAreaBPD(context.getStackLimitBP().opt);
-
-        //TODO Copied from elsewhere. May be worthwhile to factor out the common parts.
-        // currently active LM
-        BlockLevelLayoutManager curLM;
-        BlockLevelLayoutManager prevLM = null;
-        MinOptMax stackSize = new MinOptMax();
-        List returnedList;
-        List returnList = new LinkedList();
-
-        while ((curLM = ((BlockLevelLayoutManager) getChildLM())) != null) {
-            if (curLM instanceof InlineLevelLayoutManager) {
-                log.error("inline area not allowed under flow - ignoring");
-                curLM.setFinished(true);
-                continue;
-            }
-
-            // Set up a LayoutContext
-            MinOptMax bpd = context.getStackLimitBP();
-
-            LayoutContext childLC = new LayoutContext(0);
-            childLC.setStackLimitBP(MinOptMax.subtract(bpd, stackSize));
-            childLC.setRefIPD(context.getRefIPD());
-
-            // get elements from curLM
-            returnedList = curLM.getNextKnuthElements(childLC, alignment);
-            //log.debug("FLM.getNextKnuthElements> returnedList.size() = "
-            //    + returnedList.size());
-
-            // "wrap" the Position inside each element
-            List 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.get(0)).isPenalty()
-                && ((KnuthPenalty)returnedList.get(0)).getP() == -KnuthElement.INFINITE) {
-                // a descendant of this flow has break-before
-                returnList.addAll(returnedList);
-                return returnList;
-            } else {
-                if (!returnList.isEmpty()) {
-                    // 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) ListUtil.getLast(returnList))
-                            .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.isEmpty()) { // controllare!
-                    returnList.addAll(returnedList);
-                    final KnuthElement last = (KnuthElement) ListUtil
-                            .getLast(returnedList);
-                    if (last.isPenalty()
-                            && ((KnuthPenalty) last).getP() == -KnuthElement.INFINITE) {
-                        // a descendant of this flow has break-after
-/*LF*/                  //log.debug("FLM - break after!!");
-                        return returnList;
-                    }
-/*LF*/          }
-            }
-            prevLM = curLM;
-        }
-
-        setFinished(true);
-
-        if (returnList.isEmpty()) {
-            return null;
-        } else {
-            return returnList;
-        }
+        throw new IllegalStateException();
     }
 
     /**
index a5247d65211d5b3e6b3eb23ec25c0efc67a28729..95f798161ae83ec97c870fba8436212bc4d6db38 100644 (file)
@@ -27,6 +27,7 @@ import java.util.ListIterator;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+
 import org.apache.fop.area.Area;
 import org.apache.fop.area.Block;
 import org.apache.fop.area.LineArea;
@@ -43,7 +44,6 @@ import org.apache.fop.layoutmgr.PageSequenceLayoutManager;
 import org.apache.fop.layoutmgr.Position;
 import org.apache.fop.layoutmgr.PositionIterator;
 import org.apache.fop.layoutmgr.SpaceSpecifier;
-import org.apache.fop.traits.MinOptMax;
 
 /**
  * Content Layout Manager.
@@ -111,8 +111,6 @@ public class ContentLayoutManager extends AbstractBaseLayoutManager
         LayoutContext childLC = new LayoutContext(LayoutContext.NEW_AREA);
         childLC.setLeadingSpace(new SpaceSpecifier(false));
         childLC.setTrailingSpace(new SpaceSpecifier(false));
-        // set stackLimit for lines
-        childLC.setStackLimitIP(new MinOptMax(ipd));
         childLC.setRefIPD(ipd);
 
         int lineHeight = 14000;
@@ -129,8 +127,7 @@ public class ContentLayoutManager extends AbstractBaseLayoutManager
 
         stackSize = 0;
 
-        List contentList =
-            getNextKnuthElements(childLC, Constants.EN_START);
+        List contentList = getNextKnuthElements(childLC, Constants.EN_START);
         ListIterator contentIter = contentList.listIterator();
         while (contentIter.hasNext()) {
             KnuthElement element = (KnuthElement) contentIter.next();
@@ -149,8 +146,7 @@ public class ContentLayoutManager extends AbstractBaseLayoutManager
         lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
         lc.setLeadingSpace(new SpaceSpecifier(false));
         lc.setTrailingSpace(new SpaceSpecifier(false));
-        KnuthPossPosIter contentPosIter =
-            new KnuthPossPosIter(contentList, 0, contentList.size());
+        KnuthPossPosIter contentPosIter = new KnuthPossPosIter(contentList, 0, contentList.size());
         curLM.addAreas(contentPosIter, lc);
     }
 
index 0c332281faaaa9208cab646120dce59a6be3104c..6e0c34a8266a6e2a45d19d338334c7c296b37e44 100755 (executable)
@@ -248,8 +248,6 @@ public class InlineLayoutManager extends InlineStackingLayoutManager {
         List returnList = new LinkedList();
         KnuthSequence lastSequence = null;
 
-        SpaceSpecifier leadingSpace = context.getLeadingSpace();
-
         if (fobj instanceof Title) {
             alignmentContext = new AlignmentContext(font,
                                     lineHeight.getOptimum(this).getLength().getValue(this),
@@ -274,14 +272,6 @@ public class InlineLayoutManager extends InlineStackingLayoutManager {
             if (getSpaceStart() != null) {
                 context.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart(), this));
             }
-
-            // Check for "fence"
-            if (hasLeadingFence(!context.isFirstArea())) {
-                // Reset leading space sequence for child areas
-                leadingSpace = new SpaceSpecifier(false);
-            }
-            // Reset state variables
-            clearPrevIPD(); // Clear stored prev content dimensions
         }
 
         StringBuffer trace = new StringBuffer("InlineLM:");
index 963b98b376cdb27c96c911e415304d47d30ae463..65e59554fe72bcbb0906af12980e609175963616 100644 (file)
 
 package org.apache.fop.layoutmgr.inline;
 
-import java.util.LinkedList;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.ListIterator;
-import java.util.HashMap;
 
+import org.apache.fop.area.Area;
+import org.apache.fop.area.inline.Space;
 import org.apache.fop.fo.FObj;
 import org.apache.fop.fo.properties.SpaceProperty;
 import org.apache.fop.layoutmgr.AbstractLayoutManager;
@@ -34,8 +35,6 @@ import org.apache.fop.layoutmgr.LayoutManager;
 import org.apache.fop.layoutmgr.NonLeafPosition;
 import org.apache.fop.layoutmgr.Position;
 import org.apache.fop.layoutmgr.PositionIterator;
-import org.apache.fop.area.Area;
-import org.apache.fop.area.inline.Space;
 import org.apache.fop.traits.MinOptMax;
 
 /**
@@ -62,12 +61,6 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager
         }
     }
 
-
-    /**
-     * Size of any start or end borders and padding.
-     */
-    private MinOptMax allocIPD = new MinOptMax(0);
-
     /**
      * Size of border and padding in BPD (ie, before and after).
      */
@@ -78,9 +71,6 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager
     /** The child layout context */
     protected LayoutContext childLC;
 
-    /** Used to store previous content IPD for each child LM. */
-    private HashMap hmPrevIPD = new HashMap();
-
     /**
      * Create an inline stacking layout manager.
      * This is used for fo's that create areas that
@@ -148,22 +138,6 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager
         return null;
     }
 
-    /**
-     * TODO: Explain this method
-     * @param lm ???
-     * @return ???
-     */
-    protected MinOptMax getPrevIPD(LayoutManager lm) {
-        return (MinOptMax) hmPrevIPD.get(lm);
-    }
-
-    /**
-     * Clear the previous IPD calculation.
-     */
-    protected void clearPrevIPD() {
-        hmPrevIPD.clear();
-    }
-
     /**
      * Returns the current area.
      * @return the current area
index aea851f5408d73e0e67f44c797a66ac9410be0c7..7c30ab9bbaee473dd7f4e9eeeb846bbef988967b 100644 (file)
@@ -20,6 +20,7 @@
 package org.apache.fop.layoutmgr.inline;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.ListIterator;
@@ -116,8 +117,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager
      * inline break positions.
      */
     private static class LineBreakPosition extends LeafPosition {
-        private int iParIndex; // index of the Paragraph this Position refers to
-        private int iStartIndex; //index of the first element this Position refers to
+        private int parIndex; // index of the Paragraph this Position refers to
+        private int startIndex; //index of the first element this Position refers to
         private int availableShrink;
         private int availableStretch;
         private int difference;
@@ -130,16 +131,16 @@ public class LineLayoutManager extends InlineStackingLayoutManager
         private int spaceAfter;
         private int baseline;
 
-        LineBreakPosition(LayoutManager lm, int index, int iStartIndex, int iBreakIndex,
+        LineBreakPosition(LayoutManager lm, int index, int startIndex, int breakIndex,
                           int shrink, int stretch, int diff,
                           double ipdA, double adjust, int ind,
                           int lh, int lw, int sb, int sa, int bl) {
-            super(lm, iBreakIndex);
+            super(lm, breakIndex);
             availableShrink = shrink;
             availableStretch = stretch;
             difference = diff;
-            iParIndex = index;
-            this.iStartIndex = iStartIndex;
+            parIndex = index;
+            this.startIndex = startIndex;
             ipdAdjust = ipdA;
             dAdjust = adjust;
             startIndent = ind;
@@ -167,18 +168,18 @@ public class LineLayoutManager extends InlineStackingLayoutManager
     private Length lineHeight;
     private int lead;
     private int follow;
-    private AlignmentContext alignmentContext = null;
+    private AlignmentContext alignmentContext;
 
-    private List knuthParagraphs = null;
-    private int iReturnedLBP = 0;
-
-    //     parameters of Knuth's algorithm:
-    // penalty value for flagged penalties
-    private int flaggedPenalty = 50;
+    private List knuthParagraphs;
 
     private LineLayoutPossibilities lineLayouts;
     private List lineLayoutsList;
-    private int iLineWidth = 0;
+    private int ipd = 0;
+    /**
+     * When layout must be re-started due to a change of IPD, there is no need
+     * to perform hyphenation on the remaining Knuth sequence once again.
+     */
+    private boolean hyphenationPerformed;
 
     /**
      * this constant is used to create elements when text-align is center:
@@ -238,7 +239,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
             } else {
                 lineFiller = new MinOptMax(lastLineEndIndent,
                                             lastLineEndIndent,
-                                            layoutManager.iLineWidth);
+                                            layoutManager.ipd);
             }
 
             // add auxiliary elements at the beginning of the paragraph
@@ -319,11 +320,9 @@ public class LineLayoutManager extends InlineStackingLayoutManager
         private int activePossibility;
         private int addedPositions;
         private int textIndent;
-        private int fillerMinWidth;
         private int lineHeight;
         private int lead;
         private int follow;
-        private int maxDiff;
         private static final double MAX_DEMERITS = 10e6;
 
         public LineBreakingAlgorithm (int pageAlign,
@@ -334,22 +333,17 @@ public class LineLayoutManager extends InlineStackingLayoutManager
             super(textAlign, textAlignLast, first, false, maxFlagCount);
             pageAlignment = pageAlign;
             textIndent = indent;
-            fillerMinWidth = fillerWidth;
             lineHeight = lh;
             lead = ld;
             follow = fl;
             thisLLM = llm;
             activePossibility = -1;
-            maxDiff = fobj.getWidows() >= fobj.getOrphans()
-                    ? fobj.getWidows()
-                    : fobj.getOrphans();
         }
 
         public void updateData1(int lineCount, double demerits) {
             lineLayouts.addPossibility(lineCount, demerits);
-            if (super.log.isTraceEnabled()) {
-                super.log.trace(
-                        "Layout possibility in " + lineCount + " lines; break at position:");
+            if (log.isTraceEnabled()) {
+                log.trace("Layout possibility in " + lineCount + " lines; break at position:");
             }
         }
 
@@ -430,7 +424,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
             // true if this line contains only zero-height, auxiliary boxes
             // and the actual line width is 0; in this case, the line "collapses"
             // i.e. the line area will have bpd = 0
-            boolean bZeroHeightLine = (difference == iLineWidth);
+            boolean bZeroHeightLine = (difference == ipd);
 
             // if line-stacking-strategy is "font-height", the line height
             // is not affected by its content
@@ -486,7 +480,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
                                              firstElementIndex, lastElementIndex,
                                              availableShrink, availableStretch,
                                              difference, ratio, 0, indent,
-                                             0, iLineWidth, 0, 0, 0);
+                                             0, ipd, 0, 0, 0);
             } else {
                 return new LineBreakPosition(thisLLM,
                                              knuthParagraphs.indexOf(par),
@@ -494,18 +488,11 @@ public class LineLayoutManager extends InlineStackingLayoutManager
                                              availableShrink, availableStretch,
                                              difference, ratio, 0, indent,
                                              lineLead + lineFollow,
-                                             iLineWidth, spaceBefore, spaceAfter,
+                                             ipd, spaceBefore, spaceAfter,
                                              lineLead);
             }
         }
 
-        public int findBreakingPoints(Paragraph par, /*int lineWidth,*/
-                                      double threshold, boolean force,
-                                      int allowedBreaks) {
-            return super.findBreakingPoints(par, /*lineWidth,*/
-                    threshold, force, allowedBreaks);
-        }
-
         protected int filterActiveNodes() {
             KnuthNode bestActiveNode = null;
 
@@ -578,13 +565,10 @@ public class LineLayoutManager extends InlineStackingLayoutManager
         FontInfo fi = fobj.getFOEventHandler().getFontInfo();
         FontTriplet[] fontkeys = fobj.getCommonFont().getFontState(fi);
         Font fs = fi.getFontInstance(fontkeys[0], fobj.getCommonFont().fontSize.getValue(this));
-        alignmentContext
-          = new AlignmentContext(fs, lineHeight.getValue(this), context.getWritingMode());
+        alignmentContext = new AlignmentContext(fs, lineHeight.getValue(this),
+                context.getWritingMode());
         context.setAlignmentContext(alignmentContext);
-        // Get a break from currently active child LM
-        // Set up constraints for inline level managers
-
-        clearPrevIPD();
+        ipd = context.getRefIPD();
 
         //PHASE 1: Create Knuth elements
         if (knuthParagraphs == null) {
@@ -606,34 +590,33 @@ public class LineLayoutManager extends InlineStackingLayoutManager
 
         //PHASE 2: Create line breaks
         return createLineBreaks(context.getBPAlignment(), context);
-        /*
-        LineBreakPosition lbp = null;
-        if (breakpoints == null) {
-            // find the optimal line breaking points for each paragraph
-            breakpoints = new ArrayList();
-            ListIterator paragraphsIterator
-                = knuthParagraphs.listIterator(knuthParagraphs.size());
-            Paragraph currPar = null;
-            while (paragraphsIterator.hasPrevious()) {
-                currPar = (Paragraph) paragraphsIterator.previous();
-                findBreakingPoints(currPar, context.getStackLimit().opt);
-            }
-        }*/
+    }
 
-        //PHASE 3: Return lines
+    public List getNextKnuthElements(LayoutContext context, int alignment,
+            LeafPosition restartPosition) {
+        log.trace("Restarting line breaking from index " + restartPosition.getIndex());
+        int parIndex = restartPosition.getLeafPos();
+        Paragraph paragraph = (Paragraph) knuthParagraphs.get(parIndex);
+        for (int i = 0; i <= restartPosition.getIndex(); i++) {
+            paragraph.remove(0);
+        }
+        Iterator iter = paragraph.iterator();
+        while (iter.hasNext() && !((KnuthElement) iter.next()).isBox()) {
+            iter.remove();
+        }
+        if (!iter.hasNext()) {
+            knuthParagraphs.remove(parIndex);
+        }
 
-        /*
-        // get a break point from the list
-        lbp = (LineBreakPosition) breakpoints.get(iReturnedLBP ++);
-        if (iReturnedLBP == breakpoints.size()) {
+        // return finished when there's no content
+        if (knuthParagraphs.size() == 0) {
             setFinished(true);
+            return null;
         }
 
-        BreakPoss curLineBP = new BreakPoss(lbp);
-        curLineBP.setFlag(BreakPoss.ISLAST, isFinished());
-        curLineBP.setStackingSize(new MinOptMax(lbp.lineHeight));
-        return curLineBP;
-        */
+        ipd = context.getRefIPD();
+        //PHASE 2: Create line breaks
+        return createLineBreaks(context.getBPAlignment(), context);
     }
 
     /**
@@ -643,22 +626,18 @@ public class LineLayoutManager extends InlineStackingLayoutManager
     private void collectInlineKnuthElements(LayoutContext context) {
         LayoutContext inlineLC = new LayoutContext(context);
 
-        InlineLevelLayoutManager curLM;
-        List returnedList = null;
-        iLineWidth = context.getStackLimitIP().opt;
-
         // convert all the text in a sequence of paragraphs made
         // of KnuthBox, KnuthGlue and KnuthPenalty objects
-        boolean bPrevWasKnuthBox = false;
+        boolean previousIsBox = false;
 
         StringBuffer trace = new StringBuffer("LineLM:");
 
         Paragraph lastPar = null;
 
+        InlineLevelLayoutManager curLM;
         while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) {
-            returnedList = curLM.getNextKnuthElements(inlineLC, effectiveAlignment);
-            if (returnedList == null
-                    || returnedList.size() == 0) {
+            List inlineElements = curLM.getNextKnuthElements(inlineLC, effectiveAlignment);
+            if (inlineElements == null || inlineElements.size() == 0) {
                 /* curLM.getNextKnuthElements() returned null or an empty list;
                  * this can happen if there is nothing more to layout,
                  * so just iterate once more to see if there are other children */
@@ -666,7 +645,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
             }
 
             if (lastPar != null) {
-                KnuthSequence firstSeq = (KnuthSequence) returnedList.get(0);
+                KnuthSequence firstSeq = (KnuthSequence) inlineElements.get(0);
 
                 // finish last paragraph before a new block sequence
                 if (!firstSeq.isInlineSequence()) {
@@ -676,7 +655,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
                     if (log.isTraceEnabled()) {
                         trace.append(" ]");
                     }
-                    bPrevWasKnuthBox = false;
+                    previousIsBox = false;
                 }
 
                 // does the first element of the first paragraph add to an existing word?
@@ -684,27 +663,24 @@ public class LineLayoutManager extends InlineStackingLayoutManager
                     KnuthElement thisElement;
                     thisElement = (KnuthElement) firstSeq.get(0);
                     if (thisElement.isBox() && !thisElement.isAuxiliary()
-                            && bPrevWasKnuthBox) {
+                            && previousIsBox) {
                         lastPar.addALetterSpace();
                     }
                 }
             }
 
             // loop over the KnuthSequences (and single KnuthElements) in returnedList
-            ListIterator iter = returnedList.listIterator();
+            ListIterator iter = inlineElements.listIterator();
             while (iter.hasNext()) {
                 KnuthSequence sequence = (KnuthSequence) iter.next();
                 // the sequence contains inline Knuth elements
                 if (sequence.isInlineSequence()) {
                     // look at the last element
                     ListElement lastElement = sequence.getLast();
-                    if (lastElement == null) {
-                        throw new NullPointerException(
-                        "Sequence was empty! lastElement is null");
-                    }
-                    bPrevWasKnuthBox = lastElement.isBox()
-                                        && !((KnuthElement) lastElement).isAuxiliary()
-                                        && ((KnuthElement) lastElement).getW() != 0;
+                    assert lastElement != null;
+                    previousIsBox = lastElement.isBox()
+                            && !((KnuthElement) lastElement).isAuxiliary()
+                            && ((KnuthElement) lastElement).getW() != 0;
 
                     // if last paragraph is open, add the new elements to the paragraph
                     // else this is the last paragraph
@@ -729,8 +705,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
 
                     // finish last paragraph if it was closed with a linefeed
                     if (lastElement.isPenalty()
-                            && ((KnuthPenalty) lastElement).getP()
-                            == -KnuthPenalty.INFINITE) {
+                            && ((KnuthPenalty) lastElement).getP() == -KnuthPenalty.INFINITE) {
                         // a penalty item whose value is -inf
                         // represents a preserved linefeed,
                         // which forces a line break
@@ -738,7 +713,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
                         if (!lastPar.containsBox()) {
                             //only a forced linefeed on this line
                             //-> compensate with an auxiliary glue
-                            lastPar.add(new KnuthGlue(iLineWidth, 0, iLineWidth, null, true));
+                            lastPar.add(new KnuthGlue(ipd, 0, ipd, null, true));
                         }
                         lastPar.endParagraph();
                         ElementListObserver.observe(lastPar, "line", null);
@@ -746,7 +721,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
                         if (log.isTraceEnabled()) {
                             trace.append(" ]");
                         }
-                        bPrevWasKnuthBox = false;
+                        previousIsBox = false;
                     }
                 } else { // the sequence is a block sequence
                     // the positions will be wrapped with this LM in postProcessLineBreaks
@@ -767,134 +742,6 @@ public class LineLayoutManager extends InlineStackingLayoutManager
         log.trace(trace);
     }
 
-    /**
-     * Find a set of breaking points.
-     * This method is called only once by getNextBreakPoss, and it
-     * subsequently calls the other findBreakingPoints() method with
-     * different parameters, until a set of breaking points is found.
-     *
-     * @param par       the list of elements that must be parted
-     *                  into lines
-     * @param lineWidth the desired length ot the lines
-     */
-    /*
-    private void findBreakingPoints(Paragraph par, int lineWidth) {
-        // maximum adjustment ratio permitted
-        float maxAdjustment = 1;
-
-        // first try
-        if (!findBreakingPoints(par, lineWidth, maxAdjustment, false)) {
-            // the first try failed, now try something different
-            log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment);
-            if (hyphenationProperties.hyphenate == Constants.EN_TRUE) {
-                // consider every hyphenation point as a legal break
-                findHyphenationPoints(par);
-            } else {
-                // try with a higher threshold
-                maxAdjustment = 5;
-            }
-
-            if (!findBreakingPoints(par, lineWidth, maxAdjustment, false)) {
-                // the second try failed too, try with a huge threshold;
-                // if this fails too, use a different algorithm
-                log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment
-                          + (hyphenationProperties.hyphenate == Constants.EN_TRUE ? " and hyphenation" : ""));
-                maxAdjustment = 20;
-                if (!findBreakingPoints(par, lineWidth, maxAdjustment, true)) {
-                    log.debug("No set of breaking points found, using first-fit algorithm");
-                }
-            }
-        }
-    }
-
-    private boolean findBreakingPoints(Paragraph par, int lineWidth,
-            double threshold, boolean force) {
-        KnuthParagraph knuthPara = new KnuthParagraph(par);
-        int lines = knuthPara.findBreakPoints(lineWidth, threshold, force);
-        if (lines == 0) {
-            return false;
-        }
-
-        for (int i = lines-1; i >= 0; i--) {
-            int line = i+1;
-            if (log.isTraceEnabled()) {
-                log.trace("Making line from " + knuthPara.getStart(i) + " to " +
-                           knuthPara.getEnd(i));
-            }
-            // compute indent and adjustment ratio, according to
-            // the value of text-align and text-align-last
-
-            int difference = knuthPara.getDifference(i);
-            if (line == lines) {
-                difference += par.lineFillerWidth;
-            }
-            int textAlign = (line < lines)
-                ? textAlignment : textAlignmentLast;
-            int indent = (textAlign == EN_CENTER)
-                ? difference / 2
-                : (textAlign == EN_END) ? difference : 0;
-            indent += (line == 1 && knuthParagraphs.indexOf(par) == 0)
-                ? textIndent.getValue(this) : 0;
-            double ratio = (textAlign == EN_JUSTIFY)
-                ? knuthPara.getAdjustRatio(i) : 0;
-
-            int start = knuthPara.getStart(i);
-            int end = knuthPara.getEnd(i);
-            makeLineBreakPosition(par, start, end, 0, ratio, indent);
-        }
-        return true;
-    }
-
-    private void makeLineBreakPosition(Paragraph par,
-                                       int firstElementIndex, int lastElementIndex,
-                                       int insertIndex, double ratio, int indent) {
-        // line height calculation
-
-        int halfLeading = (lineHeight - lead - follow) / 2;
-        // height above the main baseline
-        int lineLead = lead + halfLeading;
-        // maximum size of top and bottom alignment
-        int lineFollow = follow + halfLeading;
-
-        ListIterator inlineIterator
-            = par.listIterator(firstElementIndex);
-        for (int j = firstElementIndex;
-             j <= lastElementIndex;
-             j++) {
-            KnuthElement element = (KnuthElement) inlineIterator.next();
-            if (element.isBox()) {
-                KnuthInlineBox box = (KnuthInlineBox)element;
-                if (box.getLead() > lineLead) {
-                    lineLead = box.getLead();
-                }
-                if (box.getTotal() > lineFollow) {
-                    lineFollow = box.getTotal();
-                }
-                if (box.getMiddle() > lineLead + middleShift) {
-                    lineLead += box.getMiddle()
-                                - lineLead - middleShift;
-                }
-                if (box.getMiddle() > middlefollow - middleShift) {
-                    middlefollow += box.getMiddle()
-                                    - middlefollow + middleShift;
-                }
-            }
-        }
-
-        if (lineFollow - lineLead > middlefollow) {
-                    middlefollow = lineFollow - lineLead;
-        }
-
-        breakpoints.add(insertIndex,
-                        new LineBreakPosition(this,
-                                              knuthParagraphs.indexOf(par),
-                                              lastElementIndex ,
-                                              ratio, 0, indent,
-                                              lineLead + middlefollow,
-                                              lineLead));
-    }*/
-
-
     /**
      * Phase 2 of Knuth algorithm: find optimal break points.
      * @param alignment alignment in BP direction of the paragraph
@@ -902,10 +749,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager
      * @return a list of Knuth elements representing broken lines
      */
     private List createLineBreaks(int alignment, LayoutContext context) {
-
         // find the optimal line breaking points for each paragraph
-        ListIterator paragraphsIterator
-            = knuthParagraphs.listIterator(knuthParagraphs.size());
+        ListIterator paragraphsIterator = knuthParagraphs.listIterator(knuthParagraphs.size());
         lineLayoutsList = new ArrayList(knuthParagraphs.size());
         LineLayoutPossibilities llPoss;
         while (paragraphsIterator.hasPrevious()) {
@@ -947,7 +792,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager
                                         this);
 
         if (hyphenationProperties.hyphenate.getEnum() == EN_TRUE
-                && fobj.getWrapOption() != EN_NO_WRAP) {
+                && fobj.getWrapOption() != EN_NO_WRAP && !hyphenationPerformed) {
+            hyphenationPerformed = true;
             findHyphenationPoints(currPar);
         }
 
@@ -958,7 +804,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
         } else {
             allowedBreaks = BreakingAlgorithm.NO_FLAGGED_PENALTIES;
         }
-        alg.setConstantLineWidth(iLineWidth);
+        alg.setConstantLineWidth(ipd);
         iBPcount = alg.findBreakingPoints(currPar,
                                           maxAdjustment, false, allowedBreaks);
         if (iBPcount == 0 || alignment == EN_JUSTIFY) {
@@ -1014,26 +860,26 @@ public class LineLayoutManager extends InlineStackingLayoutManager
                     alg.resetAlgorithm();
                     lineLayouts.savePossibilities(true);
                     // try with shorter lines
-                    int savedLineWidth = iLineWidth;
-                    iLineWidth = (int) (iLineWidth * 0.95);
+                    int savedLineWidth = ipd;
+                    ipd = (int) (ipd * 0.95);
                     iBPcount = alg.findBreakingPoints(currPar,
-                             maxAdjustment, true, allowedBreaks);
+                            maxAdjustment, true, allowedBreaks);
                     // use normal lines, when possible
                     lineLayouts.restorePossibilities();
-                    iLineWidth = savedLineWidth;
+                    ipd = savedLineWidth;
                 }
                 if (!lineLayouts.canUseLessLines()) {
                     alg.resetAlgorithm();
                     lineLayouts.savePossibilities(true);
                     // try with longer lines
-                    int savedLineWidth = iLineWidth;
-                    iLineWidth = (int) (iLineWidth * 1.05);
-                    alg.setConstantLineWidth(iLineWidth);
+                    int savedLineWidth = ipd;
+                    ipd = (int) (ipd * 1.05);
+                    alg.setConstantLineWidth(ipd);
                     iBPcount = alg.findBreakingPoints(currPar,
                             maxAdjustment, true, allowedBreaks);
                     // use normal lines, when possible
                     lineLayouts.restorePossibilities();
-                    iLineWidth = savedLineWidth;
+                    ipd = savedLineWidth;
                 }
                 //log.debug("LLM.getNextKnuthElements> now, layouts with more lines? " + lineLayouts.canUseMoreLines());
                 //log.debug("                          now, layouts with fewer lines? " + lineLayouts.canUseLessLines());
@@ -1052,6 +898,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
 
         List returnList = new LinkedList();
 
+        int endIndex = -1;
         for (int p = 0; p < knuthParagraphs.size(); p++) {
             // penalty between paragraphs
             if (p > 0) {
@@ -1089,7 +936,6 @@ public class LineLayoutManager extends InlineStackingLayoutManager
             } else {
                 /* "normal" vertical alignment: create a sequence whose boxes
                    represent effective lines, and contain LineBreakPositions */
-                Position returnPosition = new LeafPosition(this, p);
                 int startIndex = 0;
                 for (int i = 0;
                         i < llPoss.getChosenLineCount();
@@ -1101,13 +947,12 @@ public class LineLayoutManager extends InlineStackingLayoutManager
                         // penalty allowing a page break between lines
                         Keep keep = getKeepTogether();
                         returnList.add(new BreakElement(
-                                    new Position(this),
+                                new LeafPosition(this, p, endIndex),
                                     keep.getPenalty(),
                                     keep.getContext(),
                                     context));
                     }
-                    int endIndex
-                      = ((LineBreakPosition) llPoss.getChosenPosition(i)).getLeafPos();
+                    endIndex = ((LineBreakPosition) llPoss.getChosenPosition(i)).getLeafPos();
                     // create a list of the FootnoteBodyLM handling footnotes
                     // whose citations are in this line
                     List footnoteList = new LinkedList();
@@ -1115,15 +960,14 @@ public class LineLayoutManager extends InlineStackingLayoutManager
                     while (elementIterator.nextIndex() <= endIndex) {
                         KnuthElement element = (KnuthElement) elementIterator.next();
                         if (element instanceof KnuthInlineBox
-                            && ((KnuthInlineBox) element).isAnchor()) {
+                                && ((KnuthInlineBox) element).isAnchor()) {
                             footnoteList.add(((KnuthInlineBox) element).getFootnoteBodyLM());
                         } else if (element instanceof KnuthBlockBox) {
                             footnoteList.addAll(((KnuthBlockBox) element).getFootnoteBodyLMs());
                         }
                     }
                     startIndex = endIndex + 1;
-                    LineBreakPosition lbp
-                      = (LineBreakPosition) llPoss.getChosenPosition(i);
+                    LineBreakPosition lbp = (LineBreakPosition) llPoss.getChosenPosition(i);
                     returnList.add(new KnuthBlockBox
                                    (lbp.lineHeight + lbp.spaceBefore + lbp.spaceAfter,
                                     footnoteList, lbp, false));
@@ -1597,7 +1441,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
             Position pos = (Position) parentIter.next();
             boolean isLastPosition = !parentIter.hasNext();
             if (pos instanceof LineBreakPosition) {
-                addInlineArea(context, pos, isLastPosition);
+                addInlineArea(context, (LineBreakPosition) pos, isLastPosition);
             } else if ((pos instanceof NonLeafPosition) && pos.generatesAreas()) {
                 addBlockArea(context, pos, isLastPosition);
             } else {
@@ -1617,147 +1461,129 @@ public class LineLayoutManager extends InlineStackingLayoutManager
      * @param pos the position for which the line is generated
      * @param isLastPosition true if this is the last position of this LM
      */
-    private void addInlineArea(LayoutContext context, Position pos, boolean isLastPosition) {
-            ListIterator seqIterator = null;
-            KnuthElement tempElement = null;
-            // the TLM which created the last KnuthElement in this line
-            LayoutManager lastLM = null;
-
-            LineBreakPosition lbp = (LineBreakPosition) pos;
-            int iCurrParIndex;
-            iCurrParIndex = lbp.iParIndex;
-            KnuthSequence seq = (KnuthSequence) knuthParagraphs.get(iCurrParIndex);
-            int iStartElement = lbp.iStartIndex;
-            int iEndElement = lbp.getLeafPos();
-
-            LineArea lineArea
-              = new LineArea((lbp.getLeafPos() < seq.size() - 1
-                              ? textAlignment : textAlignmentLast),
-                              lbp.difference, lbp.availableStretch, lbp.availableShrink);
-            if (lbp.startIndent != 0) {
-                lineArea.addTrait(Trait.START_INDENT, new Integer(lbp.startIndent));
-            }
-            lineArea.setBPD(lbp.lineHeight);
-            lineArea.setIPD(lbp.lineWidth);
-            lineArea.addTrait(Trait.SPACE_BEFORE, new Integer(lbp.spaceBefore));
-            lineArea.addTrait(Trait.SPACE_AFTER, new Integer(lbp.spaceAfter));
-            alignmentContext.resizeLine(lbp.lineHeight, lbp.baseline);
-
-            if (seq instanceof Paragraph) {
-                Paragraph currPar = (Paragraph) seq;
-                // ignore the first elements added by the LineLayoutManager
-                iStartElement += (iStartElement == 0) ? currPar.ignoreAtStart : 0;
-
-                // if this is the last line area that for this paragraph,
-                // ignore the last elements added by the LineLayoutManager and
-                // subtract the last-line-end-indent from the area ipd
-                if (iEndElement == (currPar.size() - 1)) {
-                    iEndElement -= currPar.ignoreAtEnd;
-                    lineArea.setIPD(lineArea.getIPD() - lastLineEndIndent.getValue(this));
-                }
+    private void addInlineArea(LayoutContext context, LineBreakPosition lbp,
+            boolean isLastPosition) {
+        // the TLM which created the last KnuthElement in this line
+        LayoutManager lastLM = null;
+
+        KnuthSequence seq = (KnuthSequence) knuthParagraphs.get(lbp.parIndex);
+        int startElementIndex = lbp.startIndex;
+        int endElementIndex = lbp.getLeafPos();
+
+        LineArea lineArea = new LineArea(
+                (lbp.getLeafPos() < seq.size() - 1 ? textAlignment : textAlignmentLast),
+                lbp.difference, lbp.availableStretch, lbp.availableShrink);
+        if (lbp.startIndent != 0) {
+            lineArea.addTrait(Trait.START_INDENT, new Integer(lbp.startIndent));
+        }
+        lineArea.setBPD(lbp.lineHeight);
+        lineArea.setIPD(lbp.lineWidth);
+        lineArea.addTrait(Trait.SPACE_BEFORE, new Integer(lbp.spaceBefore));
+        lineArea.addTrait(Trait.SPACE_AFTER, new Integer(lbp.spaceAfter));
+        alignmentContext.resizeLine(lbp.lineHeight, lbp.baseline);
+
+        if (seq instanceof Paragraph) {
+            Paragraph currPar = (Paragraph) seq;
+            // ignore the first elements added by the LineLayoutManager
+            startElementIndex += (startElementIndex == 0) ? currPar.ignoreAtStart : 0;
+
+            // if this is the last line area that for this paragraph,
+            // ignore the last elements added by the LineLayoutManager and
+            // subtract the last-line-end-indent from the area ipd
+            if (endElementIndex == (currPar.size() - 1)) {
+                endElementIndex -= currPar.ignoreAtEnd;
+                lineArea.setIPD(lineArea.getIPD() - lastLineEndIndent.getValue(this));
             }
+        }
 
-            // Remove trailing spaces if allowed so
-            if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED
+        // Remove trailing spaces if allowed so
+        if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED
                 || whiteSpaceTreament == EN_IGNORE
                 || whiteSpaceTreament == EN_IGNORE_IF_BEFORE_LINEFEED) {
-                // ignore the last element in the line if it is a KnuthGlue object
-                seqIterator = seq.listIterator(iEndElement);
-                tempElement = (KnuthElement) seqIterator.next();
-                if (tempElement.isGlue()) {
-                    iEndElement--;
-                    // this returns the same KnuthElement
-                    seqIterator.previous();
-                    if (seqIterator.hasPrevious()) {
-                        tempElement = (KnuthElement) seqIterator.previous();
-                    } else {
-                        tempElement = null;
-                    }
-                }
-                if (tempElement != null) {
-                    lastLM = tempElement.getLayoutManager();
+            // ignore the last element in the line if it is a KnuthGlue object
+            ListIterator seqIterator = seq.listIterator(endElementIndex);
+            KnuthElement lastElement = (KnuthElement) seqIterator.next();
+            lastLM = lastElement.getLayoutManager();
+            if (lastElement.isGlue()) {
+                endElementIndex--;
+                // this returns the same KnuthElement
+                seqIterator.previous();
+                if (seqIterator.hasPrevious()) {
+                    lastLM = ((KnuthElement) seqIterator.previous()).getLayoutManager();
                 }
             }
+        }
 
-            // Remove leading spaces if allowed so
-            if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED
+        // Remove leading spaces if allowed so
+        if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED
                 || whiteSpaceTreament == EN_IGNORE
                 || whiteSpaceTreament == EN_IGNORE_IF_AFTER_LINEFEED) {
-                // ignore KnuthGlue and KnuthPenalty objects
-                // at the beginning of the line
-                seqIterator = seq.listIterator(iStartElement);
-                tempElement = (KnuthElement) seqIterator.next();
-                while (!tempElement.isBox() && seqIterator.hasNext()) {
-                    tempElement = (KnuthElement) seqIterator.next();
-                    iStartElement++;
-                }
-            }
-            // Add the inline areas to lineArea
-            PositionIterator inlinePosIter
-              = new KnuthPossPosIter(seq, iStartElement, iEndElement + 1);
-
-            iStartElement = lbp.getLeafPos() + 1;
-            if (iStartElement == seq.size()) {
-                // advance to next paragraph
-                iStartElement = 0;
+            // ignore KnuthGlue and KnuthPenalty objects
+            // at the beginning of the line
+            ListIterator seqIterator = seq.listIterator(startElementIndex);
+            while (seqIterator.hasNext() && !((KnuthElement) seqIterator.next()).isBox()) {
+                startElementIndex++;
             }
+        }
+        // Add the inline areas to lineArea
+        PositionIterator inlinePosIter = new KnuthPossPosIter(seq, startElementIndex,
+                endElementIndex + 1);
 
-            LayoutContext lc = new LayoutContext(0);
-            lc.setAlignmentContext(alignmentContext);
-            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 (not in the XSL FO recommendation): if the left and right margins
-             * have been optimized, recompute indents and / or adjust ratio, according
-             * to the paragraph horizontal alignment
-             */
-            if (false && textAlignment == EN_JUSTIFY) {
-                // re-compute space adjust ratio
-                int updatedDifference = context.getStackLimitIP().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);
-                //log.debug("LLM.addAreas> old difference = " + lbp.difference + " new difference = " + updatedDifference);
-                //log.debug("              old ratio = " + lbp.ipdAdjust + " new ratio = " + updatedRatio);
-            } else if (false && textAlignment == EN_CENTER) {
-                // re-compute indent
-                int updatedIndent = lbp.startIndent
-                                    + (context.getStackLimitIP().opt - lbp.lineWidth) / 2;
-                lineArea.addTrait(Trait.START_INDENT, new Integer(updatedIndent));
-            } else if (false && textAlignment == EN_END) {
-                // re-compute indent
-                int updatedIndent = lbp.startIndent
-                                    + (context.getStackLimitIP().opt - lbp.lineWidth);
-                lineArea.addTrait(Trait.START_INDENT, new Integer(updatedIndent));
-            }
+        LayoutContext lc = new LayoutContext(0);
+        lc.setAlignmentContext(alignmentContext);
+        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);
 
-            setCurrentArea(lineArea);
-            setChildContext(lc);
-            LayoutManager childLM;
-            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));
+        /*
+         * extension (not in the XSL FO recommendation): if the left and right margins
+         * have been optimized, recompute indents and / or adjust ratio, according
+         * to the paragraph horizontal alignment
+         */
+        if (false && textAlignment == EN_JUSTIFY) {
+            // re-compute space adjust ratio
+            int updatedDifference = context.getRefIPD()
+            - 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);
+            //log.debug("LLM.addAreas> old difference = " + lbp.difference + " new difference = " + updatedDifference);
+            //log.debug("              old ratio = " + lbp.ipdAdjust + " new ratio = " + updatedRatio);
+        } else if (false && textAlignment == EN_CENTER) {
+            // re-compute indent
+            int updatedIndent = lbp.startIndent
+            + (context.getRefIPD() - lbp.lineWidth) / 2;
+            lineArea.addTrait(Trait.START_INDENT, new Integer(updatedIndent));
+        } else if (false && textAlignment == EN_END) {
+            // re-compute indent
+            int updatedIndent = lbp.startIndent
+            + (context.getRefIPD() - lbp.lineWidth);
+            lineArea.addTrait(Trait.START_INDENT, new Integer(updatedIndent));
+        }
 
-            // when can this be null?
-            // if display-align is distribute, add space after
-            if (context.getSpaceAfter() > 0
-                    && (!context.isLastArea() || !isLastPosition)) {
-                lineArea.setBPD(lineArea.getBPD() + context.getSpaceAfter());
-            }
-            lineArea.finalise();
-            parentLM.addChildArea(lineArea);
+        setCurrentArea(lineArea);
+        setChildContext(lc);
+        LayoutManager childLM;
+        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));
+        }
+
+        // if display-align is distribute, add space after
+        if (context.getSpaceAfter() > 0
+                && (!context.isLastArea() || !isLastPosition)) {
+            lineArea.setBPD(lineArea.getBPD() + context.getSpaceAfter());
+        }
+        lineArea.finalise();
+        parentLM.addChildArea(lineArea);
     }
 
     /**
@@ -1800,7 +1626,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
             // set last area flag
             blocklc.setFlags(LayoutContext.LAST_AREA,
                              (context.isLastArea() && childLM == lastLM));
-            blocklc.setStackLimitsFrom(context);
+            blocklc.setStackLimitBP(context.getStackLimitBP());
             // Add the line areas to Area
             childLM.addAreas(childPosIter, blocklc);
             blocklc.setLeadingSpace(blocklc.getTrailingSpace());
@@ -1841,5 +1667,11 @@ public class LineLayoutManager extends InlineStackingLayoutManager
     public boolean getGeneratesLineArea() {
         return true;
     }
+
+    /** {@inheritDoc} */
+    public boolean isRestartable() {
+        return true;
+    }
+
 }
 
index 7000a87482890dcabcb49e108128679810f87054..fb88bb79d2da5ed42117de203b7d74545300bb16 100644 (file)
@@ -702,6 +702,13 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager
         }
     }
 
+    /** {@inheritDoc} */
+    public void reset() {
+        super.reset();
+        label.reset();
+        body.reset();
+    }
+
 
 }
 
index 420f16a096c84d526f2237209ec34c9afdd88328..fd57d50996d6495b9a0d85248bb76451c712dfe1 100644 (file)
@@ -1,3 +1,4 @@
-<?xml version="1.0" encoding="UTF-8"?><catalogue xml:lang="en">
+<?xml version="1.0" encoding="UTF-8"?>
+<catalogue xml:lang="en">
   <message key="org.apache.fop.render.pdf.PDFEventProducer.nonFullyResolvedLinkTargets">{count} link target{count,equals,1,,s} could not be fully resolved and now point{count,equals,1,,s} to the top of the page or {count,equals,1,is,are} dysfunctional.</message>
 </catalogue>
diff --git a/test/layoutengine/standard-testcases/flow_changing-ipd_1.xml b/test/layoutengine/standard-testcases/flow_changing-ipd_1.xml
new file mode 100644 (file)
index 0000000..d4db6e6
--- /dev/null
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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$ -->
+<testcase>
+  <info>
+    <p>
+      This test checks that blocks of texts are re-laid out after a change of the flow ipd.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="narrow"
+          page-height="300pt" page-width="400pt" margin="50pt">
+          <fo:region-body background-color="#F0F0F0"/>
+        </fo:simple-page-master>
+        <fo:simple-page-master master-name="wide"
+          page-height="300pt" page-width="600pt" margin="50pt">
+          <fo:region-body background-color="#F0F0F0"/>
+        </fo:simple-page-master>
+        <fo:page-sequence-master master-name="pages">
+          <fo:single-page-master-reference master-reference="narrow"/>
+          <fo:repeatable-page-master-reference master-reference="wide"/>
+        </fo:page-sequence-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="pages">
+        <fo:flow flow-name="xsl-region-body" language="en" hyphenate="true">
+          <fo:block text-align="justify" id="surrounding"
+            space-before.minimum="10pt"
+            space-before.optimum="12pt"
+            space-before.maximum="50pt">
+            <fo:block space-before="inherit" id="b1">In olden times when wishing still helped one, 
+              there lived a king whose daughters were all beautiful, but the youngest was so 
+              beautiful that the sun itself, which has seen so much, was astonished whenever it 
+              shone in her face.</fo:block>
+            <fo:block space-before="inherit" id="b2">In olden times when wishing still helped one, 
+              there lived a king whose daughters were all beautiful, but the youngest was so 
+              beautiful that the sun itself, which has seen so much, was astonished whenever it 
+              shone in her face.</fo:block>
+            <fo:block space-before="inherit" id="b3" border-top="1pt solid black" 
+              border-before-width.conditionality="retain">In olden times when wishing still helped 
+              one, there lived a king whose daughters were all beautiful, but the youngest was so 
+              beautiful that the sun itself, which has seen so much, was astonished whenever it 
+              shone in her face. In olden times when wishing still helped one, there lived a king 
+              whose daughters were all beautiful, but the youngest was so beautiful that the sun 
+              itself, which has seen so much, was astonished whenever it shone in her 
+              face.</fo:block>
+            <fo:block space-before="inherit" id="b4" border-top="1pt solid black">In olden times 
+              when wishing still helped one, there lived a king whose daughters were all beautiful, 
+              but the youngest was so beautiful that the sun itself, which has seen so much, was 
+              astonished whenever it shone in her face.</fo:block>
+            <fo:block space-before="inherit" id="b5">In olden times when wishing still helped one, 
+              there lived a king whose daughters were all beautiful, but the youngest was so 
+              beautiful that the sun itself, which has seen so much, was astonished whenever it 
+              shone in her face.</fo:block>
+          </fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="13100"  xpath="//pageViewport[1]//flow/block/block[2]/@space-before"/>
+    <eval expected="13100"  xpath="//pageViewport[1]//flow/block/block[3]/@space-before"/>
+    <eval expected="(solid,#000000,1000)"
+                            xpath="//pageViewport[1]//flow/block/block[3]/@border-before"/>
+    <eval expected="In"     xpath="//pageViewport[1]//flow/block/block[3]/lineArea[4]/text/word[position()=last()]"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/@ipd"/>
+    <eval expected="(solid,#000000,1000)"
+                            xpath="//pageViewport[2]//flow/block/block[1]/@border-before"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[1]/@ipd"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[1]/lineArea[1]/@ipd"/>
+    <eval expected="olden"  xpath="//pageViewport[2]//flow/block/block[1]/lineArea[1]/text/word[1]"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[2]/@ipd"/>
+    <eval expected="12000"  xpath="//pageViewport[2]//flow/block/block[2]/@space-before"/>
+    <eval expected="(solid,#000000,1000)"
+                            xpath="//pageViewport[2]//flow/block/block[2]/@border-before"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[2]/lineArea[1]/@ipd"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[3]/@ipd"/>
+    <eval expected="12000"  xpath="//pageViewport[2]//flow/block/block[3]/@space-before"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[3]/lineArea[1]/@ipd"/>
+  </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/flow_changing-ipd_2.xml b/test/layoutengine/standard-testcases/flow_changing-ipd_2.xml
new file mode 100644 (file)
index 0000000..dbe002e
--- /dev/null
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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$ -->
+<testcase>
+  <info>
+    <p>
+      This test checks that blocks of texts are re-laid out after a change of the flow ipd.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="narrow"
+          page-height="300pt" page-width="400pt" margin="50pt">
+          <fo:region-body background-color="#F0F0F0"/>
+        </fo:simple-page-master>
+        <fo:simple-page-master master-name="wide"
+          page-height="300pt" page-width="600pt" margin="50pt">
+          <fo:region-body background-color="#F0F0F0"/>
+        </fo:simple-page-master>
+        <fo:page-sequence-master master-name="pages">
+          <fo:single-page-master-reference master-reference="narrow"/>
+          <fo:repeatable-page-master-reference master-reference="wide"/>
+        </fo:page-sequence-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="pages">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block id="b1">In olden times when wishing still helped one, there lived a king whose 
+            daughters were all beautiful, but the youngest was so beautiful that the sun itself, 
+            which has seen so much, was astonished whenever it shone in her face.</fo:block>
+          <fo:block id="b2">In olden times when wishing still helped one, there lived a king whose 
+            daughters were all beautiful, but the youngest was so beautiful that the sun itself, 
+            which has seen so much, was astonished whenever it shone in her face.</fo:block>
+          <fo:block id="b3">In olden times when wishing still helped one, there lived a king whose 
+            daughters were all beautiful, but the youngest was so beautiful that the sun itself, 
+            which has seen so much, was astonished whenever it shone in her face. In olden times 
+            when wishing still helped one, there lived a king whose daughters were all beautiful, 
+            but the youngest was so beautiful that the sun itself, which has seen so much, was 
+            astonished whenever it shone in her face.</fo:block>
+          <fo:block id="b4">In olden times when wishing still helped one, there lived a king whose 
+            daughters were all beautiful, but the youngest was so beautiful that the sun itself, 
+            which has seen so much, was astonished whenever it shone in her face.</fo:block>
+          <fo:block id="b5">In olden times when wishing still helped one, there lived a king whose 
+            daughters were all beautiful, but the youngest was so beautiful that the sun itself, 
+            which has seen so much, was astonished whenever it shone in her face.</fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="has"    xpath="//pageViewport[1]//flow/block[3]/lineArea[3]/text/word[position()=last()]"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block[1]/@ipd"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block[1]/lineArea[1]/@ipd"/>
+    <eval expected="seen"   xpath="//pageViewport[2]//flow/block[1]/lineArea[1]/text/word[1]"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block[2]/@ipd"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block[2]/lineArea[1]/@ipd"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block[3]/@ipd"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block[3]/lineArea[1]/@ipd"/>
+  </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/flow_changing-ipd_3.xml b/test/layoutengine/standard-testcases/flow_changing-ipd_3.xml
new file mode 100644 (file)
index 0000000..2badfe7
--- /dev/null
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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$ -->
+<testcase>
+  <info>
+    <p>
+      This test checks that a change of IPD between two blocks is correctly handled.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="narrow"
+          page-height="300pt" page-width="400pt" margin="50pt">
+          <fo:region-body background-color="#F0F0F0"/>
+        </fo:simple-page-master>
+        <fo:simple-page-master master-name="wide"
+          page-height="300pt" page-width="600pt" margin="50pt">
+          <fo:region-body background-color="#F0F0F0"/>
+        </fo:simple-page-master>
+        <fo:page-sequence-master master-name="pages">
+          <fo:single-page-master-reference master-reference="narrow"/>
+          <fo:repeatable-page-master-reference master-reference="wide"/>
+        </fo:page-sequence-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="pages">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block text-align="justify" id="surrounding"
+            space-before.minimum="10pt"
+            space-before.optimum="12pt"
+            space-before.maximum="50pt">
+            <fo:block space-before="inherit" id="b1">In olden times when wishing still helped one, 
+              there lived a king whose daughters were all beautiful, but the youngest was so 
+              beautiful that the sun itself, which has seen so much, was astonished whenever it 
+              shone in her face.</fo:block>
+            <fo:block space-before="inherit" id="b2">In olden times when wishing still helped one, 
+              there lived a king whose daughters were all beautiful, but the youngest was so 
+              beautiful that the sun itself, which has seen so much, was astonished whenever it 
+              shone in her face.</fo:block>
+            <fo:block space-before="inherit" id="b3">In olden times when wishing still helped one, 
+              there lived a king whose daughters were all beautiful, but the youngest was so 
+              beautiful that the sun itself, which has seen so much, was astonished whenever it 
+              shone in her face.</fo:block>
+            <fo:block border-top="1pt solid black" space-before.minimum="10pt" 
+              space-before.optimum="12pt" space-before.maximum="50pt" 
+              space-before.conditionality="retain" id="b4">In olden times when wishing still helped 
+              one, there lived a king whose daughters were all beautiful, but the youngest was so 
+              beautiful that the sun itself, which has seen so much, was astonished whenever it 
+              shone in her face.</fo:block>
+            <fo:block space-before="inherit" id="b5">In olden times when wishing still helped one, 
+              there lived a king whose daughters were all beautiful, but the youngest was so 
+              beautiful that the sun itself, which has seen so much, was astonished whenever it 
+              shone in her face.</fo:block>
+          </fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="face."  xpath="//pageViewport[1]//flow/block/block[3]/lineArea[4]/text/word[position()=last()]"/>
+    <eval expected="12000"  xpath="//pageViewport[2]//flow/block/block[1]/@space-before"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[1]/@ipd"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[1]/lineArea[1]/@ipd"/>
+    <eval expected="(solid,#000000,1000)"
+                            xpath="//pageViewport[2]//flow/block/block[1]/@border-before"/>
+    <eval expected="In"     xpath="//pageViewport[2]//flow/block/block[1]/lineArea[1]/text/word[1]"/>
+    <eval expected="olden"  xpath="//pageViewport[2]//flow/block/block[1]/lineArea[1]/text/word[2]"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[2]/@ipd"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[2]/lineArea[1]/@ipd"/>
+  </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/flow_changing-ipd_4.xml b/test/layoutengine/standard-testcases/flow_changing-ipd_4.xml
new file mode 100644 (file)
index 0000000..fe166d0
--- /dev/null
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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$ -->
+<testcase>
+  <info>
+    <p>
+      This test checks that non-restartable elements still show up at IPD change, even if not 
+      re-laid out.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="narrow"
+          page-height="300pt" page-width="400pt" margin="50pt">
+          <fo:region-body background-color="#F0F0F0"/>
+        </fo:simple-page-master>
+        <fo:simple-page-master master-name="wide"
+          page-height="300pt" page-width="600pt" margin="50pt">
+          <fo:region-body background-color="#F0F0F0"/>
+        </fo:simple-page-master>
+        <fo:page-sequence-master master-name="pages">
+          <fo:single-page-master-reference master-reference="narrow"/>
+          <fo:repeatable-page-master-reference master-reference="wide"/>
+        </fo:page-sequence-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="pages">
+        <fo:flow flow-name="xsl-region-body" text-align="justify">
+          <fo:block space-before="10pt" id="b1_1">In olden times when wishing still helped one, there 
+            lived a king whose daughters were all beautiful, but the youngest was so beautiful that 
+            the sun itself, which has seen so much, was astonished whenever it shone in her 
+            face.</fo:block>
+          <fo:block space-before="10pt" id="b1_2">In olden times when wishing still helped one, there 
+            lived a king whose daughters were all beautiful, but the youngest was so beautiful that 
+            the sun itself, which has seen so much, was astonished whenever it shone in her 
+            face.</fo:block>
+          <fo:table table-layout="fixed" width="100%" border="1pt solid black" space-before="10pt" 
+            padding="2pt" border-collapse="separate">
+            <fo:table-body>
+              <fo:table-row>
+                <fo:table-cell>
+                  <fo:block space-before="10pt" id="b1_3">In olden times when wishing still helped 
+                    one, there lived a king whose daughters were all beautiful, but the youngest was 
+                    so beautiful that the sun itself, which has seen so much, was astonished 
+                    whenever it shone in her face.</fo:block>
+                  <fo:block space-before="10pt" id="b1_4">In olden times when wishing still helped 
+                    one, there lived a king whose daughters were all beautiful, but the youngest was 
+                    so beautiful that the sun itself, which has seen so much, was astonished 
+                    whenever it shone in her face.</fo:block>
+                </fo:table-cell>
+              </fo:table-row>
+            </fo:table-body>
+          </fo:table>
+          <fo:block space-before="10pt" id="b1_5" border-top="1pt solid red">In olden times when 
+            wishing still helped one, there lived a king whose daughters were all beautiful, but the 
+            youngest was so beautiful that the sun itself, which has seen so much, was astonished 
+            whenever it shone in her face.</fo:block>
+          <fo:block space-before="10pt" id="b1_6">In olden times when wishing still helped one, there 
+            lived a king whose daughters were all beautiful, but the youngest was so beautiful that 
+            the sun itself, which has seen so much, was astonished whenever it shone in her 
+            face.</fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+      <fo:page-sequence master-reference="pages">
+        <fo:flow flow-name="xsl-region-body" text-align="justify">
+          <fo:block space-before="10pt" id="b2_1">In olden times when wishing still helped one, there 
+            lived a king whose daughters were all beautiful, but the youngest was so beautiful that 
+            the sun itself, which has seen so much, was astonished whenever it shone in her 
+            face.</fo:block>
+          <fo:block space-before="10pt" id="b2_2">In olden times when wishing still helped one, there 
+            lived a king whose daughters were all beautiful, but the youngest was so beautiful that 
+            the sun itself, which has seen so much, was astonished whenever it shone in her 
+            face.</fo:block>
+          <fo:list-block space-before="10pt" provisional-distance-between-starts="0.5cm">
+            <fo:list-item>
+              <fo:list-item-label end-indent="label-end()">
+                <fo:block start-indent="2pt">•</fo:block>
+              </fo:list-item-label>
+              <fo:list-item-body start-indent="body-start()">
+                <fo:block space-before="10pt" id="b2_3">In olden times when wishing still helped 
+                  one, there lived a king whose daughters were all beautiful, but the youngest was 
+                  so beautiful that the sun itself, which has seen so much, was 
+                  astonished…</fo:block>
+              </fo:list-item-body>
+            </fo:list-item>
+            <fo:list-item>
+              <fo:list-item-label end-indent="label-end()">
+                <fo:block start-indent="2pt">•</fo:block>
+              </fo:list-item-label>
+              <fo:list-item-body start-indent="body-start()">
+                <fo:block id="b2_4">In olden times when wishing still helped one, there lived a king 
+                  whose daughters were all beautiful, but the youngest was so beautiful that the sun 
+                  itself, which has seen so much, was astonished…</fo:block>
+              </fo:list-item-body>
+            </fo:list-item>
+          </fo:list-block>
+          <fo:block space-before="10pt" space-before.conditionality="retain" border-top="1pt solid 
+            red" id="b2_5">In olden times when wishing still helped one, there lived a king whose 
+            daughters were all beautiful, but the youngest was so beautiful that the sun itself, 
+            which has seen so much, was astonished whenever it shone in her face.</fo:block>
+          <fo:block space-before="10pt" id="b2_6">In olden times when wishing still helped one, there 
+            lived a king whose daughters were all beautiful, but the youngest was so beautiful that 
+            the sun itself, which has seen so much, was astonished whenever it shone in her 
+            face.</fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <!-- First page sequence – table -->
+    <eval expected="(solid,#000000,1000)"
+                            xpath="//pageSequence[1]/pageViewport[2]//flow/block[1]/@border-after"/>
+    <eval expected="300000" xpath="//pageSequence[1]/pageViewport[2]//flow/block[1]//lineArea[1]/@ipd"/>
+    <eval expected="b1_4"   xpath="//pageSequence[1]/pageViewport[2]//flow/block[1]/block/block/@prod-id"/>
+    <eval expected="In"     xpath="//pageSequence[1]/pageViewport[2]//flow/block[1]//lineArea[1]/text/word[1]"/>
+    <eval expected="olden"  xpath="//pageSequence[1]/pageViewport[2]//flow/block[1]//lineArea[1]/text/word[2]"/>
+    <eval expected="her"    xpath="//pageSequence[1]/pageViewport[2]//flow/block[1]//lineArea[4]/text/word[position()=last()-1]"/>
+    <eval expected="face."  xpath="//pageSequence[1]/pageViewport[2]//flow/block[1]//lineArea[4]/text/word[position()=last()]"/>
+
+    <eval expected="b1_5"   xpath="//pageSequence[1]/pageViewport[2]//flow/block[2]/@prod-id"/>
+    <eval expected="500000" xpath="//pageSequence[1]/pageViewport[2]//flow/block[2]/@ipd"/>
+    <eval expected="(solid,#ff0000,1000)"
+                            xpath="//pageSequence[1]/pageViewport[2]//flow/block[2]/@border-before"/>
+    <eval expected="In"     xpath="//pageSequence[1]/pageViewport[2]//flow/block[2]//lineArea[1]/text/word[1]"/>
+    <eval expected="olden"  xpath="//pageSequence[1]/pageViewport[2]//flow/block[2]//lineArea[1]/text/word[2]"/>
+
+    <!-- Second page sequence – list -->
+    <eval expected="300000" xpath="//pageSequence[2]/pageViewport[2]//flow/block[1]/@ipd"/>
+    <eval expected="b2_4"   xpath="//pageSequence[2]/pageViewport[2]//flow/block[1]/block/block[2]/block/@prod-id"/>
+    <eval expected="In"     xpath="//pageSequence[2]/pageViewport[2]//flow/block[1]/block/block[2]/block/lineArea[1]/text/word[1]"/>
+    <eval expected="olden"  xpath="//pageSequence[2]/pageViewport[2]//flow/block[1]/block/block[2]/block/lineArea[1]/text/word[2]"/>
+    <eval expected="was"    xpath="//pageSequence[2]/pageViewport[2]//flow/block[1]/block/block[2]/block/lineArea[4]/text/word[position()=last()-1]"/>
+    <eval expected="astonished…"
+                            xpath="//pageSequence[2]/pageViewport[2]//flow/block[1]/block/block[2]/block/lineArea[4]/text/word[position()=last()]"/>
+
+    <eval expected="b2_5"   xpath="//pageSequence[2]/pageViewport[2]//flow/block[2]/@prod-id"/>
+    <eval expected="500000" xpath="//pageSequence[2]/pageViewport[2]//flow/block[2]/@ipd"/>
+    <eval expected="10000"  xpath="//pageSequence[2]/pageViewport[2]//flow/block[2]/@space-before"/>
+    <eval expected="(solid,#ff0000,1000)"
+                            xpath="//pageSequence[2]/pageViewport[2]//flow/block[2]/@border-before"/>
+    <eval expected="In"     xpath="//pageSequence[2]/pageViewport[2]//flow/block[2]//lineArea[1]/text/word[1]"/>
+    <eval expected="olden"  xpath="//pageSequence[2]/pageViewport[2]//flow/block[2]//lineArea[1]/text/word[2]"/>
+  </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/flow_changing-ipd_block-container_1.xml b/test/layoutengine/standard-testcases/flow_changing-ipd_block-container_1.xml
new file mode 100644 (file)
index 0000000..82b757d
--- /dev/null
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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$ -->
+<testcase>
+  <info>
+    <p>
+      This test checks that block-container elements correctly support an IPD change.
+    </p>
+    <!-- NOTE: This test case is a copy of flow_changing-ipd_1.xml, modified to simply surround 
+    block 3 with a block-container. -->
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="narrow"
+          page-height="300pt" page-width="400pt" margin="50pt">
+          <fo:region-body background-color="#F0F0F0"/>
+        </fo:simple-page-master>
+        <fo:simple-page-master master-name="wide"
+          page-height="300pt" page-width="600pt" margin="50pt">
+          <fo:region-body background-color="#F0F0F0"/>
+        </fo:simple-page-master>
+        <fo:page-sequence-master master-name="pages">
+          <fo:single-page-master-reference master-reference="narrow"/>
+          <fo:repeatable-page-master-reference master-reference="wide"/>
+        </fo:page-sequence-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="pages">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block text-align="justify" id="surrounding"
+            space-before.minimum="10pt"
+            space-before.optimum="12pt"
+            space-before.maximum="50pt">
+            <fo:block space-before="inherit" id="b1">In olden times when wishing still helped one, 
+              there lived a king whose daughters were all beautiful, but the youngest was so 
+              beautiful that the sun itself, which has seen so much, was astonished whenever it 
+              shone in her face.</fo:block>
+            <fo:block space-before="inherit" id="b2">In olden times when wishing still helped one, 
+              there lived a king whose daughters were all beautiful, but the youngest was so 
+              beautiful that the sun itself, which has seen so much, was astonished whenever it 
+              shone in her face.</fo:block>
+            <fo:block-container space-before="inherit">
+              <fo:block id="b3" border-top="1pt solid black" 
+                border-before-width.conditionality="retain">In olden times when wishing still helped 
+                one, there lived a king whose daughters were all beautiful, but the youngest was so 
+                beautiful that the sun itself, which has seen so much, was astonished whenever it 
+                shone in her face. In olden times when wishing still helped one, there lived a king 
+                whose daughters were all beautiful, but the youngest was so beautiful that the sun 
+                itself, which has seen so much, was astonished whenever it shone in her 
+                face.</fo:block>
+            </fo:block-container>
+            <fo:block space-before="inherit" id="b4" border-top="1pt solid black">In olden times 
+              when wishing still helped one, there lived a king whose daughters were all beautiful, 
+              but the youngest was so beautiful that the sun itself, which has seen so much, was 
+              astonished whenever it shone in her face.</fo:block>
+            <fo:block space-before="inherit" id="b5">In olden times when wishing still helped one, 
+              there lived a king whose daughters were all beautiful, but the youngest was so 
+              beautiful that the sun itself, which has seen so much, was astonished whenever it 
+              shone in her face.</fo:block>
+          </fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="13100"  xpath="//pageViewport[1]//flow/block/block[2]/@space-before"/>
+    <eval expected="13100"  xpath="//pageViewport[1]//flow/block/block[3]/@space-before"/>
+    <eval expected="(solid,#000000,1000)"
+                            xpath="//pageViewport[1]//flow/block/block[3]/block/block/@border-before"/>
+    <eval expected="In"     xpath="//pageViewport[1]//flow/block/block[3]/block/block/lineArea[4]/text/word[position()=last()]"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/@ipd"/>
+    <eval expected="(solid,#000000,1000)"
+                            xpath="//pageViewport[2]//flow/block/block[1]/block/block/@border-before"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[1]/block/block/@ipd"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[1]/block/block/lineArea[1]/@ipd"/>
+    <eval expected="olden"  xpath="//pageViewport[2]//flow/block/block[1]/block/block/lineArea[1]/text/word[1]"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[2]/@ipd"/>
+    <eval expected="12000"  xpath="//pageViewport[2]//flow/block/block[2]/@space-before"/>
+    <eval expected="(solid,#000000,1000)"
+                            xpath="//pageViewport[2]//flow/block/block[2]/@border-before"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[2]/lineArea[1]/@ipd"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[3]/@ipd"/>
+    <eval expected="12000"  xpath="//pageViewport[2]//flow/block/block[3]/@space-before"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/block[3]/lineArea[1]/@ipd"/>
+  </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/flow_changing-ipd_block-container_2.xml b/test/layoutengine/standard-testcases/flow_changing-ipd_block-container_2.xml
new file mode 100644 (file)
index 0000000..7a2bb9b
--- /dev/null
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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$ -->
+<testcase>
+  <info>
+    <p>
+      This test checks that a change of IPD between two blocks surrounded by a block-container is 
+      correctly handled.
+    </p>
+    <!-- NOTE: This test case is a copy of flow_changing-ipd_3.xml, with the surrounding block 
+    replaced with a block-container. -->
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="narrow"
+          page-height="300pt" page-width="400pt" margin="50pt">
+          <fo:region-body background-color="#F0F0F0"/>
+        </fo:simple-page-master>
+        <fo:simple-page-master master-name="wide"
+          page-height="300pt" page-width="600pt" margin="50pt">
+          <fo:region-body background-color="#F0F0F0"/>
+        </fo:simple-page-master>
+        <fo:page-sequence-master master-name="pages">
+          <fo:single-page-master-reference master-reference="narrow"/>
+          <fo:repeatable-page-master-reference master-reference="wide"/>
+        </fo:page-sequence-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="pages">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block-container text-align="justify" id="surrounding"
+            space-before.minimum="10pt"
+            space-before.optimum="12pt"
+            space-before.maximum="50pt">
+            <fo:block space-before="inherit" id="b1">In olden times when wishing still helped one, 
+              there lived a king whose daughters were all beautiful, but the youngest was so 
+              beautiful that the sun itself, which has seen so much, was astonished whenever it 
+              shone in her face.</fo:block>
+            <fo:block space-before="inherit" id="b2">In olden times when wishing still helped one, 
+              there lived a king whose daughters were all beautiful, but the youngest was so 
+              beautiful that the sun itself, which has seen so much, was astonished whenever it 
+              shone in her face.</fo:block>
+            <fo:block space-before="inherit" id="b3">In olden times when wishing still helped one, 
+              there lived a king whose daughters were all beautiful, but the youngest was so 
+              beautiful that the sun itself, which has seen so much, was astonished whenever it 
+              shone in her face.</fo:block>
+            <fo:block border-top="1pt solid black" space-before.minimum="10pt" 
+              space-before.optimum="12pt" space-before.maximum="50pt" 
+              space-before.conditionality="retain" id="b4">In olden times when wishing still helped 
+              one, there lived a king whose daughters were all beautiful, but the youngest was so 
+              beautiful that the sun itself, which has seen so much, was astonished whenever it 
+              shone in her face.</fo:block>
+            <fo:block space-before="inherit" id="b5">In olden times when wishing still helped one, 
+              there lived a king whose daughters were all beautiful, but the youngest was so 
+              beautiful that the sun itself, which has seen so much, was astonished whenever it 
+              shone in her face.</fo:block>
+          </fo:block-container>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="face."  xpath="//pageViewport[1]//flow/block/block/block[3]/lineArea[4]/text/word[position()=last()]"/>
+    <eval expected="12000"  xpath="//pageViewport[2]//flow/block/block/block[1]/@space-before"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/block/block[1]/@ipd"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/block/block[1]/lineArea[1]/@ipd"/>
+    <eval expected="(solid,#000000,1000)"                       
+                            xpath="//pageViewport[2]//flow/block/block/block[1]/@border-before"/>
+    <eval expected="In"     xpath="//pageViewport[2]//flow/block/block/block[1]/lineArea[1]/text/word[1]"/>
+    <eval expected="olden"  xpath="//pageViewport[2]//flow/block/block/block[1]/lineArea[1]/text/word[2]"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/block/block[2]/@ipd"/>
+    <eval expected="500000" xpath="//pageViewport[2]//flow/block/block/block[2]/lineArea[1]/@ipd"/>
+  </checks>
+</testcase>