]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Bugzilla #39777:
authorJeremias Maerki <jeremias@apache.org>
Mon, 31 Jul 2006 09:20:16 +0000 (09:20 +0000)
committerJeremias Maerki <jeremias@apache.org>
Mon, 31 Jul 2006 09:20:16 +0000 (09:20 +0000)
Initial support for fo:float with float="before" and float="none".
Submitted by: Vincent Hennebert <vincent.hennebert.at.enseeiht.fr>

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

29 files changed:
src/java/org/apache/fop/area/BeforeFloat.java
src/java/org/apache/fop/fo/FONode.java
src/java/org/apache/fop/fo/flow/Float.java
src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
src/java/org/apache/fop/layoutmgr/BalancingColumnBreakingAlgorithm.java
src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java
src/java/org/apache/fop/layoutmgr/FloatBodyLayoutManager.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/FootnoteBodyLayoutManager.java
src/java/org/apache/fop/layoutmgr/InlineKnuthSequence.java
src/java/org/apache/fop/layoutmgr/KnuthBlockBox.java
src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java
src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java
src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java
src/java/org/apache/fop/layoutmgr/breaking/OutOfLineRecord.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/inline/FloatLayoutManager.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/inline/KnuthInlineBox.java
src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java
status.xml
test/layoutengine/disabled-testcases.xml
test/layoutengine/standard-testcases/before-float_basic.xml [new file with mode: 0644]
test/layoutengine/standard-testcases/before-float_complex.xml [new file with mode: 0644]
test/layoutengine/standard-testcases/before-float_deferred_no-stretch.xml [new file with mode: 0644]
test/layoutengine/standard-testcases/before-float_deferred_stretch.xml [new file with mode: 0644]
test/layoutengine/standard-testcases/before-float_not-deferred_no-stretch_1.xml [new file with mode: 0644]
test/layoutengine/standard-testcases/before-float_not-deferred_no-stretch_2.xml [new file with mode: 0644]
test/layoutengine/standard-testcases/before-float_not-deferred_stretch.xml [new file with mode: 0644]
test/layoutengine/standard-testcases/float_float_none_1.xml [new file with mode: 0644]
test/layoutengine/standard-testcases/float_float_none_2.xml [new file with mode: 0644]

