Index: src/java/org/apache/fop/layoutmgr/PageBreaker.java
===================================================================
--- src/java/org/apache/fop/layoutmgr/PageBreaker.java	(revision 765272)
+++ src/java/org/apache/fop/layoutmgr/PageBreaker.java	Wed Apr 15 20:31:49 CEST 2009
@@ -257,7 +257,7 @@
 
         if (needColumnBalancing) {
             //column balancing for the last part
-            doPhase3(alg, partCount, originalList, effectiveList, false);
+            doPhase3(alg, partCount, originalList, effectiveList, false, false);
             return;
         }
 
@@ -266,11 +266,16 @@
             //last part is reached
             if (lastPageMasterDefined) {
                 //last-page condition
-                doPhase3(alg, partCount, originalList, effectiveList, true);
+                doPhase3(alg, partCount, originalList, effectiveList, true, false);
                 return;
             }
         }
 
+        if (lastPageMasterDefined) {
+            doPhase3(alg, partCount, originalList, effectiveList, false, true);
+            return;
+        }
+
         //nothing special: just add the areas now
         addAreas(alg, partCount, originalList, effectiveList);
     }
@@ -282,9 +287,9 @@
      *  last break in case of column-balancing
      *  and/or a last page-master)
      */
-    private void doPhase3(PageBreakingAlgorithm alg, int partCount,
+    protected void doPhase3(PageBreakingAlgorithm alg, int partCount,
             BlockSequence originalList, BlockSequence effectiveList,
-            boolean isLastPart) {
+            boolean isLastPart, boolean deferLastPart) {
 
 
         int newStartPos = 0;
@@ -312,6 +317,18 @@
         pageProvider.setStartOfNextElementList(currentPageNum,
                 pslm.getCurrentPV().getCurrentSpan().getCurrentFlowIndex());
 
+        if (deferLastPart) {
+            List lastPart = originalList.subList(newStartPos, originalList.size());
+            BlockSequence deferredPart = new BlockSequence(
+                    originalList.getStartOn(),
+                    originalList.getDisplayAlign(),
+                    originalList.getSpan());
+            deferredPart.addAll(lastPart);
+            deferredPart.ignoreAtEnd = originalList.ignoreAtEnd;
+            this.deferPart(deferredPart, originalList);
+            return;
+        }
+
         PageBreakingAlgorithm algRestart = null;
         int optimalPageCount;
         //Make sure we only add the areas we haven't added already
Index: src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
===================================================================
--- src/java/org/apache/fop/layoutmgr/AbstractBreaker.java	(revision 764988)
+++ src/java/org/apache/fop/layoutmgr/AbstractBreaker.java	Wed Apr 15 20:37:00 CEST 2009
@@ -77,19 +77,33 @@
 
         private int displayAlign;
 
+        private int span;
+
         /**
          * Creates a new BlockSequence.
-         * @param iStartOn the kind of page the sequence should start on. One of EN_ANY, EN_COLUMN,
+         * @param startOn the kind of page the sequence should start on. One of EN_ANY, EN_COLUMN,
          *                 EN_ODD_PAGE, EN_EVEN_PAGE.
          * @param displayAlign the value for the display-align property
          */
-        public BlockSequence(int iStartOn, int displayAlign) {
+        public BlockSequence(int startOn, int displayAlign) {
             super();
-            startOn = iStartOn;
+            this.startOn = startOn;
             this.displayAlign = displayAlign;
         }
 
         /**
+         * Creates a new BlockSequence with the given value for span.
+         * @param startOn the kind of page the sequence should start on. One of EN_ANY, EN_COLUMN,
+         *                 EN_ODD_PAGE, EN_EVEN_PAGE.
+         * @param displayAlign the value for the display-align property
+         * @param span  the value for the span property
+         */
+        public BlockSequence(int startOn, int displayAlign, int span) {
+            this(startOn, displayAlign);
+            this.span = span;
+        }
+
+        /**
          * @return the kind of page the sequence should start on. One of EN_ANY, EN_COLUMN,
          *         EN_ODD_PAGE, EN_EVEN_PAGE.
          */
@@ -101,6 +115,12 @@
         public int getDisplayAlign() {
             return this.displayAlign;
         }
+
+        /** @return the value for the span property */
+        public int getSpan() {
+            return this.span;
+        }
+
         /**
          * Finalizes a Knuth sequence.
          * @return a finalized sequence.
@@ -145,7 +165,7 @@
         public BlockSequence endBlockSequence(Position breakPosition) {
             KnuthSequence temp = endSequence(breakPosition);
             if (temp != null) {
-                BlockSequence returnSequence = new BlockSequence(startOn, displayAlign);
+                BlockSequence returnSequence = new BlockSequence(startOn, displayAlign, span);
                 returnSequence.addAll(temp);
                 returnSequence.ignoreAtEnd = this.ignoreAtEnd;
                 return returnSequence;
@@ -156,13 +176,68 @@
 
     }
 
-    /** blockListIndex of the current BlockSequence in blockLists */
-    private int blockListIndex = 0;
+    /**
+     * Helper proxy, with a restricted set of methods to control
+     * access to the List of BlockSequences in scope
+     * (as of Java 5, the compile-time type-safety can be guaranteed
+     *  by means of generics)
+     */
+    private static class BlockLists {
 
-    private List blockLists = null;
+        private static final int DEFAULT_CAPACITY = 5;
 
+        private List blockLists;
+
+        BlockLists() {
+            this(DEFAULT_CAPACITY);
+        }
+
+        BlockLists(int initialCapacity) {
+            this.blockLists = new java.util.ArrayList(initialCapacity);
+        }
+
+        void add(BlockSequence blockSequence) {
+            this.blockLists.add(blockSequence);
+        }
+
+        void add(int index, BlockSequence blockSequence) {
+            this.blockLists.add(index, blockSequence);
+        }
+
+        BlockSequence set(int index, BlockSequence blockSequence) {
+            return (BlockSequence) this.blockLists.set(index, blockSequence);
+        }
+
+        BlockSequence get(int index) {
+            //coerce the returned object to a BlockSequence
+            return (BlockSequence) this.blockLists.get(index);
+        }
+
+        BlockSequence getLast() {
+            return (BlockSequence) ListUtil.getLast(this.blockLists);
+        }
+
+        boolean isEmpty() {
+            return this.blockLists.isEmpty();
+        }
+
+        int size() {
+            return this.blockLists.size();
+        }
+
+        void clear() {
+            this.blockLists.clear();
+        }
+
+        int indexOf(BlockSequence blockList) {
+            return this.blockLists.indexOf(blockList);
+        }
+
+    }
+
+    private BlockLists blockLists = null;
+
     protected int alignment;
-    private int alignmentLast;
 
     protected MinOptMax footnoteSeparatorLength = new MinOptMax(0);
 
@@ -284,47 +359,76 @@
         } else {
             alignment = Constants.EN_START;
         }
-        alignmentLast = Constants.EN_START;
+        int alignmentLast = Constants.EN_START;
         if (isSinglePartFavored() && alignment == Constants.EN_JUSTIFY) {
             alignmentLast = Constants.EN_JUSTIFY;
         }
         childLC.setBPAlignment(alignment);
 
         BlockSequence blockList;
-        this.blockLists = new java.util.ArrayList();
+        this.blockLists = new BlockLists();
 
         log.debug("PLM> flow BPD =" + flowBPD);
 
         //*** Phase 1: Get Knuth elements ***
         int nextSequenceStartsOn = Constants.EN_ANY;
-        while (hasMoreContent()) {
-            blockLists.clear();
-
+        boolean deferredParts;
+        boolean allPartsDeferred = false;
+        while (hasMoreContent() || allPartsDeferred) {
+            //check if there are deferred parts from previous iterations
+            deferredParts = !blockLists.isEmpty();
+            //fetch next block list
             nextSequenceStartsOn = getNextBlockList(childLC, nextSequenceStartsOn);
 
             //*** Phase 2: Alignment and breaking ***
             log.debug("PLM> blockLists.size() = " + blockLists.size());
-            for (blockListIndex = 0; blockListIndex < blockLists.size(); blockListIndex++) {
-                blockList = (BlockSequence) blockLists.get(blockListIndex);
+            int lastPageCount = 0;
+            int numBlockLists = blockLists.size();
+            boolean isDeferredPart;
+            for (int blockListIndex = 0; blockListIndex < numBlockLists; blockListIndex++) {
+                isDeferredPart = (allPartsDeferred
+                        || (deferredParts && blockListIndex < numBlockLists - 1));
+                blockList = blockLists.get(blockListIndex);
 
                 //debug code start
                 if (log.isDebugEnabled()) {
                     log.debug("  blockListIndex = " + blockListIndex);
                     String pagina = (blockList.startOn == Constants.EN_ANY) ? "any page"
                             : (blockList.startOn == Constants.EN_ODD_PAGE) ? "odd page"
-                                    : "even page";
+                            : (blockList.startOn == Constants.EN_EVEN_PAGE) ? "even page"
+                            : "" + blockList.startOn;
                     log.debug("  sequence starts on " + pagina);
+                    if (isDeferredPart) {
+                        log.debug("  deferred sequence (allPartsDeferred=" + allPartsDeferred + ")");
-                }
+                    }
+                }
                 observeElementList(blockList);
                 //debug code end
 
+                PageBreakingAlgorithm alg;
+
+                if (isDeferredPart) {
+                    //updateLayoutContext(childLC);
+                    //deferred part from a previous iteration
+                    if (blockListIndex < numBlockLists - 1) {
+                        //'fake' the span change
+                        //switch current span
+                        childLC.signalSpanChange(blockList.span);
+                        //set next span
+                        BlockSequence nextBlockList = blockLists.get(blockListIndex + 1);
+                        log.debug("Override span change from " + blockList.span
+                                + " to " + nextBlockList.span);
+                        childLC.signalSpanChange(nextBlockList.span);
+                        handleSpanChange(childLC, nextBlockList.span);
+                    }
+                }
+
                 log.debug("PLM> start of algorithm (" + this.getClass().getName()
                         + "), flow BPD =" + flowBPD);
-                PageBreakingAlgorithm alg = new PageBreakingAlgorithm(getTopLevelLM(),
+                alg = new PageBreakingAlgorithm(getTopLevelLM(),
                         getPageProvider(), createLayoutListener(),
                         alignment, alignmentLast, footnoteSeparatorLength,
                         isPartOverflowRecoveryActivated(), autoHeight, isSinglePartFavored());
-                int iOptPageCount;
 
                 BlockSequence effectiveList;
                 if (getCurrentDisplayAlign() == Constants.EN_X_FILL) {
@@ -335,21 +439,53 @@
                     effectiveList = blockList;
                 }
 
-                //iOptPageCount = alg.firstFit(effectiveList, flowBPD, 1, true);
                 alg.setConstantLineWidth(flowBPD);
-                iOptPageCount = alg.findBreakingPoints(effectiveList, /*flowBPD,*/
+                int optimalPageCount = alg.findBreakingPoints(effectiveList, /*flowBPD,*/
                             1, true, BreakingAlgorithm.ALL_BREAKS);
-                log.debug("PLM> iOptPageCount= " + iOptPageCount
+                log.debug("PLM> optimalPageCount= " + optimalPageCount
                         + " pageBreaks.size()= " + alg.getPageBreaks().size());
 
+                if (isDeferredPart) {
+                    /* Phase 3: Add areas w/o deferral, no last page condition
+                     *          unless all parts are deferred */
+                    doPhase3(alg, optimalPageCount, blockList,
+                            effectiveList, allPartsDeferred, false);
+                } else {
+                    /* Phase 3: Add areas w/ deferral of the last part,
+                     *          no last page condition */
+                    doPhase3(alg, optimalPageCount, blockList,
+                            effectiveList, false, true);
+                }
 
-                //*** Phase 3: Add areas ***
-                doPhase3(alg, iOptPageCount, blockList, effectiveList);
+                //remember number of parts produced by the last block list
+                lastPageCount = optimalPageCount;
             }
+
+            if (deferredParts && lastPageCount > 1) {
+                //last block list produced more than one part:
+                //clear all deferred parts, and keep only the last one
+                BlockSequence lastDeferredPart = blockLists.getLast();
+                blockLists.clear();
+                blockLists.add(lastDeferredPart);
-        }
+            }
 
+            if (allPartsDeferred) {
+                //all deferred parts:
+                //clear all block lists
+                blockLists.clear();
+                //trigger exit loop
+                allPartsDeferred = false;
+            } else {
+                //if no more content, then all parts remaining
+                //in the block list are deferred
+                //TODO: Check, as this is insufficient (?)
+                allPartsDeferred = !hasMoreContent();
-    }
+            }
 
+        }
+
+    }
+
     /**
      * Phase 3 of Knuth algorithm: Adds the areas
      * @param alg PageBreakingAlgorithm instance which determined the breaks
@@ -366,7 +502,23 @@
      * @param partCount number of parts (pages) to be rendered
      * @param originalList original Knuth element list
      * @param effectiveList effective Knuth element list (after adjustments)
+     * @param isLastPage take into account last-page condition
+     * @param deferLastPart whether or not the part after the last break
+     *                      should be deferred
      */
+    protected void doPhase3(PageBreakingAlgorithm alg, int partCount,
+            BlockSequence originalList, BlockSequence effectiveList,
+            boolean isLastPage, boolean deferLastPart) {
+        //default: nop
+    }
+
+    /**
+     * Phase 3 of Knuth algorithm: Adds the areas
+     * @param alg PageBreakingAlgorithm instance which determined the breaks
+     * @param partCount number of parts (pages) to be rendered
+     * @param originalList original Knuth element list
+     * @param effectiveList effective Knuth element list (after adjustments)
+     */
     protected void addAreas(PageBreakingAlgorithm alg, int partCount,
             BlockSequence originalList, BlockSequence effectiveList) {
         addAreas(alg, 0, partCount, originalList, effectiveList);
@@ -556,13 +708,15 @@
                 nextSequenceStartsOn = handleSpanChange(childLC, nextSequenceStartsOn);
                 return nextSequenceStartsOn;
             }
-            blockList = new BlockSequence(nextSequenceStartsOn, getCurrentDisplayAlign());
+            blockList = new BlockSequence(nextSequenceStartsOn,
+                                getCurrentDisplayAlign(),
+                                childLC.getCurrentSpan());
 
             //Only implemented by the PSLM
             nextSequenceStartsOn = handleSpanChange(childLC, nextSequenceStartsOn);
 
             Position breakPosition = null;
-            if (((KnuthElement) ListUtil.getLast(returnedList)).isForcedBreak()) {
+            if (ElementListUtils.endsWithForcedBreak(returnedList)) {
                 KnuthPenalty breakPenalty = (KnuthPenalty) ListUtil
                         .removeLast(returnedList);
                 breakPosition = breakPenalty.getPosition();
@@ -812,7 +966,8 @@
         // Positions
         // which will be used in the addAreas() phase
         BlockSequence effectiveList = new BlockSequence(blockList.getStartOn(),
-                                                blockList.getDisplayAlign());
+                                                blockList.getDisplayAlign(),
+                                                blockList.getSpan());
         effectiveList.addAll(getCurrentChildLM().getChangedKnuthElements(
                 blockList.subList(0, blockList.size() - blockList.ignoreAtEnd),
                 /* 0, */0));
@@ -889,4 +1044,21 @@
         return adjustedDiff;
     }
 
+    /**
+     * Defer the part corresponding to the given block list until
+     * the next iteration.
+     *
+     * @param deferredPart  the block list for which the last phase is deferred
+     * @param originalList the originating block list
+     */
+    protected void deferPart(BlockSequence deferredPart, BlockSequence originalList) {
+        int index = this.blockLists.indexOf(originalList);
+        if (index >= 0) {
+            log.debug("replacing original list at position " + index);
+            this.blockLists.set(index, deferredPart);
+        } else {
+            log.debug("adding deferred list to start");
+            this.blockLists.add(0, deferredPart);
-}
+        }
+    }
+}