index 23883f1338bd82b35519447c0b234def7e3d1349..576e7296f339b056166826b40e4254517d90ea9f 100644 (file)
@@ -54,17 +54,22 @@ public class BeforeFloat extends BlockParent {
      *
      * @return the height of the before float including separator
      */
-    public int getBPD() {
-        int h = super.getBPD();
-        if (separator != null) {
-            h += separator.getBPD();
-        }
-        return h;
-    }
+//    public int getBPD() {
+//        int h = super.getBPD();
+//        if (separator != null) {
+//            h += separator.getBPD();
+//        }
+//        return h;
+//    }
 
-    /** @see org.apache.fop.area.BlockParent#isEmpty() */
-    public boolean isEmpty() {
-        return true; // before floats are not yet implemented
+    /**
+     * Add a block area as child to the footnote area
+     *
+     * @param child the block area.
+     */
+    public void addBlock(Block child) {
+        addChildArea(child);
+        this.setBPD(this.getBPD() + child.getBPD());
     }
 }
 
index 2fa53902e49c453ac47a95ce8854666e15598bd7..be041254fc38dba98b04c25c8940d8cd3ef9cb7f 100644 (file)
@@ -175,13 +175,14 @@ public abstract class FONode implements Cloneable {
 
     /**
      * Checks to make sure, during SAX processing of input document, that the
-     * incoming node is valid for the this (parent) node (e.g., checking to
+     * incoming node is a valid child of this node (e.g., checking to
      * see that fo:table is not an immediate child of fo:root)
      * called within FObj constructor
      * @param loc location in the FO source file
      * @param namespaceURI namespace of incoming node
-     * @param localName (e.g. "table" for "fo:table")
-     * @throws ValidationException if incoming node not valid for parent
+     * @param localName name of the incoming node (e.g. "table" for "fo:table")
+     * @throws ValidationException if incoming node is note a valid child of
+     * this node
      */
     protected void validateChildNode(Locator loc, String namespaceURI, String localName) 
             throws ValidationException {
index 3e720cb47be8e18198de78e7c5cd446836d158cb..e4305c893ab0c402a02daf26181c317febdde60e 100644 (file)
@@ -37,18 +37,21 @@ public class Float extends FObj {
     private int clear;
     // End of property values
 
-    static boolean notImplementedWarningGiven = false;
     
     /**
      * @see org.apache.fop.fo.FONode#FONode(FONode)
      */
     public Float(FONode parent) {
         super(parent);
-        
-        if (!notImplementedWarningGiven) {
-            getLogger().warn("fo:float is not yet implemented.");
-            notImplementedWarningGiven = true;
-        }
+    }
+
+
+    public int getClear() {
+        return clear;
+    }
+
+    public int getFloat() {
+        return float_;
     }
 
     /**
index c549be3ee8d6bfc429c76d3d21e3d6f7f0dd0d53..9e51715c6194e81b434610cd5a478e064b496115 100644 (file)
@@ -43,17 +43,22 @@ public abstract class AbstractBreaker {
         int footnoteFirstElementIndex;
         int footnoteLastListIndex;
         int footnoteLastElementIndex;
+        int floatFirstListIndex;
+        int floatLastListIndex;
 
         PageBreakPosition(LayoutManager lm, int iBreakIndex,
-                          int ffli, int ffei, int flli, int flei,
+                          int fofli, int fofei, int folli, int folei,
+                          int flfli, int fllli,
                           double bpdA, int diff) {
             super(lm, iBreakIndex);
             bpdAdjust = bpdA;
             difference = diff;
-            footnoteFirstListIndex = ffli;
-            footnoteFirstElementIndex = ffei;
-            footnoteLastListIndex = flli;
-            footnoteLastElementIndex = flei;
+            footnoteFirstListIndex = fofli;
+            footnoteFirstElementIndex = fofei;
+            footnoteLastListIndex = folli;
+            footnoteLastElementIndex = folei;
+            floatFirstListIndex = flfli;
+            floatLastListIndex = fllli;
         }
     }
 
@@ -163,6 +168,7 @@ public abstract class AbstractBreaker {
     private int alignmentLast;
 
     protected MinOptMax footnoteSeparatorLength = new MinOptMax(0);
+    protected MinOptMax floatSeparatorLength = new MinOptMax(0);
 
     protected abstract int getCurrentDisplayAlign();
     protected abstract boolean hasMoreContent();
@@ -311,7 +317,7 @@ public abstract class AbstractBreaker {
                         + "), flow BPD =" + flowBPD);
                 PageBreakingAlgorithm alg = new PageBreakingAlgorithm(getTopLevelLM(),
                         getPageProvider(),
-                        alignment, alignmentLast, footnoteSeparatorLength,
+                        alignment, alignmentLast, footnoteSeparatorLength, floatSeparatorLength,
                         isPartOverflowRecoveryActivated(), autoHeight, isSinglePartFavored());
                 int iOptPageCount;
 
index c917223608360c029934a051569797488e42537f..b82a6a6c45762d1da696504de5696555be1d7eca 100644 (file)
@@ -39,10 +39,11 @@ public class BalancingColumnBreakingAlgorithm extends PageBreakingAlgorithm {
             PageSequenceLayoutManager.PageProvider pageProvider,\r
             int alignment, int alignmentLast,\r
             MinOptMax footnoteSeparatorLength,\r
+            MinOptMax floatSeparatorLength,\r
             boolean partOverflowRecovery,\r
             int columnCount) {\r
         super(topLevelLM, pageProvider, alignment, alignmentLast, \r
-                footnoteSeparatorLength, partOverflowRecovery, false, false);\r
+                footnoteSeparatorLength, floatSeparatorLength, partOverflowRecovery, false, false);\r
         this.columnCount = columnCount;\r
         this.considerTooShort = true; //This is important!\r
     }\r
index 33f5f96ad91afbb742d256bd364e14f69009d771..70104a787b690727a303a242b4d735da53a6a0fe 100644 (file)
@@ -992,7 +992,7 @@ public abstract class BreakingAlgorithm {
      * @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) {
+    public void addNode(int line, KnuthNode node) {
         int headIdx = line * 2;
         if (headIdx >= activeLines.length) {
             KnuthNode[] oldList = activeLines;
@@ -1016,7 +1016,7 @@ public abstract class BreakingAlgorithm {
      * @param line number of the line ending at the node's corresponding breakpoint
      * @param node the node to deactivate
      */
-    protected void removeNode(int line, KnuthNode node) {
+    public void removeNode(int line, KnuthNode node) {
         int headIdx = line * 2;
         KnuthNode n = getNode(line);
         if (n != node) {
diff --git a/src/java/org/apache/fop/layoutmgr/FloatBodyLayoutManager.java b/src/java/org/apache/fop/layoutmgr/FloatBodyLayoutManager.java
new file mode 100644 (file)
index 0000000..c67c161
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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$ */
+
+package org.apache.fop.layoutmgr;
+
+import java.util.LinkedList;
+
+import org.apache.fop.area.Area;
+import org.apache.fop.fo.flow.Float;
+
+/**
+ * LayoutManager for the out-of-line area generated by fo:float nodes.
+ * @see org.apache.fop.layoutmgr.inline.FloatLayoutManager
+ */
+public class FloatBodyLayoutManager extends BlockStackingLayoutManager {
+
+    public FloatBodyLayoutManager(Float node) {
+        super(node);
+    }
+
+    /** @see org.apache.fop.layoutmgr.LayoutManager#addAreas(PositionIterator, LayoutContext)
+     * @deprecated */
+    public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
+        LayoutManager childLM = null;
+        LayoutManager lastLM = null;
+        LayoutContext lc = new LayoutContext(0);
+
+        // "unwrap" the NonLeafPositions stored in parentIter
+        // and put them in a new list;
+        LinkedList positionList = new LinkedList();
+        Position pos;
+        while (parentIter.hasNext()) {
+            pos = (Position) parentIter.next();
+            //log.trace("pos = " + pos.getClass().getName() + "; " + pos);
+            Position innerPosition = pos;
+            if (pos instanceof NonLeafPosition) {
+                innerPosition = ((NonLeafPosition) pos).getPosition();
+                if (innerPosition.getLM() == this) {
+                    // pos was created by this LM and was inside a penalty
+                    // allowing or forbidding a page break
+                    // nothing to do
+                    //log.trace(" penalty");
+                } else {
+                    // innerPosition was created by another LM
+                    positionList.add(innerPosition);
+                    lastLM = innerPosition.getLM();
+                    //log.trace(" " + innerPosition.getClass().getName());
+                }
+            }
+        }
+
+        // the Positions in positionList were inside the elements
+        // created by the LineLM
+        StackingIter childPosIter = new StackingIter(positionList.listIterator());
+
+        while ((childLM = childPosIter.getNextChildLM()) != null) {
+            // set last area flag
+            lc.setFlags(LayoutContext.LAST_AREA,
+                    (layoutContext.isLastArea() && childLM == lastLM));
+            // Add the line areas to Area
+            childLM.addAreas(childPosIter, lc);
+        }
+    }
+
+    /** @see org.apache.fop.layoutmgr.LayoutManager#addChildArea(org.apache.fop.area.Area) */
+    public void addChildArea(Area childArea) {
+        int floatProperty = ((Float) getFObj()).getFloat();  
+        if (floatProperty == EN_BEFORE) {
+            childArea.setAreaClass(Area.CLASS_BEFORE_FLOAT);
+        } else if (floatProperty == EN_START || floatProperty == EN_END) {
+            childArea.setAreaClass(Area.CLASS_SIDE_FLOAT);
+        } else {
+            childArea.setAreaClass(Area.CLASS_NORMAL);
+        }
+        parentLM.addChildArea(childArea);
+    }
+}
index 70f541b2d2795aff2742f3c2881db23175913030..5c2aa41b034256b396b7ff69a0f82f272102abdd 100644 (file)
@@ -37,7 +37,7 @@ public class FootnoteBodyLayoutManager extends BlockStackingLayoutManager {
         super(body);
     }
 
-    /** @see org.apache.fop.layoutmgr.LayoutManager */
+    /** @see org.apache.fop.layoutmgr.LayoutManager#addAreas(PositionIterator, LayoutContext) */
     public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
         LayoutManager childLM = null;
         LayoutManager lastLM = null;
index 839c3bd3559376b9f51bfc2568593dd5079a239e..b2cc0d3d18e66b124b69582917e0669f7d79517c 100644 (file)
@@ -148,6 +148,7 @@ public class InlineKnuthSequence extends KnuthSequence  {
             // from prevBox to the new box
             KnuthInlineBox newBox = (KnuthInlineBox) getLast();
             newBox.setFootnoteBodyLM(((KnuthInlineBox) prevBox).getFootnoteBodyLM());
+            newBox.setFloatBodyLM(((KnuthInlineBox) prevBox).getFloatBodyLM());
         }
     }
 
index 53f28537780ac8e47bf865224929147414cf86f7..415cd822e3d55a6936063ba71909627df62a7e08 100644 (file)
@@ -34,9 +34,20 @@ public class KnuthBlockBox extends KnuthBox {
      * it isn't possible to get the opt value stored in a MinOptMax object.
      */
     private int bpd;
-    private LinkedList footnoteList;
-    /** List of Knuth elements. This is a list of LinkedList elements. */
-    private LinkedList elementLists = null;
+    /** FootnoteBodyLayoutManagers corresponding to the footnotes cited on this line. */
+    private LinkedList footnoteLMList;
+    /** FloatBodyLayoutManagers corresponding to the floats cited on this line. */
+    private LinkedList floatLMList;
+    /**
+     * The Knuth sequences corresponding to the footnotes cited on this line. This is a List
+     * of List of KnuthElement objects.
+     */
+    private LinkedList footnoteElementLists = null;
+    /**
+     * The Knuth sequences corresponding to the floats cited on this line. This is a List
+     * of List of KnuthElement objects.
+     */
+    private LinkedList floatElementLists = null;
 
     /**
      * Creates a new box.
@@ -50,56 +61,96 @@ public class KnuthBlockBox extends KnuthBox {
         super(w, pos, bAux);
         ipdRange = (MinOptMax) range.clone();
         bpd = bpdim;
-        footnoteList = new LinkedList();
+        footnoteLMList = new LinkedList();
+        floatLMList = new LinkedList();
     }
 
     /**
      * Creates a new box.
      * @param w block progression dimension of this box
-     * @param list footnotes cited by elements in this box. The list contains the
-     * corresponding FootnoteBodyLayoutManagers 
+     * @param footnoteLMList footnotes cited by elements in this box. The list contains
+     * the corresponding FootnoteBodyLayoutManagers 
+     * @param floatLMList floats cited by elements in this box. The list contains the
+     * corresponding FloatBodyLayoutManagers 
      * @param pos the Position stored in this box
      * @param bAux is this box auxiliary?
      */
-    public KnuthBlockBox(int w, LinkedList list, Position pos, boolean bAux) {
+    public KnuthBlockBox(int w,
+                         LinkedList footnoteLMList,
+                         LinkedList floatLMList,
+                         Position pos,
+                         boolean bAux) {
         super(w, pos, bAux);
         ipdRange = new MinOptMax(0);
         bpd = 0;
-        footnoteList = new LinkedList(list);
+        this.footnoteLMList = new LinkedList(footnoteLMList);
+        this.floatLMList = new LinkedList(floatLMList);
     }
 
     /**
      * @return the LMs for the footnotes cited in this box.
      */
     public LinkedList getFootnoteBodyLMs() {
-        return footnoteList;
+        return footnoteLMList;
+    }
+
+    /**
+     * @return the LMs for the floats cited in this box.
+     */
+    public LinkedList getFloatBodyLMs() {
+        return floatLMList;
     }
 
     /**
      * @return true if this box contains footnote citations.
      */
-    public boolean hasAnchors() {
-        return (footnoteList.size() > 0);
+    public boolean hasFootnoteAnchors() {
+        return (footnoteLMList.size() > 0);
+    }
+
+    /**
+     * @return true if this box contains float citations.
+     */
+    public boolean hasFloatAnchors() {
+        return (floatLMList.size() > 0);
+    }
+
+    /**
+     * Adds a footnote to this box's list of footnotes.
+     * @param list KnuthElement instances corresponding to the footnote body
+     */
+    public void addFootnoteElementList(LinkedList list) {
+        if (footnoteElementLists == null) {
+            footnoteElementLists = new LinkedList();
+        }
+        footnoteElementLists.add(list);
+    }
+
+    /**
+     * Returns the list of footnotes cited by this box.
+     * @return a list of KnuthElement sequences corresponding to the footnote bodies
+     */
+    public LinkedList getFootnoteElementLists() {
+        return footnoteElementLists;
     }
 
     /**
-     * Adds the given list of Knuth elements to this box' list of elements.
-     * @param list elements corresponding to a footnote body
+     * Adds a float to this box's list of floats.
+     * @param list KnuthElement instances corresponding to the float body
      */
-    public void addElementList(LinkedList list) {
-        if (elementLists == null) {
-            elementLists = new LinkedList();
+    public void addFloatElementList(LinkedList list) {
+        if (floatElementLists == null) {
+            floatElementLists = new LinkedList();
         }
-        elementLists.add(list);
+        floatElementLists.add(list);
     }
 
     /**
-     * Returns the list of Knuth sequences registered by this box.
-     * @return a list of KnuthElement sequences corresponding to footnotes cited in this
-     * box
+     * Returns the list of floats cited by this box.
+     * @return a list of KnuthElement sequences corresponding to the float bodies
      */
-    public LinkedList getElementLists() {
-        return elementLists;
+    public LinkedList getFloatElementLists() {
+        return floatElementLists;
     }
 
     /**
index db4663df6d024e79cef56f829247a161eb91824e..9fe558e64e6d11f429d5c2c9efb0618199b20d1a 100644 (file)
@@ -28,6 +28,7 @@ import java.util.Iterator;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
+import org.apache.fop.fo.Constants;
 import org.apache.fop.fo.FONode;
 import org.apache.fop.fo.FOText;
 import org.apache.fop.fo.FObjMixed;
@@ -37,6 +38,7 @@ import org.apache.fop.fo.flow.Block;
 import org.apache.fop.fo.flow.BlockContainer;
 import org.apache.fop.fo.flow.Character;
 import org.apache.fop.fo.flow.ExternalGraphic;
+import org.apache.fop.fo.flow.Float;
 import org.apache.fop.fo.flow.Footnote;
 import org.apache.fop.fo.flow.Inline;
 import org.apache.fop.fo.flow.InlineLevel;
@@ -69,6 +71,7 @@ import org.apache.fop.layoutmgr.inline.BidiLayoutManager;
 import org.apache.fop.layoutmgr.inline.CharacterLayoutManager;
 import org.apache.fop.layoutmgr.inline.ContentLayoutManager;
 import org.apache.fop.layoutmgr.inline.ExternalGraphicLayoutManager;
+import org.apache.fop.layoutmgr.inline.FloatLayoutManager;
 import org.apache.fop.layoutmgr.inline.FootnoteLayoutManager;
 import org.apache.fop.layoutmgr.inline.ICLayoutManager;
 import org.apache.fop.layoutmgr.inline.InlineLayoutManager;
@@ -109,6 +112,7 @@ public class LayoutManagerMapping implements LayoutManagerMaker {
         makers.put(BidiOverride.class, new BidiOverrideLayoutManagerMaker());
         makers.put(Inline.class, new InlineLayoutManagerMaker());
         makers.put(Footnote.class, new FootnodeLayoutManagerMaker());
+        makers.put(Float.class, new FloatLayoutManagerMaker());
         makers.put(InlineContainer.class,
                    new InlineContainerLayoutManagerMaker());
         makers.put(BasicLink.class, new BasicLinkLayoutManagerMaker());
@@ -269,6 +273,16 @@ public class LayoutManagerMapping implements LayoutManagerMaker {
          }
     }
 
+    public static class FloatLayoutManagerMaker extends Maker {
+        public void make(FONode node, List lms) {
+            if (((Float) node).getFloat() == Constants.EN_NONE) {
+                lms.add(new FloatBodyLayoutManager((Float) node));
+            } else {
+                lms.add(new FloatLayoutManager((Float) node));
+            }
+        }
+    }
+
     public static class FootnodeLayoutManagerMaker extends Maker {
         public void make(FONode node, List lms) {
             lms.add(new FootnoteLayoutManager((Footnote) node));
index 3910fa0234ab37f1f6a918e00f91a90c52617c41..30d30f4a941bb124a178c404d30ccc3c36a7e25f 100644 (file)
@@ -19,9 +19,7 @@
 
 package org.apache.fop.layoutmgr;
 
-import java.util.ArrayList;
 import java.util.LinkedList;
-import java.util.ListIterator;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -29,10 +27,11 @@ import org.apache.fop.fo.Constants;
 import org.apache.fop.fo.FONode;
 import org.apache.fop.fo.FObj;
 import org.apache.fop.layoutmgr.AbstractBreaker.PageBreakPosition;
+import org.apache.fop.layoutmgr.breaking.OutOfLineRecord;
 
 import org.apache.fop.traits.MinOptMax;
 
-class PageBreakingAlgorithm extends BreakingAlgorithm {
+public class PageBreakingAlgorithm extends BreakingAlgorithm {
 
     /** the logger for the class */
     protected static Log classLog = LogFactory.getLog(PageBreakingAlgorithm.class);
@@ -42,42 +41,14 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
     /** List of PageBreakPosition elements. */
     private LinkedList pageBreaks = null;
 
-    /** Footnotes which are cited between the currently considered active node (previous
-     * break) and the current considered break. Its type is
-     * List&lt;List&lt;KnuthElement&gt;&gt;, it contains the sequences of KnuthElement
-     * representing the footnotes bodies.
-     */
-    private ArrayList footnotesList = null;
-    /** Cumulated bpd of unhandled footnotes. */
-    private ArrayList lengthList = null;
-    /** Length of all the footnotes which will be put on the current page. */
-    private int totalFootnotesLength = 0;
-    /**
-     * Length of all the footnotes which have already been inserted, up to the currently
-     * considered element. That is, footnotes from the currently considered page plus
-     * footnotes from its preceding pages.
-     */
-    private int insertedFootnotesLength = 0;
-    /** True if footnote citations have been met since the beginning of the page sequence. */
-    private boolean footnotesPending = false;
-    /**
-     * True if the elements met after the previous break point contain footnote citations.
-     */
-    private boolean newFootnotes = false;
-    /**
-     * Index of the first footnote met after the previous break point.
-     */
-    private int firstNewFootnoteIndex = 0;
-    /** Index of the last footnote inserted on the current page. */
-    private int footnoteListIndex = 0;
-    /** Index of the last element of the last footnote inserted on the current page. */
-    private int footnoteElementIndex = -1;
+    private OutOfLineRecord footnotes;
+    private OutOfLineRecord floats;
 
     // demerits for a page break that splits a footnote 
     private int splitFootnoteDemerits = 5000;
     // demerits for a page break that defers a whole footnote to the following page 
     private int deferredFootnoteDemerits = 10000;
-    private MinOptMax footnoteSeparatorLength = null;
+    private int deferredFloatDemerits = 10000;
 
     // the method noBreakBetween(int, int) uses these variables 
     // to store parameters and result of the last call, in order
@@ -95,7 +66,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
     public PageBreakingAlgorithm(LayoutManager topLevelLM,
                                  PageSequenceLayoutManager.PageProvider pageProvider,
                                  int alignment, int alignmentLast,
-                                 MinOptMax footnoteSeparatorLength,
+                                 MinOptMax footnoteSeparatorLength, MinOptMax floatSeparatorLength,
                                  boolean partOverflowRecovery, boolean autoHeight,
                                  boolean favorSinglePart) {
         super(alignment, alignmentLast, true, partOverflowRecovery, 0);
@@ -103,7 +74,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
         this.topLevelLM = topLevelLM;
         this.pageProvider = pageProvider;
         best = new BestPageRecords();
-        this.footnoteSeparatorLength = (MinOptMax) footnoteSeparatorLength.clone();
+        footnotes = new OutOfLineRecord((MinOptMax) footnoteSeparatorLength.clone());
+        floats = new OutOfLineRecord((MinOptMax) floatSeparatorLength.clone());
         // add some stretch, to avoid a restart for every page containing footnotes
         if (footnoteSeparatorLength.min == footnoteSeparatorLength.max) {
             footnoteSeparatorLength.max += 10000;
@@ -116,29 +88,23 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
      * This class represents a feasible breaking point
      * with extra information about footnotes.
      */
-    protected class KnuthPageNode extends KnuthNode {
-
-        /** Additional length due to footnotes. */
-        public int totalFootnotes;
-
-        /** Index of the last inserted footnote. */
-        public int footnoteListIndex;
+    public class KnuthPageNode extends KnuthNode {
 
-        /** Index of the last inserted element of the last inserted footnote. */
-        public int footnoteElementIndex;
+        public OutOfLineRecord.ProgressInfo footnotesProgress;
+        public OutOfLineRecord.ProgressInfo floatsProgress;
 
         public KnuthPageNode(int position, int line, int fitness,
                              int totalWidth, int totalStretch, int totalShrink,
-                             int totalFootnotes, int footnoteListIndex, int footnoteElementIndex,
+                             OutOfLineRecord.ProgressInfo footnotesProgress,
+                             OutOfLineRecord.ProgressInfo floatsProgress,
                              double adjustRatio, int availableShrink, int availableStretch,
                              int difference, double totalDemerits, KnuthNode previous) {
             super(position, line, fitness,
                   totalWidth, totalStretch, totalShrink,
                   adjustRatio, availableShrink, availableStretch,
                   difference, totalDemerits, previous);
-            this.totalFootnotes = totalFootnotes;
-            this.footnoteListIndex = footnoteListIndex;
-            this.footnoteElementIndex = footnoteElementIndex;
+            this.footnotesProgress = footnotesProgress.copy();
+            this.floatsProgress = floatsProgress.copy();
         }
 
     }
@@ -149,48 +115,55 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
      */
     protected class BestPageRecords extends BestRecords {
 
-        private int[] bestFootnotesLength = new int[4];
-        private int[] bestFootnoteListIndex = new int[4];
-        private int[] bestFootnoteElementIndex = new int[4];
-
+        private OutOfLineRecord.ProgressInfo[] bestFootnotesProgress
+                = new OutOfLineRecord.ProgressInfo[4];
+        private OutOfLineRecord.ProgressInfo[] bestFloatsProgress
+                = new OutOfLineRecord.ProgressInfo[4];
+        
         public void addRecord(double demerits, KnuthNode node, double adjust,
                               int availableShrink, int availableStretch,
                               int difference, int fitness) {
             super.addRecord(demerits, node, adjust,
                             availableShrink, availableStretch,
                             difference, fitness);
-            bestFootnotesLength[fitness] = insertedFootnotesLength;
-            bestFootnoteListIndex[fitness] = footnoteListIndex;
-            bestFootnoteElementIndex[fitness] = footnoteElementIndex;
+            bestFootnotesProgress[fitness] = footnotes.getProgress().copy();
+            bestFloatsProgress[fitness] = floats.getProgress().copy();
         }
 
         public int getFootnotesLength(int fitness) {
-            return bestFootnotesLength[fitness];
+            return bestFootnotesProgress[fitness].getAlreadyInsertedLength();
         }
 
         public int getFootnoteListIndex(int fitness) {
-            return bestFootnoteListIndex[fitness];
+            return bestFootnotesProgress[fitness].getLastInsertedIndex();
         }
 
         public int getFootnoteElementIndex(int fitness) {
-            return bestFootnoteElementIndex[fitness];
+            return bestFootnotesProgress[fitness].getLastElementOfLastInsertedIndex();
+        }
+
+        public OutOfLineRecord.ProgressInfo getFootnoteProgress(int fitness) {
+            return bestFootnotesProgress[fitness];
+        }
+
+        public OutOfLineRecord.ProgressInfo getFloatProgress(int fitness) {
+            return bestFloatsProgress[fitness];
         }
     }
 
     protected void initialize() {
         super.initialize();
-        insertedFootnotesLength = 0;
-        footnoteListIndex = 0;
-        footnoteElementIndex = -1;
+        footnotes.initialize();
+        floats.initialize();
     }
 
-    protected KnuthNode createNode(int position, int line, int fitness,
+    public KnuthNode createNode(int position, int line, int fitness,
                                    int totalWidth, int totalStretch, int totalShrink,
                                    double adjustRatio, int availableShrink, int availableStretch,
                                    int difference, double totalDemerits, KnuthNode previous) {
         return new KnuthPageNode(position, line, fitness,
                                  totalWidth, totalStretch, totalShrink,
-                                 insertedFootnotesLength, footnoteListIndex, footnoteElementIndex,
+                                 footnotes.getProgress(), floats.getProgress(),
                                  adjustRatio, availableShrink, availableStretch,
                                  difference, totalDemerits, previous);
     }
@@ -199,9 +172,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
                                    int totalWidth, int totalStretch, int totalShrink) {
         return new KnuthPageNode(position, line, fitness,
                                  totalWidth, totalStretch, totalShrink,
-                                 ((BestPageRecords) best).getFootnotesLength(fitness),
-                                 ((BestPageRecords) best).getFootnoteListIndex(fitness),
-                                 ((BestPageRecords) best).getFootnoteElementIndex(fitness), 
+                                 ((BestPageRecords) best).getFootnoteProgress(fitness),
+                                 ((BestPageRecords) best).getFloatProgress(fitness),
                                  best.getAdjust(fitness), best.getAvailableShrink(fitness),
                                  best.getAvailableStretch(fitness), best.getDifference(fitness),
                                  best.getDemerits(fitness), best.getNode(fitness));
@@ -214,174 +186,171 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
      */
     protected void handleBox(KnuthBox box) {
         if (box instanceof KnuthBlockBox
-            && ((KnuthBlockBox) box).hasAnchors()) {
-            handleFootnotes(((KnuthBlockBox) box).getElementLists());
-            if (!newFootnotes) {
-                newFootnotes = true;
-                firstNewFootnoteIndex = footnotesList.size() - 1;
-            }
+                && ((KnuthBlockBox) box).hasFootnoteAnchors()) {
+            footnotes.add(((KnuthBlockBox) box).getFootnoteElementLists());
         }
-    }
-
-    /**
-     * Handles the footnotes cited inside a block-level box. Updates footnotesList and the
-     * value of totalFootnotesLength with the lengths of the given footnotes.
-     * @param elementLists list of KnuthElement sequences corresponding to the footnotes
-     * bodies
-     */
-    private void handleFootnotes(LinkedList elementLists) {
-        // initialization
-        if (!footnotesPending) {
-            footnotesPending = true;
-            footnotesList = new ArrayList();
-            lengthList = new ArrayList();
-            totalFootnotesLength = 0;
-        }
-        if (!newFootnotes) {
-            newFootnotes = true;
-            firstNewFootnoteIndex = footnotesList.size();
-        }
-
-        // compute the total length of the footnotes
-        ListIterator elementListsIterator = elementLists.listIterator();
-        while (elementListsIterator.hasNext()) {
-            LinkedList noteList = (LinkedList) elementListsIterator.next();
-            
-            //Space resolution (Note: this does not respect possible stacking constraints 
-            //between footnotes!)
-            SpaceResolver.resolveElementList(noteList);
-            
-            int noteLength = 0;
-            footnotesList.add(noteList);
-            ListIterator noteListIterator = noteList.listIterator();
-            while (noteListIterator.hasNext()) {
-                KnuthElement element = (KnuthElement) noteListIterator.next();
-                if (element.isBox() || element.isGlue()) {
-                    noteLength += element.getW();
-                }
-            }
-            int prevLength = (lengthList.size() == 0 
-                    ? 0 
-                    : ((Integer) lengthList.get(lengthList.size() - 1)).intValue());
-            lengthList.add(new Integer(prevLength + noteLength));
-            totalFootnotesLength += noteLength;
+        if (box instanceof KnuthBlockBox
+                && ((KnuthBlockBox) box).hasFloatAnchors()) {
+            floats.add(((KnuthBlockBox) box).getFloatElementLists());
         }
     }
 
+
     protected int restartFrom(KnuthNode restartingNode, int currentIndex) {
         int returnValue = super.restartFrom(restartingNode, currentIndex);
-        newFootnotes = false;
-        if (footnotesPending) {
+        footnotes.resetNewSinceLastBreakpoint();
+        floats.resetNewSinceLastBreakpoint();
+        if (footnotes.existing() || floats.existing()) {
             // remove from footnotesList the note lists that will be met
             // after the restarting point
             for (int j = currentIndex; j >= restartingNode.position; j--) {
                 KnuthElement resettedElement = getElement(j);
                 if (resettedElement instanceof KnuthBlockBox
-                    && ((KnuthBlockBox) resettedElement).hasAnchors()) {
-                    resetFootnotes(((KnuthBlockBox) resettedElement).getElementLists());
+                        && ((KnuthBlockBox) resettedElement).hasFootnoteAnchors()) {
+                    footnotes.reset(((KnuthBlockBox) resettedElement).getFootnoteElementLists());
+                }
+                if (resettedElement instanceof KnuthBlockBox
+                        && ((KnuthBlockBox) resettedElement).hasFloatAnchors()) {
+                    floats.reset(((KnuthBlockBox) resettedElement).getFloatElementLists());//TODO
                 }
             }
         }
         return returnValue;
     }
 
-    private void resetFootnotes(LinkedList elementLists) {
-        for (int i = 0; i < elementLists.size(); i++) {
-            LinkedList removedList = (LinkedList) footnotesList.remove(footnotesList.size() - 1);
-            lengthList.remove(lengthList.size() - 1);
-
-            // update totalFootnotesLength
-            if (lengthList.size() > 0) {
-                totalFootnotesLength = ((Integer) lengthList.get(lengthList.size() - 1)).intValue();
-            } else {
-                totalFootnotesLength = 0;
-            }
-        }
-        // update footnotesPending;
-        if (footnotesList.size() == 0) {
-            footnotesPending = false;
-        }
-    }
-
     protected void considerLegalBreak(KnuthElement element, int elementIdx) {
         super.considerLegalBreak(element, elementIdx);
-        newFootnotes = false;
+        footnotes.resetNewSinceLastBreakpoint();
+        floats.resetNewSinceLastBreakpoint();
     }
 
     protected int computeDifference(KnuthNode activeNode, KnuthElement element,
                                     int elementIndex) {
         KnuthPageNode pageNode = (KnuthPageNode) activeNode;
         int actualWidth = totalWidth - pageNode.totalWidth;
-        int footnoteSplit;
-        boolean canDeferOldFootnotes;
         if (element.isPenalty()) {
             actualWidth += element.getW();
         }
-        if (footnotesPending) {
+        if (footnotes.existing()) {
+            footnotes.setProgress(pageNode.footnotesProgress);
             // compute the total length of the footnotes not yet inserted
-            int allFootnotes = totalFootnotesLength - pageNode.totalFootnotes;
+            int allFootnotes = footnotes.getTotalLength()
+                    - pageNode.footnotesProgress.getAlreadyInsertedLength();
             if (allFootnotes > 0) {
                 // this page contains some footnote citations
                 // add the footnote separator width
-                actualWidth += footnoteSeparatorLength.opt;
+                actualWidth += footnotes.getSeparatorLength().opt;
                 if (actualWidth + allFootnotes <= getLineWidth()) {
                     // there is enough space to insert all footnotes:
                     // add the whole allFootnotes length
                     actualWidth += allFootnotes;
-                    insertedFootnotesLength = pageNode.totalFootnotes + allFootnotes;
-                    footnoteListIndex = footnotesList.size() - 1;
-                    footnoteElementIndex = ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1;
-                } else if (((canDeferOldFootnotes = checkCanDeferOldFootnotes(pageNode, elementIndex))
-                            || newFootnotes)
-                           && (footnoteSplit = getFootnoteSplit(pageNode, getLineWidth() - actualWidth,
-                                                                canDeferOldFootnotes)) > 0) {
-                    // it is allowed to break or even defer footnotes if either:
-                    //  - there are new footnotes in the last piece of content, and
-                    //    there is space to add at least a piece of the first one
-                    //  - or the previous page break deferred some footnote lines, and
-                    //    this is the first feasible break; in this case it is allowed
-                    //    to break and defer, if necessary, old and new footnotes
-                    actualWidth += footnoteSplit;
-                    insertedFootnotesLength = pageNode.totalFootnotes + footnoteSplit;
-                    // footnoteListIndex has been set in getFootnoteSplit()
-                    // footnoteElementIndex has been set in getFootnoteSplit()
+                    footnotes.insertAll();
                 } else {
-                    // there is no space to add the smallest piece of footnote,
-                    // or we are trying to add a piece of content with no footnotes and
-                    // it does not fit in the page, because of previous footnote bodies
-                    // that cannot be broken:
-                    // add the whole allFootnotes length, so this breakpoint will be discarded
-                    actualWidth += allFootnotes;
-                    insertedFootnotesLength = pageNode.totalFootnotes + allFootnotes;
-                    footnoteListIndex = footnotesList.size() - 1;
-                    footnoteElementIndex = ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1;
+                    boolean canDeferOldFootnotes = checkCanDeferOldOutOfLines(footnotes,
+                            pageNode.position, elementIndex);
+                    int footnoteSplit;
+                    if ((canDeferOldFootnotes || footnotes.newSinceLastBreakpoint())
+                            && (footnoteSplit = footnotes.getFootnoteSplit(
+                                    pageNode.footnotesProgress,
+                                    getLineWidth() - actualWidth, canDeferOldFootnotes)) > 0) {
+                        // it is allowed to break or even defer footnotes if either:
+                        //  - there are new footnotes in the last piece of content, and
+                        //    there is space to add at least a piece of the first one
+                        //  - or the previous page break deferred some footnote lines, and
+                        //    this is the first feasible break; in this case it is allowed
+                        //    to break and defer, if necessary, old and new footnotes
+                        actualWidth += footnoteSplit;
+                    } else {
+                        // there is no space to add the smallest piece of footnote,
+                        // or we are trying to add a piece of content with no footnotes and
+                        // it does not fit in the page, because of previous footnote bodies
+                        // that cannot be broken:
+                        // add the whole allFootnotes length, so this breakpoint will be discarded
+                        actualWidth += allFootnotes;
+                        footnotes.insertAll();
+                    }
+                }
+            } // else: all footnotes have already been placed on previous pages
+        }
+        if (floats.existing()) {
+            floats.setProgress(pageNode.floatsProgress);
+            // compute the total length of the floats not yet inserted
+            int allFloats = floats.getTotalLength()
+                    - pageNode.floatsProgress.getAlreadyInsertedLength();
+            if (allFloats > 0
+                    && getLineWidth() - actualWidth - floats.getSeparatorLength().opt > 0) {
+                // this page contains some float citations
+                // add the float separator width
+                int split = floats.getFloatSplit(pageNode.floatsProgress,
+                        getLineWidth() - actualWidth - floats.getSeparatorLength().opt);
+                if (split > 0) {
+                    actualWidth += floats.getSeparatorLength().opt + split;
                 }
-            } else {
-                // all footnotes have already been placed on previous pages
             }
-        } else {
-            // there are no footnotes
         }
+        /* Another algorithm exactly mimicing the handling of footnotes: it should force
+         * more floats to be on the same page as their citations, at the price of more
+         * underfull pages (thus a higher total number of pages). If the current method
+         * works well enough, we may keep it.
+         */
+//      if (floats.existing()) {
+//          floats.setProgress(pageNode.floatsProgress);
+//          // compute the total length of the floats not yet inserted
+//          int allFloats = floats.getTotalLength()
+//                  - pageNode.floatsProgress.getAlreadyInsertedLength();
+//          if (allFloats > 0) {
+//              // this page contains some float citations
+//              // add the float separator width
+//              actualWidth += floats.getSeparatorLength().opt;
+//              if (actualWidth + allFloats <= getLineWidth()) {
+//                  // there is enough space to insert all floats:
+//                  // add the whole allFloats length
+//                  actualWidth += allFloats;
+//                  floats.insertAll();
+//              } else {
+//                  boolean canDeferOldFloats = checkCanDeferOldOutOfLines(floats,
+//                          pageNode.position, elementIndex);
+//                  int floatSplit;
+//                  if ((canDeferOldFloats || floats.newSinceLastBreakpoint())
+//                          && (floatSplit = floats.getFloatSplit(
+//                                  pageNode.floatsProgress,
+//                                  getLineWidth() - actualWidth)) > 0) {
+//                      actualWidth += floatSplit;
+//                  } else {
+//                      actualWidth += allFloats;
+//                      floats.insertAll();
+//                  }
+//              }
+//          } // else: all floats have already been placed on previous pages
+//      }
         return getLineWidth(activeNode.line) - actualWidth;
     }
 
-    /** Checks whether footnotes from preceding pages may be deferred to the page after
-     * the given element.
-     * @param node active node for the preceding page break
-     * @param contentElementIndex index of the Knuth element considered for the
-     * current page break
+    /**
+     * Checks whether out-of-line objects from preceding pages may be deferred
+     * to the page after the given element.
+     * 
+     * @param outOfLine informations about the out-of-line objects
+     * @param activeNodePosition index in the Knuth sequence of the currently considered
+     * active node
+     * @param contentElementIndex index in the Knuth sequence of the currently considered
+     * legal breakpoint
+     * @return <code>true</code> if it is allowed to defer some out-of-line objects on
+     * following pages
      */
-    private boolean checkCanDeferOldFootnotes(KnuthPageNode node, int contentElementIndex) {
-        return (noBreakBetween(node.position, contentElementIndex)
-                && deferredFootnotes(node.footnoteListIndex, node.footnoteElementIndex, node.totalFootnotes));
+    private boolean checkCanDeferOldOutOfLines(OutOfLineRecord outOfLine,
+                                               int activeNodePosition,
+                                               int contentElementIndex) {
+        return (noBreakBetween(activeNodePosition, contentElementIndex)
+                && outOfLine.deferred());
     }
 
     /**
-     * Returns true if there may be no breakpoint between the two given elements.
+     * Returns true if there is no legal breakpoint between the two given elements.
      * @param prevBreakIndex index of the element from the currently considered active
      * node
      * @param breakIndex index of the currently considered breakpoint
-     * @return true if no element between the two can be a breakpoint
+     * @return true if no element between the two is a legal breakpoint
      */
     private boolean noBreakBetween(int prevBreakIndex, int breakIndex) {
         // this method stores the parameters and the return value from previous calls
@@ -426,163 +395,17 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
         return storedValue;
     }
 
-    /**
-     * Returns true if their are (pieces of) footnotes to be typeset on the current page.
-     * @param listIndex index of the last inserted footnote for the currently considered
-     * active node
-     * @param elementIndex index of the last element of the last inserted footnote
-     * @param length total length of all footnotes inserted so far
-     */
-    private boolean deferredFootnotes(int listIndex, int elementIndex, int length) {
-        return ((newFootnotes
-                 && firstNewFootnoteIndex != 0
-                 && (listIndex < firstNewFootnoteIndex - 1
-                     || elementIndex < ((LinkedList) footnotesList.get(listIndex)).size() - 1))
-                || length < totalFootnotesLength);
-    }
-
-    /**
-     * Tries to split the flow of footnotes to put one part on the current page.
-     * @param activeNode currently considered previous page break
-     * @param availableLength available space for footnotes
-     * @param canDeferOldFootnotes
-     */
-    private int getFootnoteSplit(KnuthPageNode activeNode, int availableLength, boolean canDeferOldFootnotes) {
-        return getFootnoteSplit(activeNode.footnoteListIndex,
-                                activeNode.footnoteElementIndex,
-                                activeNode.totalFootnotes,
-                                availableLength, canDeferOldFootnotes);
-    }
-
-    /**
-     * Tries to split the flow of footnotes to put one part on the current page.
-     * @param prevListIndex index of the last footnote on the previous page
-     * @param prevElementIndex index of the last element of the last footnote
-     * @param prevLength total length of footnotes inserted so far
-     * @param availableLength available space for footnotes on this page
-     * @param canDeferOldFootnotes
-     */
-    private int getFootnoteSplit(int prevListIndex, int prevElementIndex, int prevLength,
-                                 int availableLength, boolean canDeferOldFootnotes) {
-        if (availableLength <= 0) {
-            return 0;
-        } else {
-            // the split should contain a piece of the last footnote
-            // together with all previous, not yet inserted footnotes;
-            // but if this is not possible, try adding as much content as possible
-            int splitLength = 0;
-            ListIterator noteListIterator = null;
-            KnuthElement element = null;
-            boolean somethingAdded = false;
-
-            // prevListIndex and prevElementIndex points to the last footnote element
-            // already placed in a page: advance to the next element
-            int listIndex = prevListIndex;
-            int elementIndex = prevElementIndex;
-            if (elementIndex == ((LinkedList) footnotesList.get(listIndex)).size() - 1) {
-                listIndex++;
-                elementIndex = 0;
-            } else {
-                elementIndex++;
-            }
-
-            // try adding whole notes
-            if (footnotesList.size() - 1 > listIndex) {
-                // add the previous footnotes: these cannot be broken or deferred
-                if (!canDeferOldFootnotes
-                    && newFootnotes
-                    && firstNewFootnoteIndex > 0) {
-                    splitLength = ((Integer) lengthList.get(firstNewFootnoteIndex - 1)).intValue()
-                                  - prevLength;
-                    listIndex = firstNewFootnoteIndex;
-                    elementIndex = 0;
-                }
-                // try adding the new footnotes
-                while (((Integer) lengthList.get(listIndex)).intValue() - prevLength
-                       <= availableLength) {
-                    splitLength = ((Integer) lengthList.get(listIndex)).intValue()
-                                  - prevLength;
-                    somethingAdded = true;
-                    listIndex++;
-                    elementIndex = 0;
-                }
-                // as this method is called only if it is not possible to insert
-                // all footnotes, at this point listIndex and elementIndex points to
-                // an existing element, the next one we will try to insert 
-            }
-
-            // try adding a split of the next note
-            noteListIterator = ((LinkedList) footnotesList.get(listIndex)).listIterator(elementIndex);
-
-            int prevSplitLength = 0;
-            int prevIndex = -1;
-            int index = -1;
-
-            while (!(somethingAdded && splitLength > availableLength)) {
-                if (!somethingAdded) {
-                    somethingAdded = true;
-                } else {
-                    prevSplitLength = splitLength;
-                    prevIndex = index;
-                }
-                // get a sub-sequence from the note element list
-                boolean bPrevIsBox = false;
-                while (noteListIterator.hasNext()) {
-                    // as this method is called only if it is not possible to insert
-                    // all footnotes, and we have already tried (and failed) to insert
-                    // this whole footnote, the while loop will never reach the end
-                    // of the note sequence
-                    element = (KnuthElement) noteListIterator.next();
-                    if (element.isBox()) {
-                        // element is a box
-                        splitLength += element.getW();
-                        bPrevIsBox = true;
-                    } else if (element.isGlue()) {
-                        // element is a glue
-                        if (bPrevIsBox) {
-                            // end of the sub-sequence
-                            index = noteListIterator.previousIndex();
-                            break;
-                        }
-                        bPrevIsBox = false;
-                        splitLength += element.getW();
-                    } else {
-                        // element is a penalty
-                        if (element.getP() < KnuthElement.INFINITE) {
-                            // end of the sub-sequence
-                            index = noteListIterator.previousIndex();
-                            break;
-                        }
-                    }
-                }
-            }
-            // if prevSplitLength is 0, this means that the available length isn't enough
-            // to insert even the smallest split of the last footnote, so we cannot end a
-            // page here
-            // if prevSplitLength is > 0 we can insert some footnote content in this page
-            // and insert the remaining in the following one
-            if (!somethingAdded) {
-                // there was not enough space to add a piece of the first new footnote
-                // this is not a good break
-                prevSplitLength = 0;
-            } else if (prevSplitLength > 0) {
-                // prevIndex is -1 if we have added only some whole footnotes
-                footnoteListIndex = (prevIndex != -1) ? listIndex : listIndex - 1;
-                footnoteElementIndex = (prevIndex != -1)
-                    ? prevIndex 
-                    : ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1;
-            }
-            return prevSplitLength;
-        }
-    }
-
     protected double computeAdjustmentRatio(KnuthNode activeNode, int difference) {
         // compute the adjustment ratio
         if (difference > 0) {
             int maxAdjustment = totalStretch - activeNode.totalStretch;
             // add the footnote separator stretch if some footnote content will be added
-            if (((KnuthPageNode) activeNode).totalFootnotes < totalFootnotesLength) {
-                maxAdjustment += footnoteSeparatorLength.max - footnoteSeparatorLength.opt;
+            if (((KnuthPageNode) activeNode).footnotesProgress.getAlreadyInsertedLength() < footnotes.getTotalLength()) {
+                maxAdjustment += footnotes.getSeparatorLength().max - footnotes.getSeparatorLength().opt;
+            }
+            // add the float separator stretch if some float content will be added
+            if (((KnuthPageNode) activeNode).floatsProgress.getAlreadyInsertedLength() < floats.getTotalLength()) {
+                maxAdjustment += floats.getSeparatorLength().max - floats.getSeparatorLength().opt;
             }
             if (maxAdjustment > 0) {
                 return (double) difference / maxAdjustment;
@@ -592,8 +415,12 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
         } else if (difference < 0) {
             int maxAdjustment = totalShrink - activeNode.totalShrink;
             // add the footnote separator shrink if some footnote content will be added
-            if (((KnuthPageNode) activeNode).totalFootnotes < totalFootnotesLength) {
-                maxAdjustment += footnoteSeparatorLength.opt - footnoteSeparatorLength.min;
+            if (((KnuthPageNode) activeNode).footnotesProgress.getAlreadyInsertedLength() < footnotes.getTotalLength()) {
+                maxAdjustment += footnotes.getSeparatorLength().opt - footnotes.getSeparatorLength().min;
+            }
+            // add the float separator shrink if some float content will be added
+            if (((KnuthPageNode) activeNode).floatsProgress.getAlreadyInsertedLength() < floats.getTotalLength()) {
+                maxAdjustment += floats.getSeparatorLength().opt - floats.getSeparatorLength().min;
             }
             if (maxAdjustment > 0) {
                 return (double) difference / maxAdjustment;
@@ -610,6 +437,19 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
         double demerits = 0;
         // compute demerits
         double f = Math.abs(r);
+        /* If the adjustment ratio is too high, the demerits will be "almost infinite"
+         * (10^22). Adding demerits for a deferred float (10000) thus won't change the
+         * demerits value. We may end up with two breakpoints with the same demerits,
+         * whereas in one case there are deferred floats and not in the other case. The
+         * case with no deferred floats is still preferable, so we must have the
+         * possibility to distinguish it. By forcing f to 1 it becomes possible to make
+         * the difference when there are deferred floats.
+         * TODO vh: use threshold instead of 1 (currently threshold == 1 but it might be
+         * configurable)
+         */
+        if (f > 1) {
+            f = 1;
+        }
         f = 1 + 100 * f * f * f;
         if (element.isPenalty() && element.getP() >= 0) {
             f += element.getP();
@@ -633,18 +473,15 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
             demerits += incompatibleFitnessDemerit;
         }
 
-        if (footnotesPending) {
-            if (footnoteListIndex < footnotesList.size() - 1) {
-                // add demerits for the deferred footnotes
-                demerits += (footnotesList.size() - 1 - footnoteListIndex) 
-                                * deferredFootnoteDemerits;
-            }
-            if (footnoteElementIndex 
-                    < ((LinkedList) footnotesList.get(footnoteListIndex)).size() - 1) {
-                // add demerits for the footnote split between pages
+        if (footnotes.existing()) {
+            demerits += footnotes.getNbOfDeferred() * deferredFootnoteDemerits; 
+            if (footnotes.isSplit()) {
                 demerits += splitFootnoteDemerits;
             }
         }
+        if (floats.existing()) {
+            demerits += floats.getNbOfDeferred() * deferredFloatDemerits; 
+        }
         demerits += activeNode.totalDemerits;
         return demerits;
     }
@@ -654,66 +491,19 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
             for (KnuthPageNode node = (KnuthPageNode) getNode(i);
                  node != null;
                  node = (KnuthPageNode) node.next) {
-                if (node.totalFootnotes < totalFootnotesLength) {
+                if (node.footnotesProgress.getAlreadyInsertedLength()
+                        < footnotes.getTotalLength()) {
                     // layout remaining footnote bodies
-                    createFootnotePages(node);
+                    footnotes.createFootnotePages(node, this, getLineWidth());
+                }
+                if (node.floatsProgress.getAlreadyInsertedLength() < floats.getTotalLength()) {
+                    // layout remaining float bodies
+                    floats.createFloatPages(node, this, getLineWidth());
                 }
             }
         }
     }
 
-    private void createFootnotePages(KnuthPageNode lastNode) {
-        insertedFootnotesLength = lastNode.totalFootnotes;
-        footnoteListIndex = lastNode.footnoteListIndex;
-        footnoteElementIndex = lastNode.footnoteElementIndex;
-        int availableBPD = getLineWidth();
-        int split = 0;
-        KnuthPageNode prevNode = lastNode;
-
-        // create pages containing the remaining footnote bodies
-        while (insertedFootnotesLength < totalFootnotesLength) {
-            // try adding some more content
-            if (((Integer) lengthList.get(footnoteListIndex)).intValue() - insertedFootnotesLength
-                <= availableBPD) {
-                // add a whole footnote
-                availableBPD -= ((Integer) lengthList.get(footnoteListIndex)).intValue()
-                                - insertedFootnotesLength;
-                insertedFootnotesLength = ((Integer)lengthList.get(footnoteListIndex)).intValue();
-                footnoteElementIndex
-                    = ((LinkedList)footnotesList.get(footnoteListIndex)).size() - 1;
-            } else if ((split = getFootnoteSplit(footnoteListIndex, footnoteElementIndex,
-                                                 insertedFootnotesLength, availableBPD, true))
-                       > 0) {
-                // add a piece of a footnote
-                availableBPD -= split;
-                insertedFootnotesLength += split;
-                // footnoteListIndex has already been set in getFootnoteSplit()
-                // footnoteElementIndex has already been set in getFootnoteSplit()
-            } else {
-                // cannot add any content: create a new node and start again
-                KnuthPageNode node = (KnuthPageNode)
-                                     createNode(lastNode.position, prevNode.line + 1, 1,
-                                                insertedFootnotesLength - prevNode.totalFootnotes, 
-                                                0, 0,
-                                                0, 0, 0,
-                                                0, 0, prevNode);
-                addNode(node.line, node);
-                removeNode(prevNode.line, prevNode);
-
-                prevNode = node;
-                availableBPD = getLineWidth();
-            }
-        }
-        // create the last node
-        KnuthPageNode node = (KnuthPageNode)
-                             createNode(lastNode.position, prevNode.line + 1, 1,
-                                        totalFootnotesLength - prevNode.totalFootnotes, 0, 0,
-                                        0, 0, 0,
-                                        0, 0, prevNode);
-        addNode(node.line, node);
-        removeNode(prevNode.line, prevNode);
-    }
-
     /**
      * @return a list of PageBreakPosition elements
      */
@@ -782,16 +572,25 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
             }
         }
         // compute the indexes of the first footnote list and the first element in that list
-        int firstListIndex = ((KnuthPageNode) bestActiveNode.previous).footnoteListIndex;
-        int firstElementIndex = ((KnuthPageNode) bestActiveNode.previous).footnoteElementIndex;
-        if (footnotesList != null
-            && firstElementIndex == ((LinkedList) footnotesList.get(firstListIndex)).size() - 1) {
+        int firstFootnoteListIndex = ((KnuthPageNode) bestActiveNode.previous).
+                footnotesProgress.getLastInsertedIndex();
+        int firstFootnoteElementIndex = ((KnuthPageNode) bestActiveNode.previous).
+                footnotesProgress.getLastElementOfLastInsertedIndex();
+        if (firstFootnoteListIndex == -1) {
+            firstFootnoteListIndex++;
+            firstFootnoteElementIndex = 0;
+        } else if (footnotes.getSequence(firstFootnoteListIndex) != null
+                && firstFootnoteElementIndex == ((LinkedList) footnotes.
+                        getSequence(firstFootnoteListIndex)).size() - 1) {
             // advance to the next list
-            firstListIndex++;
-            firstElementIndex = 0;
+            firstFootnoteListIndex++;
+            firstFootnoteElementIndex = 0;
         } else {
-            firstElementIndex++;
+            firstFootnoteElementIndex++;
         }
+        // compute the indexes of the first float list
+        int firstFloatListIndex = ((KnuthPageNode) bestActiveNode.previous).
+                floatsProgress.getLastInsertedIndex() + 1;
 
         // add nodes at the beginning of the list, as they are found
         // backwards, from the last one to the first one
@@ -801,9 +600,12 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
         }
         insertPageBreakAsFirst(new PageBreakPosition(this.topLevelLM, 
                 bestActiveNode.position,
-                firstListIndex, firstElementIndex,
-                ((KnuthPageNode) bestActiveNode).footnoteListIndex,
-                ((KnuthPageNode) bestActiveNode).footnoteElementIndex,
+                firstFootnoteListIndex, firstFootnoteElementIndex,
+                ((KnuthPageNode) bestActiveNode).footnotesProgress.getLastInsertedIndex(),
+                ((KnuthPageNode) bestActiveNode).footnotesProgress.
+                        getLastElementOfLastInsertedIndex(),
+                firstFloatListIndex,
+                ((KnuthPageNode) bestActiveNode).floatsProgress.getLastInsertedIndex(),
                 ratio, difference));
     }
 
@@ -830,9 +632,13 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
     }
 
     public LinkedList getFootnoteList(int index) {
-        return (LinkedList) footnotesList.get(index);
+        return (LinkedList) footnotes.getSequence(index);
     }
-    
+
+    public LinkedList getFloatList(int index) {
+        return (LinkedList) floats.getSequence(index);
+    }
+
     /** @return the associated top-level formatting object. */
     public FObj getFObj() {
         return topLevelLM.getFObj();
index 567153aea6b3c437c49fb252864ea2b97e1dfaf1..e1e40744bad4512ef77811b178672cf1af307dda 100644 (file)
@@ -27,6 +27,7 @@ import org.apache.fop.datatypes.Numeric;
 import org.apache.fop.area.AreaTreeHandler;
 import org.apache.fop.area.AreaTreeModel;
 import org.apache.fop.area.Block;
+import org.apache.fop.area.BeforeFloat;
 import org.apache.fop.area.Footnote;
 import org.apache.fop.area.PageViewport;
 import org.apache.fop.area.LineArea;
@@ -89,7 +90,8 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
     private int startPageNum = 0;
     private int currentPageNum = 0;
 
-    private Block separatorArea = null;
+    private Block footnoteSeparatorArea = null;
+    private Block floatSeparatorArea = null;
     
     /**
      * Constructor
@@ -180,6 +182,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
         private boolean needColumnBalancing;
         
         private StaticContentLayoutManager footnoteSeparatorLM = null;
+        private StaticContentLayoutManager floatSeparatorLM = null;
 
         public PageBreaker(PageSequenceLayoutManager pslm) {
             this.pslm = pslm;
@@ -242,30 +245,53 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
             }
 
             // scan contentList, searching for footnotes
-            boolean bFootnotesPresent = false;
+            boolean footnotesPresent = false;
+            boolean floatsPresent = false;
             if (contentList != null) {
                 ListIterator contentListIterator = contentList.listIterator();
                 while (contentListIterator.hasNext()) {
                     ListElement element = (ListElement) contentListIterator.next();
-                    if (element instanceof KnuthBlockBox
-                        && ((KnuthBlockBox) element).hasAnchors()) {
-                        // element represents a line with footnote citations
-                        bFootnotesPresent = true;
-                        LayoutContext footnoteContext = new LayoutContext(context);
-                        footnoteContext.setStackLimit(context.getStackLimit());
-                        footnoteContext.setRefIPD(getCurrentPV()
-                                .getRegionReference(Constants.FO_REGION_BODY).getIPD());
-                        LinkedList footnoteBodyLMs = ((KnuthBlockBox) element).getFootnoteBodyLMs();
-                        ListIterator footnoteBodyIterator = footnoteBodyLMs.listIterator();
-                        // store the lists of elements representing the footnote bodies
-                        // in the box representing the line containing their references
-                        while (footnoteBodyIterator.hasNext()) {
-                            FootnoteBodyLayoutManager fblm 
-                                = (FootnoteBodyLayoutManager) footnoteBodyIterator.next();
-                            fblm.setParent(childFLM);
-                            fblm.initialize();
-                            ((KnuthBlockBox) element).addElementList(
-                                    fblm.getNextKnuthElements(footnoteContext, alignment));
+                    if (element instanceof KnuthBlockBox) {
+                        if (((KnuthBlockBox) element).hasFootnoteAnchors()) {
+                            // element represents a line with footnote citations
+                            footnotesPresent = true;
+                            LayoutContext footnoteContext = new LayoutContext(context);
+                            footnoteContext.setStackLimit(context.getStackLimit());
+                            footnoteContext.setRefIPD(getCurrentPV()
+                                    .getRegionReference(Constants.FO_REGION_BODY).getIPD());
+                            LinkedList footnoteBodyLMs
+                                    = ((KnuthBlockBox) element).getFootnoteBodyLMs();
+                            ListIterator footnoteBodyIterator = footnoteBodyLMs.listIterator();
+                            // store the lists of elements representing the footnote bodies
+                            // in the box representing the line containing their references
+                            while (footnoteBodyIterator.hasNext()) {
+                                FootnoteBodyLayoutManager fblm 
+                                    = (FootnoteBodyLayoutManager) footnoteBodyIterator.next();
+                                fblm.setParent(childFLM);
+                                fblm.initialize();
+                                ((KnuthBlockBox) element).addFootnoteElementList(
+                                        fblm.getNextKnuthElements(footnoteContext, alignment));
+                            }
+                        }
+                        if (((KnuthBlockBox) element).hasFloatAnchors()) {
+                            // element represents a line with float citations
+                            floatsPresent = true;
+                            LayoutContext floatContext = new LayoutContext(context);
+                            floatContext.setStackLimit(context.getStackLimit());
+                            floatContext.setRefIPD(getCurrentPV()
+                                    .getRegionReference(Constants.FO_REGION_BODY).getIPD());
+                            LinkedList floatBodyLMs = ((KnuthBlockBox) element).getFloatBodyLMs();
+                            ListIterator floatBodyIterator = floatBodyLMs.listIterator();
+                            // store the lists of elements representing the footnote bodies
+                            // in the box representing the line containing their references
+                            while (floatBodyIterator.hasNext()) {
+                                FloatBodyLayoutManager fblm 
+                                    = (FloatBodyLayoutManager) floatBodyIterator.next();
+                                fblm.setParent(childFLM);
+                                fblm.initialize();
+                                ((KnuthBlockBox) element).addFloatElementList(
+                                        fblm.getNextKnuthElements(floatContext, alignment));
+                            }
                         }
                     }
                 }
@@ -273,7 +299,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
 
             // handle the footnote separator
             StaticContent footnoteSeparator;
-            if (bFootnotesPresent
+            if (footnotesPresent
                     && (footnoteSeparator = pageSeq.getStaticContent(
                                             "xsl-footnote-separator")) != null) {
                 // the footnote separator can contain page-dependent content such as
@@ -284,16 +310,41 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
                 // always the same
 
                 // create a Block area that will contain the separator areas
-                separatorArea = new Block();
-                separatorArea.setIPD(pslm.getCurrentPV()
+                footnoteSeparatorArea = new Block();
+                footnoteSeparatorArea.setIPD(pslm.getCurrentPV()
                             .getRegionReference(Constants.FO_REGION_BODY).getIPD());
                 // create a StaticContentLM for the footnote separator
                 footnoteSeparatorLM = (StaticContentLayoutManager)
                     getLayoutManagerMaker().makeStaticContentLayoutManager(
-                    pslm, footnoteSeparator, separatorArea);
+                    pslm, footnoteSeparator, footnoteSeparatorArea);
                 footnoteSeparatorLM.doLayout();
 
-                footnoteSeparatorLength = new MinOptMax(separatorArea.getBPD());
+                footnoteSeparatorLength = new MinOptMax(footnoteSeparatorArea.getBPD());
+            }
+
+            // handle the float separator
+            StaticContent floatSeparator;
+            if (floatsPresent
+                    && (floatSeparator = pageSeq.getStaticContent(
+                                            "xsl-before-float-separator")) != null) {
+                // the float separator can contain page-dependent content such as
+                // page numbers or retrieve markers, so its areas cannot simply be 
+                // obtained now and repeated in each page;
+                // we need to know in advance the separator bpd: the actual separator
+                // could be different from page to page, but its bpd would likely be
+                // always the same
+
+                // create a Block area that will contain the separator areas
+                floatSeparatorArea = new Block();
+                floatSeparatorArea.setIPD(pslm.getCurrentPV()
+                            .getRegionReference(Constants.FO_REGION_BODY).getIPD());
+                // create a StaticContentLM for the float separator
+                floatSeparatorLM = (StaticContentLayoutManager)
+                    getLayoutManagerMaker().makeStaticContentLayoutManager(
+                    pslm, floatSeparator, floatSeparatorArea);
+                floatSeparatorLM.doLayout();
+
+                floatSeparatorLength = new MinOptMax(floatSeparatorArea.getBPD());
             }
             return contentList;
         }
@@ -312,15 +363,28 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
                 StaticContent footnoteSeparator = pageSeq.getStaticContent(
                         "xsl-footnote-separator");
                 // create a Block area that will contain the separator areas
-                separatorArea = new Block();
-                separatorArea.setIPD(
+                footnoteSeparatorArea = new Block();
+                footnoteSeparatorArea.setIPD(
                         getCurrentPV().getRegionReference(Constants.FO_REGION_BODY).getIPD());
                 // create a StaticContentLM for the footnote separator
                 footnoteSeparatorLM = (StaticContentLayoutManager)
                     getLayoutManagerMaker().makeStaticContentLayoutManager(
-                    pslm, footnoteSeparator, separatorArea);
+                    pslm, footnoteSeparator, footnoteSeparatorArea);
                 footnoteSeparatorLM.doLayout();
             }
+            if (floatSeparatorLM != null) {
+                StaticContent floatSeparator = pageSeq.getStaticContent(
+                        "xsl-before-float-separator");
+                // create a Block area that will contain the separator areas
+                floatSeparatorArea = new Block();
+                floatSeparatorArea.setIPD(
+                        getCurrentPV().getRegionReference(Constants.FO_REGION_BODY).getIPD());
+                // create a StaticContentLM for the float separator
+                floatSeparatorLM = (StaticContentLayoutManager)
+                    getLayoutManagerMaker().makeStaticContentLayoutManager(
+                    pslm, floatSeparator, floatSeparatorArea);
+                floatSeparatorLM.doLayout();
+            }
 
             childFLM.addAreas(posIter, context);    
         }
@@ -374,7 +438,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
                     getTopLevelLM(),
                     getPageProvider(),
                     alg.getAlignment(), alg.getAlignmentLast(), 
-                    footnoteSeparatorLength,
+                    footnoteSeparatorLength, floatSeparatorLength,
                     isPartOverflowRecoveryActivated(), false, false);
             //alg.setConstantLineWidth(flowBPD);
             int iOptPageCount = algRestart.findBreakingPoints(effectiveList,
@@ -433,7 +497,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
             PageBreakingAlgorithm algRestart = new BalancingColumnBreakingAlgorithm(
                     getTopLevelLM(),
                     getPageProvider(),
-                    alignment, Constants.EN_START, footnoteSeparatorLength,
+                    alignment, Constants.EN_START, footnoteSeparatorLength, floatSeparatorLength,
                     isPartOverflowRecoveryActivated(),
                     getCurrentPV().getBodyRegion().getColumnCount());
             //alg.setConstantLineWidth(flowBPD);
@@ -488,6 +552,26 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
         }
         
         protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) {
+            // add float areas
+            if (pbp.floatFirstListIndex <= pbp.floatLastListIndex) {
+                // call addAreas() for each FloatBodyLM
+                for (int i = pbp.floatFirstListIndex; i <= pbp.floatLastListIndex; i++) {
+                    LinkedList elementList = alg.getFloatList(i);
+                    int firstIndex = 0;
+                    int lastIndex = elementList.size() - 1;
+
+                    SpaceResolver.performConditionalsNotification(elementList, 
+                            firstIndex, lastIndex, -1);
+                    LayoutContext childLC = new LayoutContext(0);
+                    AreaAdditionUtil.addAreas(null, 
+                            new KnuthPossPosIter(elementList, firstIndex, lastIndex + 1), 
+                            childLC);
+                }
+                // set the offset from the top margin
+                BeforeFloat parentArea
+                        = (BeforeFloat) getCurrentPV().getBodyRegion().getBeforeFloat();
+                parentArea.setSeparator(floatSeparatorArea);
+            }
             // add footnote areas
             if (pbp.footnoteFirstListIndex < pbp.footnoteLastListIndex
                 || pbp.footnoteFirstElementIndex <= pbp.footnoteLastElementIndex) {
@@ -508,12 +592,13 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
                 }
                 // set the offset from the top margin
                 Footnote parentArea = (Footnote) getCurrentPV().getBodyRegion().getFootnote();
-                int topOffset = (int) getCurrentPV().getBodyRegion().getBPD() - parentArea.getBPD();
-                if (separatorArea != null) {
-                    topOffset -= separatorArea.getBPD();
+                int topOffset = (int) getCurrentPV().getBodyRegion().getBPD() - parentArea.getBPD()
+                        - ((BeforeFloat) getCurrentPV().getBodyRegion().getBeforeFloat()).getBPD();
+                if (footnoteSeparatorArea != null) {
+                    topOffset -= footnoteSeparatorArea.getBPD();
                 }
                 parentArea.setTop(topOffset);
-                parentArea.setSeparator(separatorArea);
+                parentArea.setSeparator(footnoteSeparatorArea);
             }
             getCurrentPV().getCurrentSpan().notifyFlowsFinished();
         }
index e7ccc2b712bd12e4f6545c3642fb2729f88efca3..871c81e8314a79692bef6206e7c9f3a88595af07 100644 (file)
@@ -186,7 +186,8 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager {
      * @see org.apache.fop.layoutmgr.LayoutManager#addChildArea(Area)
      */
     public void addChildArea(Area childArea) {
-        if (getStaticContentFO().getFlowName().equals("xsl-footnote-separator")) {
+        if (getStaticContentFO().getFlowName().equals("xsl-footnote-separator")
+                || getStaticContentFO().getFlowName().equals("xsl-before-float-separator")) {
             targetBlock.addBlock((Block)childArea);
         } else {
             targetRegion.addBlock((Block)childArea);
@@ -197,7 +198,8 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager {
      * @see org.apache.fop.layoutmgr.LayoutManager#getParentArea(Area)
      */
     public Area getParentArea(Area childArea) {
-        if (getStaticContentFO().getFlowName().equals("xsl-footnote-separator")) {
+        if (getStaticContentFO().getFlowName().equals("xsl-footnote-separator")
+                || getStaticContentFO().getFlowName().equals("xsl-before-float-separator")) {
             return targetBlock;
         } else {
             return targetRegion;
@@ -214,7 +216,8 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager {
         boolean autoHeight = false;
         StaticContentBreaker breaker;
 
-        if (getStaticContentFO().getFlowName().equals("xsl-footnote-separator")) {
+        if (getStaticContentFO().getFlowName().equals("xsl-footnote-separator")
+                || getStaticContentFO().getFlowName().equals("xsl-before-float-separator")) {
             targetIPD = targetBlock.getIPD();
             targetBPD = targetBlock.getBPD();
             if (targetBPD == 0) {
diff --git a/src/java/org/apache/fop/layoutmgr/breaking/OutOfLineRecord.java b/src/java/org/apache/fop/layoutmgr/breaking/OutOfLineRecord.java
new file mode 100644 (file)
index 0000000..53e8805
--- /dev/null
@@ -0,0 +1,601 @@
+/*
+ * 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$ */
+
+package org.apache.fop.layoutmgr.breaking;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.apache.fop.layoutmgr.KnuthElement;
+import org.apache.fop.layoutmgr.PageBreakingAlgorithm;
+import org.apache.fop.layoutmgr.SpaceResolver;
+import org.apache.fop.layoutmgr.PageBreakingAlgorithm.KnuthPageNode;
+import org.apache.fop.traits.MinOptMax;
+
+/**
+ * Helper class for dealing with out-of-line objects (before-floats and footnotes) when
+ * breaking text into pages. It stores the necessary informations to place out-of-line
+ * objects, and provides methods to manipulate them.
+ * 
+ * @see PageBreakingAlgorithm
+ */
+public class OutOfLineRecord {
+    
+    /**
+     * Stores informations about how many out-of-line objects have already been handled.
+     */
+    public static class ProgressInfo {
+
+        /** Cumulated BPD length of all out-of-line objects inserted so far. */
+        private int alreadyInsertedLength = 0;
+
+        /** Index of the last inserted out-of-line object. */
+        private int lastInsertedIndex = -1;
+
+        /**
+         * Index of the last inserted Knuth element of the last inserted out-of-line
+         * object. Currently only used for footnotes, as before-floats may not be split on
+         * several pages. Might be useful when later dealing with floats that cannot even
+         * be put on a page alone, however.
+         */
+        private int lastElementOfLastInsertedIndex = -1;
+        
+        /**
+         * Initializes this record, as if no out-of-line object were handled yet.
+         */
+        private void initialize() {
+            alreadyInsertedLength = 0;
+            lastInsertedIndex = -1;
+            lastElementOfLastInsertedIndex = -1;
+        }
+
+        /**
+         * @return a copy of this record
+         */
+        public ProgressInfo copy() {
+            ProgressInfo info = new ProgressInfo();
+            info.alreadyInsertedLength = alreadyInsertedLength;
+            info.lastInsertedIndex = lastInsertedIndex;
+            info.lastElementOfLastInsertedIndex = lastElementOfLastInsertedIndex;
+            return info;
+        }
+
+        /**
+         * Returns the cumulated length of all already typeset out-of-line objects.
+         * @return the total length in the block-progression-direction
+         */
+        public int getAlreadyInsertedLength() {
+            return alreadyInsertedLength;
+        }
+
+        /**
+         * Returns the index of the last element of the last already typeset out-of-line
+         * object.
+         * @return the index of the last placed KnuthElement
+         */
+        public int getLastElementOfLastInsertedIndex() {
+            return lastElementOfLastInsertedIndex;
+        }
+
+        /**
+         * @return the index of the last already typeset out-of-line object.
+         */
+        public int getLastInsertedIndex() {
+            return lastInsertedIndex;
+        }
+
+        public String toString() {
+            return "length=" + alreadyInsertedLength
+                    + ", index=" + lastInsertedIndex
+                    + ", elt=" + lastElementOfLastInsertedIndex;
+        }
+    }
+
+    /**
+     * Sequences of KnuthElement corresponding to already encountered out-of-line objects.
+     * This is a List of List of KnuthElement.
+     */
+    private List knuthSequences = null;
+
+    /**
+     * Each element of this list corresponds to the cumulated length in the BPD of all the
+     * out-of-line objects up to the given index. This is a List of Integer.
+     * 
+     * @see OutOfLineRecord#knuthSequences 
+     */
+    private List cumulativeLengths = null;
+
+    /**
+     * True if new out-of-line objects are cited in the sequence of Knuth elements since
+     * the last encountered legal breakpoint.
+     * 
+     * @see OutOfLineRecord#newSinceLastBreakpoint()
+     */
+    private boolean newSinceLastBreakpoint = false;
+
+    /**
+     * Index of the first newly encountered out-of-line object since the last legal
+     * breakpoint.
+     * 
+     * @see OutOfLineRecord#knuthSequences
+     */
+    private int firstNewIndex = 0;
+
+    /**
+     * Dimension in the BPD of the separator between the out-of-line area and the main
+     * area.
+     */
+    private MinOptMax separatorLength = null;
+
+    /**
+     * Record of already handled out-of-line objects.
+     * 
+     * @see ProgressInfo
+     */
+    private ProgressInfo progressInfo;
+
+    public OutOfLineRecord(MinOptMax separatorLength) {
+        this.separatorLength = separatorLength;
+        this.progressInfo = new ProgressInfo();
+    }
+
+    /**
+     * Initializes this record, as if no out-of-line object were handled yet.
+     */
+    public void initialize() {
+        knuthSequences = null;
+        cumulativeLengths = null;
+        newSinceLastBreakpoint = false;
+        firstNewIndex = 0;
+        progressInfo.initialize();
+    }
+
+    /**
+     * @return the informations about already handled out-of-line objects
+     */
+    public ProgressInfo getProgress() {
+        return this.progressInfo;
+    }
+
+    /**
+     * @return the length in the BPD of the separator between the out-of-line area and the
+     * main area.
+     */
+    public MinOptMax getSeparatorLength() {
+        return separatorLength;
+    }
+
+    /**
+     * @return the total length of already encountered out-of-line objects
+     */
+    public int getTotalLength() {
+        if (cumulativeLengths == null || cumulativeLengths.size() == 0) {
+            return 0;
+        } else {
+            return ((Integer) cumulativeLengths.get(cumulativeLengths.size() - 1)).intValue(); 
+        }
+    }
+
+    /**
+     * @return true if out-of-line objects have already been encountered (but not
+     * necessarily typeset yet)
+     */
+    public boolean existing() {
+        return (knuthSequences != null && knuthSequences.size() > 0);
+    }
+
+    public void resetNewSinceLastBreakpoint() {
+        newSinceLastBreakpoint = false;
+    }
+
+    /**
+     * @return true if new out-of-line objects are cited in the sequence of Knuth
+     * elements since the last encountered legal breakpoint.
+     */
+    public boolean newSinceLastBreakpoint() {
+        return newSinceLastBreakpoint;
+    }
+
+    /**
+     * Records one or more newly encountered out-of-line objects.
+     * @param elementLists the list of corresponding Knuth sequences
+     */
+    public void add(List elementLists) {
+        // Initialize stuff if necessary
+        if (knuthSequences == null) {
+            knuthSequences = new ArrayList();
+            cumulativeLengths = new ArrayList();
+        }
+        if (!newSinceLastBreakpoint) {
+            newSinceLastBreakpoint = true;
+            firstNewIndex = knuthSequences.size();
+        }
+        // compute the total length of the footnotes
+        ListIterator elementListsIterator = elementLists.listIterator();
+        while (elementListsIterator.hasNext()) {
+            LinkedList noteList = (LinkedList) elementListsIterator.next();
+            
+            //Space resolution (Note: this does not respect possible stacking constraints 
+            //between footnotes!)
+            SpaceResolver.resolveElementList(noteList);
+            
+            int noteLength = 0;
+            knuthSequences.add(noteList);
+            ListIterator noteListIterator = noteList.listIterator();
+            while (noteListIterator.hasNext()) {
+                KnuthElement element = (KnuthElement) noteListIterator.next();
+                if (element.isBox() || element.isGlue()) {
+                    noteLength += element.getW();
+                }
+            }
+            cumulativeLengths.add(new Integer(getTotalLength() + noteLength));
+        }
+    }
+
+    /**
+     * Sets the progress informations to the given values. Called whenever a new active
+     * node is considered; the informations regarding already handled out-of-line objects
+     * must be set to the active node's values in order to know from where to start the
+     * placement of further objects.
+     *
+     * @param info progress informations of the currently considered active node
+     */
+    public void setProgress(ProgressInfo info) {
+        this.progressInfo.alreadyInsertedLength = info.alreadyInsertedLength;
+        this.progressInfo.lastElementOfLastInsertedIndex = info.lastElementOfLastInsertedIndex;
+        this.progressInfo.lastInsertedIndex = info.lastInsertedIndex;
+    }
+
+    /* Unless I'm wrong, newOnThisPagePlusPiecesFromPrevious always implies
+     * notAllInserted. And if A => B, then A && B <=> B
+     * So this code may be simplified, see deferred() below
+     */
+    /**
+//   * Returns true if their are (pieces of) footnotes to be typeset on the
+//   * current page.
+//   * @param listIndex index of the last inserted footnote for the
+//   * currently considered active node
+//   * @param elementIndex index of the last element of the last inserted footnote
+//   * @param length total length of all footnotes inserted so far
+//   */
+//  public boolean deferredFootnotes(ProgressInfo progressInfo) {
+//      boolean newOnThisPagePlusPiecesFromPrevious =
+//              newSinceLastBreakpoint()
+//              && firstNewIndex != 0
+//              && (progressInfo.lastInsertedIndex < firstNewIndex - 1
+//                  || progressInfo.lastElementOfLastInsertedIndex <
+//                      ((LinkedList) knuthSequences.get(progressInfo.lastInsertedIndex)).size() - 1);
+//      boolean notAllInserted = progressInfo.alreadyInsertedLength < getTotalLength();
+//      return notAllInserted;
+//  }
+
+    /**
+     * @return <code>true</code> if some out-of-line objects have not already been
+     * typeset.
+     */
+    public boolean deferred() {
+        return progressInfo.alreadyInsertedLength < getTotalLength();
+    }
+
+    /**
+     * @return the number of not yet typeset out-of-line objects.
+     */
+    public int getNbOfDeferred() {
+        return knuthSequences.size() - 1 - progressInfo.lastInsertedIndex;
+    }
+
+    /**
+     * @return <code>true</code> if the last typeset out-of-line object must be split on
+     * several pages.
+     */
+    public boolean isSplit() {
+        return (progressInfo.lastElementOfLastInsertedIndex 
+                < ((LinkedList) knuthSequences.get(progressInfo.lastInsertedIndex)).size() - 1);
+    }
+
+    /**
+     * Returns the out-of-line object corresponding to the given index.
+     * @param index index of the object
+     * @return a List of KnuthElement corresponding to the object, or <code>null</code> if
+     * it does not exist
+     */
+    public List getSequence(int index) {
+        /*TODO vh: bof */
+        if (knuthSequences == null) {
+            return null;
+        } else {
+            return (List) knuthSequences.get(index);
+        }
+    }
+
+    /**
+     * Tries to split the flow of footnotes to put one part on the current page.
+     * @param prevNodeProgress informations about footnotes already inserted on the
+     * previous page
+     * @param availableLength available space for footnotes on this page
+     * @param canDeferOldFootnotes
+     * @return the length of footnotes which could be inserted on this page
+     */
+    public int getFootnoteSplit(ProgressInfo prevNodeProgress,
+                                int availableLength, boolean canDeferOldFootnotes) {
+        if (availableLength <= 0) {
+            progressInfo.alreadyInsertedLength = prevNodeProgress.getAlreadyInsertedLength();
+            return 0;
+        } else {
+            // the split should contain a piece of the last footnote
+            // together with all previous, not yet inserted footnotes;
+            // but if this is not possible, try adding as much content as possible
+            int splitLength = 0;
+            ListIterator noteListIterator = null;
+            KnuthElement element = null;
+            boolean somethingAdded = false;
+
+            // prevNodeProgress.lastInsertedIndex and
+            // prevNodeProgress.lastElementOfLastInsertedIndex points to the last footnote element
+            // already placed in a page: advance to the next element
+            int listIndex = prevNodeProgress.lastInsertedIndex;
+            int elementIndex = prevNodeProgress.lastElementOfLastInsertedIndex;
+            if (listIndex == -1
+                    || elementIndex == ((LinkedList) knuthSequences.get(listIndex)).size() - 1) {
+                listIndex++;
+                elementIndex = 0;
+            } else {
+                elementIndex++;
+            }
+
+            // try adding whole notes
+            // if there are more than 1 footnote to insert
+            if (knuthSequences.size() - 1 > listIndex) {
+                // add the previous footnotes: these cannot be broken or deferred
+                if (!canDeferOldFootnotes
+                    && newSinceLastBreakpoint()
+                    && firstNewIndex > 0) {
+                    splitLength = ((Integer) cumulativeLengths.get(firstNewIndex - 1)).intValue()
+                                  - prevNodeProgress.alreadyInsertedLength;
+                    listIndex = firstNewIndex;
+                    elementIndex = 0;
+                }
+                // try adding the new footnotes
+                while (((Integer) cumulativeLengths.get(listIndex)).intValue()
+                        - prevNodeProgress.alreadyInsertedLength <= availableLength) {
+                    splitLength = ((Integer) cumulativeLengths.get(listIndex)).intValue()
+                                  - prevNodeProgress.alreadyInsertedLength;
+                    somethingAdded = true;
+                    listIndex++;
+                    elementIndex = 0;
+                }
+                // as this method is called only if it is not possible to insert
+                // all footnotes, at this point listIndex and elementIndex points to
+                // an existing element, the next one we will try to insert 
+            }
+
+            // try adding a split of the next note
+            noteListIterator = ((List) knuthSequences.get(listIndex)).listIterator(elementIndex);
+
+            int prevSplitLength = 0;
+            int prevIndex = -1;
+            int index = -1;
+
+            while (!somethingAdded || splitLength <= availableLength) {
+                if (!somethingAdded) {
+                    somethingAdded = true;
+                } else {
+                    prevSplitLength = splitLength;
+                    prevIndex = index;
+                }
+                // get a sub-sequence from the note element list
+                boolean bPrevIsBox = false;
+                while (noteListIterator.hasNext()) {
+                    // as this method is called only if it is not possible to insert
+                    // all footnotes, and we have already tried (and failed) to insert
+                    // this whole footnote, the while loop will never reach the end
+                    // of the note sequence
+                    element = (KnuthElement) noteListIterator.next();
+                    if (element.isBox()) {
+                        // element is a box
+                        splitLength += element.getW();
+                        bPrevIsBox = true;
+                    } else if (element.isGlue()) {
+                        // element is a glue
+                        if (bPrevIsBox) {
+                            // end of the sub-sequence
+                            index = noteListIterator.previousIndex();
+                            break;
+                        }
+                        bPrevIsBox = false;
+                        splitLength += element.getW();
+                    } else {
+                        // element is a penalty
+                        if (element.getP() < KnuthElement.INFINITE) {
+                            // end of the sub-sequence
+                            index = noteListIterator.previousIndex();
+                            break;
+                        }
+                    }
+                }
+            }
+            // if prevSplitLength is 0, this means that the available length isn't enough
+            // to insert even the smallest split of the last footnote, so we cannot end a
+            // page here
+            // if prevSplitLength is > 0 we can insert some footnote content in this page
+            // and insert the remaining in the following one
+            if (!somethingAdded) {
+                // there was not enough space to add a piece of the first new footnote
+                // this is not a good break
+                prevSplitLength = 0;
+            } else if (prevSplitLength > 0) {
+                // prevIndex is -1 if we have added only some whole footnotes
+                progressInfo.lastInsertedIndex = (prevIndex != -1) ? listIndex : listIndex - 1;
+                progressInfo.lastElementOfLastInsertedIndex = (prevIndex != -1)
+                    ? prevIndex 
+                    : ((LinkedList) knuthSequences.get(progressInfo.lastInsertedIndex)).size() - 1;
+            }
+            progressInfo.alreadyInsertedLength
+                    = prevNodeProgress.getAlreadyInsertedLength() + prevSplitLength;
+            return prevSplitLength;
+        }
+    }
+
+    /**
+     * Tries to split the flow of floats to put some floats on the current page.
+     * @param prevProgress floats already inserted on the previous page
+     * @param availableLength available space for floats
+     * @return the length of floats which could be placed on the current page
+     */
+    public int getFloatSplit(ProgressInfo prevProgress, int availableLength) {
+        /*
+         * Normally this method is called only when there is some place for
+         * floats => availableLength > 0
+         */
+        int splitLength = 0;
+        int listIndex = prevProgress.lastInsertedIndex + 1;
+
+        while (listIndex < knuthSequences.size()
+                && ((Integer) cumulativeLengths.get(listIndex)).intValue()
+                    - prevProgress.alreadyInsertedLength <= availableLength) {
+            splitLength = ((Integer) cumulativeLengths.get(listIndex)).intValue()
+                    - prevProgress.alreadyInsertedLength;
+            listIndex++;
+        }
+        progressInfo.lastInsertedIndex = listIndex - 1;
+        progressInfo.alreadyInsertedLength = prevProgress.alreadyInsertedLength + splitLength;
+        return splitLength;
+    }
+
+    /**
+     * Places on the current page all of the out-of-line objects not yet inserted.
+     */
+    public void insertAll() {
+        progressInfo.alreadyInsertedLength = getTotalLength();
+        progressInfo.lastInsertedIndex = knuthSequences.size() - 1;
+        progressInfo.lastElementOfLastInsertedIndex
+                = ((List) knuthSequences.get(progressInfo.lastInsertedIndex)).size() - 1;        
+    }
+
+    /**
+     * When restarting the algorithm from a given point, reset the informations about
+     * out-of-line objects to the values at that point.
+     * @param elementLists out-of-line sequences which are met after the restarting point,
+     * and thus must be removed from the list of already encoutered objects.
+     */
+    public void reset(List elementLists) {
+        for (int i = 0; i < elementLists.size(); i++) {
+            knuthSequences.remove(knuthSequences.size() - 1);
+            cumulativeLengths.remove(cumulativeLengths.size() - 1);
+        }
+    }
+
+    /**
+     * When the whole normal flow has been typeset and there are still footnotes to be
+     * placed, creates as many pages as necessary to place them.
+     */
+    public void createFootnotePages(KnuthPageNode lastNode, PageBreakingAlgorithm algo, int lineWidth) {
+        progressInfo.alreadyInsertedLength = lastNode.footnotesProgress.getAlreadyInsertedLength();
+        progressInfo.lastInsertedIndex = lastNode.footnotesProgress.getLastInsertedIndex();
+        progressInfo.lastElementOfLastInsertedIndex = lastNode.footnotesProgress.getLastElementOfLastInsertedIndex();
+        int availableBPD = lineWidth;
+        int split = 0;
+        KnuthPageNode prevNode = lastNode;
+
+        // create pages containing the remaining footnote bodies
+        while (progressInfo.alreadyInsertedLength < getTotalLength()) {
+            // try adding some more content
+            if (((Integer) cumulativeLengths.get(progressInfo.lastInsertedIndex)).intValue() - progressInfo.alreadyInsertedLength
+                <= availableBPD) {
+                // add a whole footnote
+                availableBPD -= ((Integer) cumulativeLengths.get(progressInfo.lastInsertedIndex)).intValue()
+                                - progressInfo.alreadyInsertedLength;
+                progressInfo.alreadyInsertedLength = ((Integer)cumulativeLengths.get(progressInfo.lastInsertedIndex)).intValue();
+                progressInfo.lastElementOfLastInsertedIndex
+                    = ((LinkedList)knuthSequences.get(progressInfo.lastInsertedIndex)).size() - 1;
+            } else if ((split = getFootnoteSplit(progressInfo, availableBPD, true))
+                       > 0) {
+                // add a piece of a footnote
+                availableBPD -= split;
+                // footnoteListIndex has already been set in getFootnoteSplit()
+                // footnoteElementIndex has already been set in getFootnoteSplit()
+            } else {
+                // cannot add any content: create a new node and start again
+                KnuthPageNode node = (KnuthPageNode)
+                                     algo.createNode(lastNode.position, prevNode.line + 1, 1,
+                                                progressInfo.alreadyInsertedLength - prevNode.footnotesProgress.getAlreadyInsertedLength(), 
+                                                0, 0,
+                                                0, 0, 0,
+                                                0, 0, prevNode);
+                algo.addNode(node.line, node);
+                algo.removeNode(prevNode.line, prevNode);
+
+                prevNode = node;
+                availableBPD = lineWidth;
+            }
+        }
+        // create the last node
+        KnuthPageNode node = (KnuthPageNode)
+                             algo.createNode(lastNode.position, prevNode.line + 1, 1,
+                                        getTotalLength() - prevNode.footnotesProgress.getAlreadyInsertedLength(), 0, 0,
+                                        0, 0, 0,
+                                        0, 0, prevNode);
+        algo.addNode(node.line, node);
+        algo.removeNode(prevNode.line, prevNode);
+    }
+
+    /* TODO vh: won't work when there are also footnotes. To be merged with createFootnotePages */
+    public void createFloatPages(KnuthPageNode lastNode, PageBreakingAlgorithm algo, int lineWidth) {
+        progressInfo.alreadyInsertedLength = lastNode.floatsProgress.getAlreadyInsertedLength();
+        progressInfo.lastInsertedIndex = lastNode.floatsProgress.getLastInsertedIndex();
+        int availableBPD = lineWidth;
+        KnuthPageNode prevNode = lastNode;
+
+        // create pages containing the remaining float bodies
+        while (progressInfo.alreadyInsertedLength < getTotalLength()) {
+            // try adding some more content
+            if (((Integer) cumulativeLengths.get(progressInfo.lastInsertedIndex + 1)).intValue() - progressInfo.alreadyInsertedLength
+                <= availableBPD) {
+                // add a whole float
+                progressInfo.lastInsertedIndex++;
+                availableBPD -= ((Integer) cumulativeLengths.get(progressInfo.lastInsertedIndex)).intValue()
+                                - progressInfo.alreadyInsertedLength;
+                progressInfo.alreadyInsertedLength = ((Integer)cumulativeLengths.get(progressInfo.lastInsertedIndex)).intValue();
+            } else {
+                // cannot add any content: create a new node and start again
+                KnuthPageNode node = (KnuthPageNode)
+                                     algo.createNode(lastNode.position, prevNode.line + 1, 1,
+                                                progressInfo.alreadyInsertedLength - prevNode.floatsProgress.getAlreadyInsertedLength(), 
+                                                0, 0,
+                                                0, 0, 0,
+                                                0, prevNode.totalDemerits + (progressInfo.lastInsertedIndex - prevNode.floatsProgress.lastInsertedIndex) * 10000, prevNode);
+                algo.addNode(node.line, node);
+                algo.removeNode(prevNode.line, prevNode);
+
+                prevNode = node;
+                availableBPD = lineWidth;
+            }
+        }
+        // create the last node
+        KnuthPageNode node = (KnuthPageNode)
+                             algo.createNode(lastNode.position, prevNode.line + 1, 1,
+                                        getTotalLength() - prevNode.floatsProgress.getAlreadyInsertedLength(), 0, 0,
+                                        0, 0, 0,
+                                        0, prevNode.totalDemerits + (progressInfo.lastInsertedIndex - prevNode.floatsProgress.lastInsertedIndex) * 10000, prevNode);
+        algo.addNode(node.line, node);
+        algo.removeNode(prevNode.line, prevNode);
+    }
+}
diff --git a/src/java/org/apache/fop/layoutmgr/inline/FloatLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/FloatLayoutManager.java
new file mode 100644 (file)
index 0000000..c297e06
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * 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$ */
+
+package org.apache.fop.layoutmgr.inline;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.fop.fo.flow.Float;
+import org.apache.fop.layoutmgr.AbstractLayoutManager;
+import org.apache.fop.layoutmgr.FloatBodyLayoutManager;
+import org.apache.fop.layoutmgr.InlineKnuthSequence;
+import org.apache.fop.layoutmgr.KnuthElement;
+import org.apache.fop.layoutmgr.KnuthSequence;
+import org.apache.fop.layoutmgr.LayoutContext;
+import org.apache.fop.layoutmgr.Position;
+
+/**
+ * Layout manager for fo:float.
+ */
+public class FloatLayoutManager extends AbstractLayoutManager 
+                                   implements InlineLevelLayoutManager {
+
+    private Float floatNode;
+    private FloatBodyLayoutManager bodyLM;
+    /** Represents the float citation **/
+    private KnuthElement forcedAnchor;
+
+    /**
+     * Create a new float layout manager.
+     * @param node float to create the layout manager for
+     */
+    public FloatLayoutManager(Float node) {
+        super(node);
+        floatNode = node;
+    }
+    
+    /** @see org.apache.fop.layoutmgr.LayoutManager#initialize() */
+    public void initialize() {
+        // create a FloatBodyLM handling the fo:float-body child of fo:float
+        bodyLM = new FloatBodyLayoutManager(floatNode);
+    }
+
+    /** @see org.apache.fop.layoutmgr.LayoutManager */
+    public LinkedList getNextKnuthElements(LayoutContext context,
+                                           int alignment) {
+        // this is the only method that must be implemented:
+        // all other methods will never be called, as the returned elements
+        // contain Positions created by the citationLM, so its methods will
+        // be called instead
+
+        bodyLM.setParent(this);
+        bodyLM.initialize();
+
+        // get Knuth elements representing the float citation
+        LinkedList returnedList = new LinkedList();
+        //Inline part of the float is empty. Need to send back an auxiliary
+        //zero-width, zero-height inline box so the float gets painted.
+        KnuthSequence seq = new InlineKnuthSequence();
+        //Need to use an aux. box, otherwise, the line height can't be forced to zero height.
+        forcedAnchor = new KnuthInlineBox(0, null, null, true);
+        ((KnuthInlineBox) forcedAnchor).setFloatBodyLM(bodyLM);
+        seq.add(forcedAnchor);
+        returnedList.add(seq);
+        setFinished(true);
+
+        return returnedList;
+    }
+
+    /** @see org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager */
+    public List addALetterSpaceTo(List oldList) {
+        log.warn("null implementation of addALetterSpaceTo() called!");
+        return oldList;
+    }
+
+    /**
+     * Remove the word space represented by the given elements
+     *
+     * @param oldList the elements representing the word space
+     */
+    public void removeWordSpace(List oldList) {
+        // do nothing
+        log.warn(this.getClass().getName() + " should not receive a call to removeWordSpace(list)");
+    }
+
+    /** @see org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager */
+    public void getWordChars(StringBuffer sbChars, Position pos) {
+        log.warn("null implementation of getWordChars() called!");
+    }
+
+    /** @see org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager */
+    public void hyphenate(Position pos, HyphContext hc) {
+        log.warn("null implementation of hyphenate called!");
+    }
+
+    /** @see org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager */
+    public boolean applyChanges(List oldList) {
+        log.warn("null implementation of applyChanges() called!");
+        return false;
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.LayoutManager#getChangedKnuthElements(java.util.List, int)
+     */
+    public LinkedList getChangedKnuthElements(List oldList,
+                                              int alignment) {
+        log.warn("null implementation of getChangeKnuthElement() called!");
+        return null;
+    }
+}
index 3d20b4ce4ffdfb6c563a9d7be627779d617f7a41..191fa730c817ffc842ed55940b28724009151f27 100644 (file)
@@ -20,6 +20,7 @@
 package org.apache.fop.layoutmgr.inline;
 
 import org.apache.fop.layoutmgr.inline.AlignmentContext;
+import org.apache.fop.layoutmgr.FloatBodyLayoutManager;
 import org.apache.fop.layoutmgr.FootnoteBodyLayoutManager;
 import org.apache.fop.layoutmgr.KnuthBox;
 import org.apache.fop.layoutmgr.Position;
@@ -27,6 +28,7 @@ import org.apache.fop.layoutmgr.Position;
 public class KnuthInlineBox extends KnuthBox {
     
     private FootnoteBodyLayoutManager footnoteBodyLM = null;
+    private FloatBodyLayoutManager floatBodyLM = null;
     private AlignmentContext alignmentContext = null;
 
     /**
@@ -64,16 +66,43 @@ public class KnuthInlineBox extends KnuthBox {
     }
 
     /**
-     * @return true if this box holds a reference to a FootnoteBodyLM
+     * @param fblm the FloatBodyLM this box must hold a reference to
+     */
+    public void setFloatBodyLM(FloatBodyLayoutManager fblm) {
+        floatBodyLM = fblm;
+    }
+
+    /**
+     * @return the FloatBodyLM this box holds a reference to
+     */
+    public FloatBodyLayoutManager getFloatBodyLM() {
+        return floatBodyLM;
+    }
+
+    /**
+     * @return true if this box holds a reference to an out-of-line object
      */
     public boolean isAnchor() {
+        return (footnoteBodyLM != null || floatBodyLM != null);
+    }
+
+    /**
+     * @return <code>true</code> if this box holds a reference to a footnote
+     */
+    public boolean isFootnoteAnchor() {
         return (footnoteBodyLM != null);
     }
-    
-    
+
+    /**
+     * @return <code>true</code> if this box holds a reference to a before-float
+     */
+    public boolean isFloatAnchor() {
+        return (floatBodyLM != null);
+    }
+
     /** @see java.lang.Object#toString() */
     public String toString() {
         StringBuffer sb = new StringBuffer(super.toString());
         return sb.toString();
     }
-}
\ No newline at end of file
+}
index e7571fda0cfc55ae2cc5f92c442d271a5ac3978d..aada86f8f0a01c0fe3f1f29dde7e848f98ef0e25 100644 (file)
@@ -1094,14 +1094,20 @@ public class LineLayoutManager extends InlineStackingLayoutManager
                     // create a list of the FootnoteBodyLM handling footnotes 
                     // whose citations are in this line
                     LinkedList footnoteList = new LinkedList();
+                    LinkedList floatList = new LinkedList();
                     ListIterator elementIterator = seq.listIterator(startIndex);
                     while (elementIterator.nextIndex() <= endIndex) {
                         KnuthElement element = (KnuthElement) elementIterator.next();
                         if (element instanceof KnuthInlineBox
                             && ((KnuthInlineBox) element).isAnchor()) {
-                            footnoteList.add(((KnuthInlineBox) element).getFootnoteBodyLM());
+                            if (((KnuthInlineBox) element).isFootnoteAnchor()) {
+                                footnoteList.add(((KnuthInlineBox) element).getFootnoteBodyLM());
+                            } else {
+                                floatList.add(((KnuthInlineBox) element).getFloatBodyLM());
+                            }
                         } else if (element instanceof KnuthBlockBox) {
                             footnoteList.addAll(((KnuthBlockBox) element).getFootnoteBodyLMs());
+                            floatList.addAll(((KnuthBlockBox) element).getFloatBodyLMs());
                         }
                     }
                     startIndex = endIndex + 1;
@@ -1109,7 +1115,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
                       = (LineBreakPosition) llPoss.getChosenPosition(i);
                     returnList.add(new KnuthBlockBox
                                    (lbp.lineHeight + lbp.spaceBefore + lbp.spaceAfter,
-                                    footnoteList, lbp, false));
+                                    footnoteList, floatList, lbp, false));
                     /* // add stretch and shrink to the returnlist
                     if (!seq.isInlineSequence()
                             && lbp.availableStretch != 0 || lbp.availableShrink != 0) {
index 1948a8c14e59506e4b060d04ee57a2465697d690..dcb047d89adb3614b8770a8764a6876a1301f5cd 100644 (file)
@@ -28,6 +28,9 @@
 
   <changes>
     <release version="FOP Trunk">
+      <action context="Code" dev="JM" type="add" fixes-bug="39777" due-to="Vincent Hennebert">
+        Initial support for fo:float with float="before" and float="none".
+      </action>
       <action context="Code" dev="JM" type="fix" fixes-bug="40106" due-to="Jeroen Meijer">
         Compatibility fix for GCJ (GNU Classpath): Using "UTF-16BE" instead of "UnicodeBigUnmarked"
         encoding.
index f920caea444da4074321e7491106cb08c2dff7a3..2d1d8318a63548f3de97154e131107402d9a4a3f 100644 (file)
     <description>Long text cause an IndexOutOfBounds exception</description>
     <reference>http://issues.apache.org/bugzilla/show_bug.cgi?id=39414</reference>
   </testcase>
-</disabled-testcases>
\ No newline at end of file
+  <testcase>
+    <name>fo:float float="none" should be split</name>
+    <file>float_float_none_2.xml</file>
+    <description>When float="none", it should be possible to split an fo:float
+      element on several pages.</description>
+  </testcase>
+  <testcase>
+    <name>Before-floats when no stretch available</name>
+    <file>before-float_not-deferred_no-stretch_2.xml</file>
+    <description>Whereas there is room for placing the before-float on the same
+      page as its citation, the algorithm prefers to defer it to the following
+      page.</description>
+  </testcase>
+  <testcase>
+    <name>Before-floats deferred when no stretch available</name>
+    <file>before-float_deferred_no-stretch.xml</file>
+    <description>Instead of deferring a before-float on the following page, an
+    underfull page is created so that the citation be on the same page as the
+    float.</description>
+  </testcase>
+</disabled-testcases>
diff --git a/test/layoutengine/standard-testcases/before-float_basic.xml b/test/layoutengine/standard-testcases/before-float_basic.xml
new file mode 100644 (file)
index 0000000..5cd3d47
--- /dev/null
@@ -0,0 +1,77 @@
+<?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 before-float on a simple one-page document is
+      rightly placed on the top of the page.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="normal" page-width="5in" page-height="3in">
+          <fo:region-body/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="normal" white-space-collapse="true">
+        <fo:flow flow-name="xsl-region-body">
+         <fo:block>
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+         </fo:block>
+         <fo:block>
+           This is a block with a float. This is a block with a float.
+           The float anchor is just behind this <fo:inline
+             color="blue">word</fo:inline><fo:float float="before"
+             color="red">
+             <fo:block>
+               This is the float content. This is the float content. This is
+               the float content. This is the float content. This is the float
+               content. This is the float content. This is the float content.
+               This is the float content.
+             </fo:block>
+           </fo:float>.
+           This is a block with a float. This is a block with a float.
+           This is a block with a float. This is a block with a float.
+         </fo:block>
+         <fo:block>
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+         </fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="1" xpath="count(//pageViewport)"/>
+    <!-- before-float block -->
+    <eval expected="1"
+         xpath="count(//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block)"/>
+    <eval expected="This is the float content. This is the float content. This is the float"
+          xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block/lineArea/text[1]"/>
+    <eval expected="43200"
+          xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block/@bpd"/>
+    <!-- main area -->
+    <eval expected="3"
+         xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block)"/>
+  </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/before-float_complex.xml b/test/layoutengine/standard-testcases/before-float_complex.xml
new file mode 100644 (file)
index 0000000..d9d5afa
--- /dev/null
@@ -0,0 +1,200 @@
+<?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>
+      Test for before-floats. This a complex documents with several floats; some
+      may be put on the same page as their citation, others not.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="normal" page-width="5in" page-height="3in">
+          <fo:region-body/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="normal" white-space-collapse="true">
+        <fo:flow flow-name="xsl-region-body">
+         <fo:block space-after.minimum="2pt"
+                   space-after.optimum="6pt"
+                   space-after.maximum="12pt"
+                   orphans="1"
+                   widows="1">
+           <fo:block space-after="inherit">
+             This is a block with a float. This is a block with a float.
+             This float has the number <fo:inline
+               color="blue">1</fo:inline><fo:float float="before" color="red">
+               <fo:block>
+                 This is the float content. This is the float content. This is
+                 the float content. This is the float content. This is the
+                 float content. This was the float number 1.
+               </fo:block>
+             </fo:float>.
+             This is a block with a float. This is a block with a float.
+             This is a block with a float.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block with a float. This is a block with a float.
+             This float has the number <fo:inline
+               color="blue">2</fo:inline><fo:float float="before" color="red">
+               <fo:block>
+                 This is the float content. This is the float content. This is
+                 the float content. This is the float content. This is the
+                 float content. This was the float number 2.
+               </fo:block>
+             </fo:float>.
+             This is a block with a float. This is a block with a float.
+             This is a block with a float.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block with a float. This is a block with a float.
+             This float has the number <fo:inline
+               color="blue">3</fo:inline><fo:float float="before" color="red">
+               <fo:block>
+                 This is the float content. This is the float content. This is
+                 the float content. This is the float content. This is the
+                 float content. This was the float number 3.
+               </fo:block>
+             </fo:float>.
+             This is a block with a float. This is a block with a float.
+             This is a block with a float.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block with a float. This is a block with a float.
+             This float has the number <fo:inline
+               color="blue">4</fo:inline><fo:float float="before" color="red">
+               <fo:block>
+                 This is the float content. This is the float content. This is
+                 the float content. This is the float content. This is the
+                 float content. This was the float number 4.
+               </fo:block>
+             </fo:float>.
+             This is a block with a float. This is a block with a float.
+             This is a block with a float.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block with a float. This is a block with a float.
+             This float has the number <fo:inline
+               color="blue">5</fo:inline><fo:float float="before" color="red">
+               <fo:block>
+                 This is the float content. This is
+                 the float content. This is the float content. This is the
+                 float content. This is the float content. This is the float
+                 content. This is the float content. This is the float content.
+                 This is the float content. This is the float content. This is
+                 the float content. This is the float content. This is the
+                 float content. This is the float content. This is the float
+                 content. This is the float content. This is the float content.
+                 This was the float number 5.
+               </fo:block>
+             </fo:float>.
+             This is a block with a float. This is a block with a float.
+             This is a block with a float.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+           </fo:block>
+         </fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="5" xpath="count(//pageViewport)"/>
+    <!-- page 1 -->
+    <eval expected="2"
+         xpath="count(//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block)"/>
+    <!-- float number 1 -->
+    <eval expected="the float number 1."
+          xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block[1]/lineArea[3]/text"/>
+    <eval expected="43200"
+          xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block[1]/@bpd"/>
+    <!-- float number 2 -->
+    <eval expected="the float number 2."
+          xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block[2]/lineArea[3]/text"/>
+    <eval expected="43200"
+          xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block[2]/@bpd"/>
+    <!-- page 2 -->
+    <eval expected="1"
+         xpath="count(//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block)"/>
+    <!-- float number 3 -->
+    <eval expected="the float number 3."
+          xpath="//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block/lineArea[3]/text"/>
+    <eval expected="43200"
+          xpath="//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block/@bpd"/>
+    <!-- page 3 -->
+    <eval expected="1"
+         xpath="count(//pageViewport[3]/page/regionViewport/regionBody/beforeFloat/block)"/>
+    <!-- float number 4 -->
+    <eval expected="the float number 4."
+          xpath="//pageViewport[3]/page/regionViewport/regionBody/beforeFloat/block/lineArea[3]/text"/>
+    <eval expected="43200"
+          xpath="//pageViewport[3]/page/regionViewport/regionBody/beforeFloat/block/@bpd"/>
+    <!-- page 4 -->
+    <eval expected="1"
+         xpath="count(//pageViewport[4]/page/regionViewport/regionBody/beforeFloat/block)"/>
+    <!-- float number 5 -->
+    <eval expected="This is the float content. This was the float number 5."
+          xpath="//pageViewport[4]/page/regionViewport/regionBody/beforeFloat/block/lineArea[7]/text"/>
+    <eval expected="100800"
+          xpath="//pageViewport[4]/page/regionViewport/regionBody/beforeFloat/block/@bpd"/>
+    <!-- page 5 -->
+    <eval expected="0"
+         xpath="count(//pageViewport[5]/page/regionViewport/regionBody/beforeFloat/block)"/>
+  </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/before-float_deferred_no-stretch.xml b/test/layoutengine/standard-testcases/before-float_deferred_no-stretch.xml
new file mode 100644 (file)
index 0000000..073b072
--- /dev/null
@@ -0,0 +1,116 @@
+<?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>
+      Test for before-floats. On a two-page document, when no stretching is
+      allowed between blocks, when the float citation is on the first page and
+      there is not enough room for the float, it should be placed on the top of
+      the second page.
+    </p>
+    <p>
+      When no stretching is available, only "too short" nodes are created. When
+      there is not enough room to place the float, a too short node is created
+      with added demerits for the deferred float. This results in higher
+      demerits than for previous too short nodes, which then are preferred
+      whereas they create underfull pages.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="normal" page-width="5in" page-height="3.1in">
+          <fo:region-body/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="normal" white-space-collapse="true">
+        <fo:flow flow-name="xsl-region-body">
+         <fo:block orphans="1" widows="1">
+           <fo:block>
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+           </fo:block>
+           <fo:block>
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+           </fo:block>
+           <fo:block>
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+           </fo:block>
+           <fo:block>
+             This is a block with a float. This is a block with a float.
+             The float anchor is just behind this <fo:inline
+               color="blue">word</fo:inline><fo:float float="before"
+               color="red">
+               <fo:block>
+                 This is the float content. This is the float content. This is
+                 the float content. This is the float content. This is the
+                 float content. This is the float content. This is the float
+                 content. This is the float content.
+               </fo:block>
+             </fo:float>.
+             This is a block with a float. This is a block with a float.
+             This is a block with a float. This is a block with a float.
+             This is a block with a float. This is a block with a float.
+             This is a block with a float. This is a block with a float.
+           </fo:block>
+           <fo:block>
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+           </fo:block>
+         </fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="2" xpath="count(//pageViewport)"/>
+    <!-- first page -->
+    <eval expected="0"
+         xpath="count(//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block)"/>
+    <!-- main area -->
+    <eval expected="4"
+         xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block/block)"/>
+    <!-- four lines in the last block -->
+    <eval expected="4"
+          xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block/block[4]/lineArea)"/>
+    <!-- second page -->
+    <!-- before-float block -->
+    <eval expected="1"
+         xpath="count(//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block)"/>
+    <eval expected="This is the float content. This is the float content. This is the float"
+          xpath="//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block/lineArea/text[1]"/>
+    <eval expected="43200"
+          xpath="//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block/@bpd"/>
+    <!-- one block in the normal content -->
+    <eval expected="2"
+         xpath="count(//pageViewport[2]/page/regionViewport/regionBody/mainReference/span/flow/block/block)"/>
+    <!-- one remaining line in the first block -->
+    <eval expected="1"
+          xpath="count(//pageViewport[2]/page/regionViewport/regionBody/mainReference/span/flow/block/block[1]/lineArea)"/>
+  </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/before-float_deferred_stretch.xml b/test/layoutengine/standard-testcases/before-float_deferred_stretch.xml
new file mode 100644 (file)
index 0000000..c1d5f35
--- /dev/null
@@ -0,0 +1,108 @@
+<?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>
+      Test for before-floats. On a two-page document, when some stretching is
+      allowed between blocks, when the float citation is on the first page and
+      there is not enough room for the float, it should be placed on the top of
+      the second page.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="normal" page-width="5in" page-height="3in">
+          <fo:region-body/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="normal" white-space-collapse="true">
+        <fo:flow flow-name="xsl-region-body">
+         <fo:block space-after.minimum="2pt"
+                   space-after.optimum="6pt"
+                   space-after.maximum="16pt"
+                   orphans="1"
+                   widows="1">
+           <fo:block space-after="inherit">
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block with a float. This is a block with a float.
+             The float anchor is just behind this <fo:inline
+               color="blue">word</fo:inline><fo:float float="before"
+               color="red">
+               <fo:block>
+                 This is the float content. This is the float content. This is
+                 the float content. This is the float content. This is the
+                 float content. This is the float content. This is the float
+                 content. This is the float content.
+               </fo:block>
+             </fo:float>.
+             This is a block with a float. This is a block with a float.
+             This is a block with a float.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+           </fo:block>
+         </fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="2" xpath="count(//pageViewport)"/>
+    <!-- first page -->
+    <eval expected="0"
+         xpath="count(//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block)"/>
+    <!-- main area -->
+    <eval expected="4"
+         xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block/block)"/>
+    <!-- only one line in the last block -->
+    <eval expected="1"
+          xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block/block[4]/lineArea)"/>
+    <!-- second page -->
+    <!-- before-float block -->
+    <eval expected="1"
+         xpath="count(//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block)"/>
+    <eval expected="This is the float content. This is the float content. This is the float"
+          xpath="//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block/lineArea/text[1]"/>
+    <eval expected="43200"
+          xpath="//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block/@bpd"/>
+    <!-- one block in the normal content -->
+    <eval expected="1"
+         xpath="count(//pageViewport[2]/page/regionViewport/regionBody/mainReference/span/flow/block/block)"/>
+    <!-- three remaining lines in the normal content -->
+    <eval expected="3"
+          xpath="count(//pageViewport[2]/page/regionViewport/regionBody/mainReference/span/flow/block/block[1]/lineArea)"/>
+  </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/before-float_not-deferred_no-stretch_1.xml b/test/layoutengine/standard-testcases/before-float_not-deferred_no-stretch_1.xml
new file mode 100644 (file)
index 0000000..f9caea7
--- /dev/null
@@ -0,0 +1,99 @@
+<?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>
+      Test for before-floats. On a two-page document, when no stretching is
+      allowed between blocks, when the float citation is on the first page and
+      there is enough room for the float, it should be placed on the top of the
+      first page.
+    </p>
+    <p>
+      When no stretching is available, only "too short" nodes are created. There
+      is no feasible break. The result regarding floats should be the same,
+      however.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="normal" page-width="5in" page-height="3.1in">
+          <fo:region-body/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="normal" white-space-collapse="true">
+        <fo:flow flow-name="xsl-region-body">
+         <fo:block>
+           This is a block with a float. This is a block with a float.
+           The float anchor is just behind this <fo:inline
+             color="blue">word</fo:inline><fo:float float="before"
+             color="red">
+             <fo:block>
+               This is the float content. This is the float content. This is
+               the float content. This is the float content. This is the float
+               content. This is the float content. This is the float content.
+               This is the float content.
+             </fo:block>
+           </fo:float>.
+           This is a block with a float. This is a block with a float.
+           This is a block with a float. This is a block with a float.
+         </fo:block>
+         <fo:block>
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+         </fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="2" xpath="count(//pageViewport)"/>
+    <!-- first page -->
+    <!-- before-float block -->
+    <eval expected="1"
+         xpath="count(//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block)"/>
+    <eval expected="This is the float content. This is the float content. This is the float"
+          xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block/lineArea/text[1]"/>
+    <eval expected="43200"
+          xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block/@bpd"/>
+    <!-- main area -->
+    <eval expected="2"
+         xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block)"/>
+    <!-- only eight lines in the last block -->
+    <eval expected="8"
+          xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block[2]/lineArea)"/>
+    <!-- second page -->
+    <eval expected="0"
+         xpath="count(//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block)"/>
+    <!-- four remaining lines from the previous last block -->
+    <eval expected="4"
+          xpath="count(//pageViewport[2]/page/regionViewport/regionBody/mainReference/span/flow/block[1]/lineArea)"/>
+  </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/before-float_not-deferred_no-stretch_2.xml b/test/layoutengine/standard-testcases/before-float_not-deferred_no-stretch_2.xml
new file mode 100644 (file)
index 0000000..c245d4f
--- /dev/null
@@ -0,0 +1,96 @@
+<?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 does not work. There is a before-float and a block of normal
+      text with no stretching. Normally the float should be on the first page.
+    </p>
+    <p>
+      Without the before-float, the normal content would fit on one page. With
+      the float, two pages are necessary. As there is no available stretching
+      the algorithm can only create "too short" nodes. In such a case it prefers
+      to defer the float on the last page, instead of putting it on the first
+      page and splitting the normal content on two pages. This should be
+      corrected eventually.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="normal" page-width="5in" page-height="3.1in">
+          <fo:region-body/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="normal" white-space-collapse="true">
+        <fo:flow flow-name="xsl-region-body">
+         <fo:block>
+           This is a block with a float. This is a block with a float.
+           The float anchor is just behind this <fo:inline
+             color="blue">word</fo:inline><fo:float float="before"
+             color="red">
+             <fo:block>
+               This is the float content. This is the float content. This is
+               the float content. This is the float content. This is the float
+               content. This is the float content. This is the float content.
+               This is the float content.
+             </fo:block>
+           </fo:float>.
+           This is a block with a float. This is a block with a float.
+           This is a block with a float. This is a block with a float.
+         </fo:block>
+         <fo:block>
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+           This is a block without a float. This is a block without a float.
+         </fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="2" xpath="count(//pageViewport)"/>
+    <!-- first page -->
+    <!-- before-float block -->
+    <eval expected="1"
+         xpath="count(//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block)"/>
+    <eval expected="This is the float content. This is the float content. This is the float"
+          xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block/lineArea/text[1]"/>
+    <eval expected="43200"
+          xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block/@bpd"/>
+    <!-- main area -->
+    <eval expected="2"
+         xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block)"/>
+    <!-- second page -->
+    <eval expected="0"
+         xpath="count(//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block)"/>
+    <!-- three remaining lines on the second page -->
+    <eval expected="3"
+         xpath="count(//pageViewport[2]/page/regionViewport/regionBody/mainReference/span/flow/block[1]/lineArea)"/>
+  </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/before-float_not-deferred_stretch.xml b/test/layoutengine/standard-testcases/before-float_not-deferred_stretch.xml
new file mode 100644 (file)
index 0000000..1f7bb75
--- /dev/null
@@ -0,0 +1,100 @@
+<?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>
+      Test for before-floats. On a two-page document, when some stretching is
+      allowed between blocks, when the float citation is on the first page and
+      there is enough room for the float, it should be placed on the top of the
+      first page.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="normal" page-width="5in" page-height="3in">
+          <fo:region-body/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="normal" white-space-collapse="true">
+        <fo:flow flow-name="xsl-region-body">
+         <fo:block space-after.minimum="6pt"
+                   space-after.optimum="10pt"
+                   space-after.maximum="16pt"
+                   orphans="1"
+                   widows="1">
+           <fo:block space-after="inherit">
+             This is a block with a float. This is a block with a float.
+             The float anchor is just behind this <fo:inline
+               color="blue">word</fo:inline><fo:float float="before"
+               color="red">
+               <fo:block>
+                 This is the float content. This is the float content. This is
+                 the float content. This is the float content. This is the
+                 float content. This is the float content. This is the float
+                 content. This is the float content.
+               </fo:block>
+             </fo:float>.
+             This is a block with a float. This is a block with a float.
+             This is a block with a float. This is a block with a float.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+             This is a block without a float. This is a block without a float.
+           </fo:block>
+         </fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="2" xpath="count(//pageViewport)"/>
+    <!-- first page -->
+    <!-- before-float block -->
+    <eval expected="1"
+         xpath="count(//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block)"/>
+    <eval expected="This is the float content. This is the float content. This is the float"
+          xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block/lineArea/text[1]"/>
+    <eval expected="43200"
+          xpath="//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block/@bpd"/>
+    <!-- main area -->
+    <eval expected="3"
+         xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block/block)"/>
+    <!-- only two lines in the last block -->
+    <eval expected="2"
+          xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block/block[3]/lineArea)"/>
+    <!-- second page -->
+    <eval expected="0"
+         xpath="count(//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block)"/>
+  </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/float_float_none_1.xml b/test/layoutengine/standard-testcases/float_float_none_1.xml
new file mode 100644 (file)
index 0000000..5efdb7c
--- /dev/null
@@ -0,0 +1,81 @@
+<?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 fo:float objects with a "float" property of
+      "none" are typeset in the normal flow of elements.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="normal" page-width="5in" page-height="3in">
+          <fo:region-body/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="normal" white-space-collapse="true">
+        <fo:flow flow-name="xsl-region-body">
+         <fo:block space-after.minimum="6pt" space-after.optimum="10pt"
+           space-after.maximum="12pt">
+           <fo:block space-after="inherit">
+             This is a block containing no before-float. This is a block
+             containing no before-float. This is a block containing no
+             before-float. This is a block containing no before-float. This is
+             a block containing no before-float. This is a block containing no
+             before-float.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block containing a before-float with "float" set to
+             "none". The float anchor is just behind this <fo:inline
+               color="blue">word</fo:inline><fo:float float="none" color="red">
+               <fo:block>
+                 This is the float content. This is the float content. This is
+                 the float content. This is the float content. This is the
+                 float content. This is the float content. This is the float
+                 content.
+               </fo:block>
+             </fo:float>
+             This is the end of the block containing the float. This is the
+             end of the block containing the float. This is the end of the
+             block containing the float.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block containing no before-float. This is a block
+             containing no before-float. This is a block containing no
+             before-float. This is a block containing no before-float. This is
+             a block containing no before-float. This is a block containing no
+             before-float.
+           </fo:block>
+         </fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="2" xpath="count(//pageViewport)"/>
+    <!-- block with footnote -->
+    <eval expected='This is a block containing a before-float with "float" set to "none".'
+      xpath="//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block[1]/block[2]/lineArea[1]/text[1]"/>
+    <eval expected="This is the float content. This is the float content. This is the float"
+      xpath="//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block[1]/block[2]/block[1]/lineArea[1]/text[1]"/>
+    <eval expected="43200"
+      xpath="//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block[1]/block[2]/block[1]/@bpd"/>
+  </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/float_float_none_2.xml b/test/layoutengine/standard-testcases/float_float_none_2.xml
new file mode 100644 (file)
index 0000000..90f24fd
--- /dev/null
@@ -0,0 +1,83 @@
+<?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>
+    <!-- don't work, not implemented, TODO -->
+    <p>
+      This test checks that fo:float objects with a "float" property of
+      "none" are typeset in the normal flow of elements, and normally broken
+      across pages.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="normal" page-width="5in" page-height="2in">
+          <fo:region-body/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="normal" white-space-collapse="true">
+        <fo:flow flow-name="xsl-region-body">
+         <fo:block space-after.minimum="6pt" space-after.optimum="10pt"
+           space-after.maximum="12pt">
+           <fo:block space-after="inherit">
+             This is a block containing no fo:float. This is a block
+             containing no fo:float. This is a block containing no fo:float.
+             This is a block containing no fo:float. This is a block
+             containing no fo:float. This is a block containing no fo:float.
+             This is a block containing no fo:float. This is a block
+             containing no fo:float.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block containing an fo:float with "float" set to
+             "none", which will be split on two pages. The float anchor is
+             just behind this <fo:inline
+               color="blue">word</fo:inline><fo:float float="none" color="red">
+               <fo:block>
+                 This is the float content. This is the float content. This is
+                 the float content. This is the float content. This is the
+                 float content. This is the float content. This is the float
+                 content.
+               </fo:block>
+             </fo:float>
+             This is the end of the block containing the float. This is the
+             end of the block containing the float. This is the end of the
+             block containing the float.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block containing no fo:float. This is a block
+             containing no fo:float. This is a block containing no fo:float.
+             This is a block containing no fo:float. This is a block
+             containing no fo:float. This is a block containing no fo:float.
+           </fo:block>
+         </fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="2" xpath="count(//pageViewport)"/>
+    <eval expected='This is a block containing a before-float with "float" set to "none".'
+      xpath="//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block[1]/block[2]/lineArea[1]/text[1]"/>
+    <eval expected="This is the float content. This is the float content. This is the float"
+      xpath="//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block[1]/block[2]/block[1]/lineArea[1]/text[1]"/>
+    <eval expected="43200"
+      xpath="//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block[1]/block[2]/block[1]/@bpd"/>
+  </checks>
+</testcase>