]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Patch for the implementation of the improved float-placement algorithm Temp_Floats Temp_Floats
authorVincent Hennebert <vhennebert@apache.org>
Mon, 13 Nov 2006 09:39:19 +0000 (09:39 +0000)
committerVincent Hennebert <vhennebert@apache.org>
Mon, 13 Nov 2006 09:39:19 +0000 (09:39 +0000)
Changes:
- merge in changes up to revision 432403 of the trunk
- extract LineBreakingAlgorithm and LineBreakPosition from LineLayoutManager and
  put them in the breaking subpackage (was in preparation for the side-floats
  implementation)

New features:
- minimum accepted fill ratio for pages: underfull pages which have at least
  this fill ratio are considered to be feasible breaks
- float-only pages; this may be parameterized
- big floats may now be split on several pages
- shrink- and stretchability of out-of-line object are now taken into account

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

79 files changed:
src/documentation/content/xdocs/trunk/configuration.xml
src/documentation/content/xdocs/trunk/pdfx.xml
src/foschema/fop-configuration.xsd
src/java-1.4/org/apache/fop/image/ImageIOImage.java
src/java/org/apache/fop/fo/FOEventHandler.java
src/java/org/apache/fop/fo/FONode.java
src/java/org/apache/fop/fo/FOPropertyMapping.java
src/java/org/apache/fop/fo/FOText.java
src/java/org/apache/fop/fo/FOTreeBuilder.java
src/java/org/apache/fop/fo/FObj.java
src/java/org/apache/fop/fo/FObjMixed.java
src/java/org/apache/fop/fo/PropertyList.java
src/java/org/apache/fop/fo/PropertySets.java [deleted file]
src/java/org/apache/fop/fo/StaticPropertyList.java
src/java/org/apache/fop/fo/XMLWhiteSpaceHandler.java
src/java/org/apache/fop/fo/expr/FopPropValFunction.java [deleted file]
src/java/org/apache/fop/fo/expr/FromTableColumnFunction.java
src/java/org/apache/fop/fo/expr/PPColWidthFunction.java
src/java/org/apache/fop/fo/expr/PropertyParser.java
src/java/org/apache/fop/fo/flow/AbstractListItemPart.java
src/java/org/apache/fop/fo/flow/Block.java
src/java/org/apache/fop/fo/flow/Marker.java
src/java/org/apache/fop/fo/flow/RetrieveMarker.java
src/java/org/apache/fop/fo/flow/Table.java
src/java/org/apache/fop/fo/flow/TableBody.java
src/java/org/apache/fop/fo/flow/TableCell.java
src/java/org/apache/fop/fo/flow/TableColumn.java
src/java/org/apache/fop/fo/flow/TableFObj.java
src/java/org/apache/fop/fo/flow/TableFooter.java
src/java/org/apache/fop/fo/flow/TableHeader.java
src/java/org/apache/fop/fo/flow/TableRow.java
src/java/org/apache/fop/fo/pagination/PageSequence.java
src/java/org/apache/fop/fo/properties/ColumnNumberPropertyMaker.java [deleted file]
src/java/org/apache/fop/fo/properties/FontShorthandProperty.java
src/java/org/apache/fop/fonts/FontSetup.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/PageBreakingAlgorithm.java
src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java
src/java/org/apache/fop/layoutmgr/SpaceResolver.java
src/java/org/apache/fop/layoutmgr/breaking/BeforeFloatsRecord.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/breaking/ElasticLength.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/breaking/FootnotesRecord.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/breaking/LineBreakPosition.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/breaking/LineBreakingAlgorithm.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/breaking/OutOfLineRecord.java
src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java
src/java/org/apache/fop/layoutmgr/list/ListItemContentLayoutManager.java
src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java
src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java
src/java/org/apache/fop/layoutmgr/table/TableStepper.java
src/java/org/apache/fop/render/pdf/PDFSVGHandler.java
src/java/org/apache/fop/svg/PDFAElementBridge.java
src/java/org/apache/fop/svg/PDFDocumentGraphics2D.java
src/java/org/apache/fop/svg/PDFGraphics2D.java
status.xml
test/fotree/testcases/table-cell_column-number_rowspan_bug38397.fo [new file with mode: 0644]
test/java/org/apache/fop/memory/MemoryEater.java [new file with mode: 0644]
test/layoutengine/disabled-testcases.xml
test/layoutengine/standard-testcases/before-float_footnote_last-page.xml [new file with mode: 0644]
test/layoutengine/standard-testcases/before-float_large.xml [new file with mode: 0644]
test/layoutengine/standard-testcases/before-float_not-deferred_stretch.xml
test/layoutengine/standard-testcases/block_keep-together_overflow_1.xml [new file with mode: 0644]
test/layoutengine/standard-testcases/marker_font-size.xml
test/layoutengine/standard-testcases/marker_percentage-resolution.xml [new file with mode: 0644]
test/layoutengine/standard-testcases/marker_white-space-collapse.xml
test/layoutengine/standard-testcases/markers_7.xml
test/layoutengine/standard-testcases/markers_8.xml
test/layoutengine/standard-testcases/markers_9.xml
test/layoutengine/standard-testcases/table-body_basic_1.xml
test/layoutengine/standard-testcases/table-cell_background-image.xml
test/layoutengine/standard-testcases/table-cell_padding_percentages.xml
test/layoutengine/standard-testcases/table-header_in_list_bug.xml [new file with mode: 0644]
test/layoutengine/standard-testcases/table-row_background-image.xml
test/layoutengine/standard-testcases/table_background-image.xml
test/layoutengine/standard-testcases/table_padding_percentages.xml
test/xsl/fo-page-sequence-splitter.xsl [new file with mode: 0644]
test/xsl/fo-replicator.xsl [new file with mode: 0644]

index 53a48b2e8732cb15cf1dc353f2281a6f0761018b..99b92c7d377edb0b27ea028ad583b941b25b1d0d 100644 (file)
       
       <fonts....
     </renderer>]]></source>
+      <p>
+        Another (optional) setting specific to the PDF Renderer is an output color profile, an ICC 
+        color profile which indicates the target color space the PDF file is generated for. This 
+        setting is mainly used in conjunction with the <a href="pdfx.html">PDF/X</a> feature. 
+        An example:
+      </p>
+    <source><![CDATA[
+    <renderer mime="application/pdf">
+      <filterList...
+      
+      <output-profile>C:\FOP\Color\EuropeISOCoatedFOGRA27.icc</output-profile>
+      
+      <fonts....
+    </renderer>]]></source>
     </section>
     <section id="ps-renderer">
       <title>Special Settings for the PostScript Renderer</title>
index 7a526f8f658cf4848aebf3484232f4d8b29baab2..cf796c74dbf3c150c9bffc0e8e1ce16ce981fa2d 100644 (file)
@@ -108,9 +108,10 @@ Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, userAgent);
         <li>
           Don't use CMYK images without an ICC color profile. PDF/X doesn't allow mixing
           color spaces and FOP currently only properly supports the sRGB color space. However,
-          you will need to specify an output device profile (usually a CMYK profile) in the
-          configuration. sRGB won't work here since it's a display device profile, not an
-          output device profile.
+          you will need to specify an 
+          <a href="configuration.html#pdf-renderer">output device profile</a>
+          (usually a CMYK profile) in the configuration. sRGB won't work here since it's a 
+          display device profile, not an output device profile.
         </li>
         <li>
           Don't use non-RGB colors in SVG images. Same issue as with CMYK images.
index 2cc0fca56f97ea1055b2827c0e08b154a4e6362c..f1e792858873ad90d4ca4b28165ce9406cd649b1 100644 (file)
       <xsd:attribute name="version" use="optional">
         <xsd:simpleType>
           <xsd:restriction base="xsd:string">
-            <xsd:enumeration value="1.0"></xsd:enumeration>
+            <xsd:enumeration value="1.0"/>
           </xsd:restriction>
         </xsd:simpleType>
       </xsd:attribute>
     </xsd:complexType>
   </xsd:element>
-
   <xsd:complexType name="rendererType">
-  <xsd:annotation>
-    <xsd:documentation>This type is stricter than required by FOP, in that it imposes an
+    <xsd:annotation>
+      <xsd:documentation>This type is stricter than required by FOP, in that it imposes an
       order of the elements, which is not required by FOP.</xsd:documentation>
-  </xsd:annotation>
-  <xsd:sequence>
+    </xsd:annotation>
+    <xsd:sequence>
       <xsd:choice minOccurs="0">
-        <xsd:element name="filterList" maxOccurs="2">
-        <xsd:annotation>
-          <xsd:documentation>filterLists are used by the PDF renderer, MIME type
-            application/pdf.</xsd:documentation>
-        </xsd:annotation>
-        <xsd:complexType>
-            <xsd:sequence>
-              <xsd:element name="value" type="xsd:string"
-                maxOccurs="unbounded">
-              </xsd:element>
-            </xsd:sequence>
-            <xsd:attribute name="type" use="optional">
-              <xsd:simpleType>
-                <xsd:restriction base="xsd:string">
-                  <xsd:enumeration value="image"></xsd:enumeration>
-                </xsd:restriction>
-              </xsd:simpleType>
-            </xsd:attribute>
-          </xsd:complexType>
-        </xsd:element>
-        <xsd:element name="auto-rotate-landscape">
-        <xsd:annotation>
-          <xsd:documentation>auto-rotate-landscape is used by the PostScript renderer,
-          MIME type application/postscript.</xsd:documentation>
-        </xsd:annotation>
-        <xsd:simpleType>
-            <xsd:restriction base="xsd:string">
-              <xsd:enumeration value="false"></xsd:enumeration>
-              <xsd:enumeration value="true"></xsd:enumeration>
-            </xsd:restriction>
-          </xsd:simpleType>
-        </xsd:element>
         <xsd:sequence>
-        <xsd:annotation>
-          <xsd:documentation>rendering and text-rendering are used by the PCL renderer,
+          <xsd:annotation>
+            <xsd:documentation>Configuration elements used by the PDF renderer,
+          MIME type application/pdf</xsd:documentation>
+          </xsd:annotation>
+          <xsd:element name="filterList" minOccurs="0" maxOccurs="unbounded">
+            <xsd:annotation>
+              <xsd:documentation>filterLists are used by the PDF renderer, MIME type
+              application/pdf.</xsd:documentation>
+            </xsd:annotation>
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" maxOccurs="unbounded"/>
+              </xsd:sequence>
+              <xsd:attribute name="type" use="optional">
+                <xsd:simpleType>
+                  <xsd:restriction base="xsd:string">
+                    <xsd:enumeration value="default"/>
+                    <xsd:enumeration value="content"/>
+                    <xsd:enumeration value="image"/>
+                    <xsd:enumeration value="jpeg"/>
+                    <xsd:enumeration value="tiff"/>
+                    <xsd:enumeration value="font"/>
+                    <xsd:enumeration value="metadata"/>
+                    <!-- Available values: see PDFFilterList.java -->
+                  </xsd:restriction>
+                </xsd:simpleType>
+              </xsd:attribute>
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="output-profile" type="xsd:string" minOccurs="0">
+            <xsd:annotation>
+              <xsd:documentation>Output color profile used by the PDF renderer. Specifies a 
+              filename to an ICC file.</xsd:documentation>
+            </xsd:annotation>
+          </xsd:element>
+        </xsd:sequence>
+        <xsd:sequence>
+          <xsd:annotation>
+            <xsd:documentation>Configuration elements used by the PostScript renderer,
+          MIME type application/postscript</xsd:documentation>
+          </xsd:annotation>
+          <xsd:element name="auto-rotate-landscape">
+            <xsd:annotation>
+              <xsd:documentation>auto-rotate-landscape is used by the PostScript renderer,
+            MIME type application/postscript.</xsd:documentation>
+            </xsd:annotation>
+            <xsd:simpleType>
+              <xsd:restriction base="xsd:string">
+                <xsd:enumeration value="false"/>
+                <xsd:enumeration value="true"/>
+              </xsd:restriction>
+            </xsd:simpleType>
+          </xsd:element>
+        </xsd:sequence>
+        <xsd:sequence>
+          <xsd:annotation>
+            <xsd:documentation>rendering and text-rendering are used by the PCL renderer,
           MIME type application/vnd.hp-PCL</xsd:documentation>
-        </xsd:annotation>
-        <xsd:element name="rendering" minOccurs="0">
+          </xsd:annotation>
+          <xsd:element name="rendering" minOccurs="0">
             <xsd:simpleType>
               <xsd:restriction base="xsd:string">
-                <xsd:enumeration value="speed"></xsd:enumeration>
-                <xsd:enumeration value="quality"></xsd:enumeration>
+                <xsd:enumeration value="speed"/>
+                <xsd:enumeration value="quality"/>
               </xsd:restriction>
             </xsd:simpleType>
           </xsd:element>
           <xsd:element name="text-rendering" minOccurs="0">
             <xsd:simpleType>
               <xsd:restriction base="xsd:string">
-                <xsd:enumeration value="auto"></xsd:enumeration>
-                <xsd:enumeration value="bitmap"></xsd:enumeration>
+                <xsd:enumeration value="auto"/>
+                <xsd:enumeration value="bitmap"/>
               </xsd:restriction>
             </xsd:simpleType>
           </xsd:element>
         </xsd:sequence>
+        <xsd:sequence>
+          <xsd:annotation>
+            <xsd:documentation>The elements in this sequence apply only to the text renderer,
+          MIME type text/plain.</xsd:documentation>
+          </xsd:annotation>
+          <xsd:element name="encoding" type="xsd:string" default="UTF-8" minOccurs="0"/>
+        </xsd:sequence>
       </xsd:choice>
       <xsd:element name="fonts" minOccurs="0">
         <xsd:complexType>
           <xsd:sequence>
-            <xsd:element name="font" type="fontType"
-              maxOccurs="unbounded">
-            </xsd:element>
+            <xsd:element name="font" type="fontType" maxOccurs="unbounded"/>
           </xsd:sequence>
         </xsd:complexType>
       </xsd:element>
+      <xsd:element name="xml-handler" minOccurs="0" maxOccurs="unbounded">
+        <xsd:complexType>
+          <xsd:complexContent>
+            <xsd:extension base="xsd:anyType"/>
+          </xsd:complexContent>
+        </xsd:complexType>
+      </xsd:element>
     </xsd:sequence>
-    <xsd:attribute name="mime" type="MimeConstants" use="required"></xsd:attribute>
+    <xsd:attribute name="mime" type="MimeConstants" use="required"/>
   </xsd:complexType>
-
   <xsd:complexType name="fontType">
     <xsd:sequence>
-      <xsd:element name="font-triplet" type="fontTripletType" maxOccurs="unbounded"></xsd:element>
+      <xsd:element name="font-triplet" type="fontTripletType" maxOccurs="unbounded"/>
     </xsd:sequence>
-    <xsd:attribute name="metrics-url" type="xsd:anyURI" use="required"></xsd:attribute>
-    <xsd:attribute name="embed-url" type="xsd:anyURI" use="optional"></xsd:attribute>
-    <xsd:attribute name="kerning" use="optional">
+    <xsd:attribute name="metrics-url" type="xsd:anyURI" use="required"/>
+    <xsd:attribute name="embed-url" type="xsd:anyURI" use="optional"/>
+    <xsd:attribute name="kerning" use="optional" default="no">
       <xsd:simpleType>
         <xsd:restriction base="xsd:string">
-          <xsd:enumeration value="yes"></xsd:enumeration>
+          <xsd:enumeration value="yes"/>
+          <xsd:enumeration value="no"/>
         </xsd:restriction>
       </xsd:simpleType>
     </xsd:attribute>
   </xsd:complexType>
-
   <xsd:complexType name="fontTripletType">
-    <xsd:attribute name="name" type="xsd:string" use="required"></xsd:attribute>
-    <xsd:attribute name="style" type="xsd:string" use="required"></xsd:attribute>
-    <xsd:attribute name="weight" type="xsd:string" use="required"></xsd:attribute>
+    <xsd:attribute name="name" type="xsd:string" use="required"/>
+    <xsd:attribute name="style" type="xsd:string" use="required"/>
+    <xsd:attribute name="weight" type="xsd:string" use="required"/>
   </xsd:complexType>
-
   <xsd:simpleType name="MimeConstants">
     <xsd:restriction base="xsd:string">
-      <xsd:enumeration value="application/pdf"></xsd:enumeration>
-      <xsd:enumeration value="application/postscript"></xsd:enumeration>
-      <xsd:enumeration value="application/x-pcl"></xsd:enumeration>
-      <xsd:enumeration value="application/vnd.hp-PCL"></xsd:enumeration>
-      <xsd:enumeration value="application/x-afp"></xsd:enumeration>
-      <xsd:enumeration value="application/vnd.ibm.modcap"></xsd:enumeration>
-      <xsd:enumeration value="text/plain"></xsd:enumeration>
-      <xsd:enumeration value="application/rtf"></xsd:enumeration>
-      <xsd:enumeration value="text/richtext"></xsd:enumeration>
-      <xsd:enumeration value="text/rtf"></xsd:enumeration>
-      <xsd:enumeration value="application/mif"></xsd:enumeration>
-      <xsd:enumeration value="application/X-fop-awt-preview"></xsd:enumeration>
-      <xsd:enumeration value="application/X-fop-print"></xsd:enumeration>
-      <xsd:enumeration value="application/X-fop-areatree"></xsd:enumeration>
+      <xsd:enumeration value="application/pdf"/>
+      <xsd:enumeration value="application/postscript"/>
+      <xsd:enumeration value="application/x-pcl"/>
+      <xsd:enumeration value="application/vnd.hp-PCL"/>
+      <xsd:enumeration value="application/x-afp"/>
+      <xsd:enumeration value="application/vnd.ibm.modcap"/>
+      <xsd:enumeration value="text/plain"/>
+      <xsd:enumeration value="application/rtf"/>
+      <xsd:enumeration value="text/richtext"/>
+      <xsd:enumeration value="text/rtf"/>
+      <xsd:enumeration value="application/mif"/>
+      <xsd:enumeration value="application/X-fop-awt-preview"/>
+      <xsd:enumeration value="application/X-fop-print"/>
+      <xsd:enumeration value="application/X-fop-areatree"/>
+      <xsd:enumeration value="image/png"/>
+      <xsd:enumeration value="image/tiff"/>
     </xsd:restriction>
   </xsd:simpleType>
-
 </xsd:schema>
index db9262adcfe38b81cf0bb2f7886163dad5121407..463331530335cb752311cbb820e26b7c6a8b72cc 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.fop.image;
 
 // AWT
 import java.awt.Color;
+import java.awt.color.ColorSpace;
 import java.awt.image.ColorModel;
 import java.awt.image.IndexColorModel;
 import java.awt.image.BufferedImage;
@@ -121,7 +122,9 @@ public class ImageIOImage extends AbstractFopImage {
 
             ColorModel cm = imageData.getColorModel();
             this.bitsPerPixel = cm.getComponentSize(0); //only use first, we assume all are equal
-            this.colorSpace = cm.getColorSpace();
+            //this.colorSpace = cm.getColorSpace();
+            //We currently force the image to sRGB
+            this.colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
 
             int[] tmpMap = imageData.getRGB(0, 0, this.width,
                                             this.height, null, 0, this.width);
index 69cb7d06bbd758ae00aaf2cb053b41dfe0f745f4..28dd32ba0f9e81a3114935286baf6abca7754bec 100644 (file)
@@ -40,9 +40,9 @@ import org.apache.fop.fo.flow.ListBlock;
 import org.apache.fop.fo.flow.ListItem;
 import org.apache.fop.fo.flow.PageNumber;
 import org.apache.fop.fo.flow.Table;
-import org.apache.fop.fo.flow.TableColumn;
 import org.apache.fop.fo.flow.TableBody;
 import org.apache.fop.fo.flow.TableCell;
+import org.apache.fop.fo.flow.TableColumn;
 import org.apache.fop.fo.flow.TableRow;
 import org.apache.fop.fo.pagination.Flow;
 import org.apache.fop.fo.pagination.PageSequence;
@@ -78,7 +78,7 @@ public abstract class FOEventHandler {
      */
     private Set idReferences = new HashSet();
 
-    /*
+    /**
      * The property list maker.
      */
     protected PropertyListMaker propertyListMaker;
@@ -88,6 +88,11 @@ public abstract class FOEventHandler {
      */
     protected XMLWhiteSpaceHandler whiteSpaceHandler = new XMLWhiteSpaceHandler();
     
+    /**
+     * Indicates whether processing descendants of a marker
+     */
+    private boolean inMarker = false;
+    
     /**
      * Main constructor
      * @param foUserAgent the apps.FOUserAgent instance for this process
@@ -143,6 +148,23 @@ public abstract class FOEventHandler {
         return whiteSpaceHandler;
     }
 
+    /**
+     * Switch to or from marker context
+     * (used by FOTreeBuilder when processing
+     *  a marker)
+     *
+     */
+    protected void switchMarkerContext(boolean inMarker) {
+        this.inMarker = inMarker;
+    }
+    
+    /**
+     * Check whether in marker context
+     */
+    protected boolean inMarker() {
+        return this.inMarker;
+    }
+    
     /**
      * This method is called to indicate the start of a new document run.
      * @throws SAXException In case of a problem
@@ -185,9 +207,10 @@ public abstract class FOEventHandler {
     }
 
     /**
-     * This method is called to indicate the start of a new fo:flow or fo:static-content.
-     * This method also handles fo:static-content tags, because the StaticContent class
-     * is derived from the Flow class.
+     * This method is called to indicate the start of a new fo:flow 
+     * or fo:static-content.
+     * This method also handles fo:static-content tags, because the 
+     * StaticContent class is derived from the Flow class.
      *
      * @param fl Flow that is starting.
      */
@@ -219,15 +242,15 @@ public abstract class FOEventHandler {
     *
     * @param blc BlockContainer that is starting.
     */
-   public void startBlockContainer(BlockContainer blc) {
-   }
+    public void startBlockContainer(BlockContainer blc) {
+    }
 
-   /**
+    /**
     *
     * @param blc BlockContainer that is ending.
     */
-   public void endBlockContainer(BlockContainer blc) {
-   }
+    public void endBlockContainer(BlockContainer blc) {
+    }
 
     /**
      *
index be041254fc38dba98b04c25c8940d8cd3ef9cb7f..39d42423cc101decfb2a47b9a8d3168135f77135 100644 (file)
@@ -81,7 +81,6 @@ public abstract class FONode implements Cloneable {
                 throws FOPException {
         FONode foNode = (FONode) clone();
         foNode.parent = cloneparent;
-        cloneparent.addChildNode(foNode);
         return foNode;
     }
 
@@ -124,6 +123,10 @@ public abstract class FONode implements Cloneable {
     public FOEventHandler getFOEventHandler() {
         return parent.getFOEventHandler();
     }
+    
+    protected boolean inMarker() {
+        return getFOEventHandler().inMarker();
+    }
 
     /**
      * Returns the user agent for the node.
@@ -258,7 +261,7 @@ public abstract class FONode implements Cloneable {
 
     /**
      * Return an iterator over the object's child nodes starting
-     * at the pased node.
+     * at the passed node.
      * @param childNode First node in the iterator
      * @return A ListIterator or null if child node isn't a child of
      * this FObj.
@@ -581,5 +584,35 @@ public abstract class FONode implements Cloneable {
         return null;
     }
     
+    /**
+     * @return true if markers are valid children
+     */
+    protected boolean canHaveMarkers() {
+        int foId = getNameId();
+        switch (foId) {
+        case Constants.FO_BASIC_LINK:
+        case Constants.FO_BIDI_OVERRIDE:
+        case Constants.FO_BLOCK:
+        case Constants.FO_BLOCK_CONTAINER:
+        case Constants.FO_FLOW:
+        case Constants.FO_INLINE:
+        case Constants.FO_INLINE_CONTAINER:
+        case Constants.FO_LIST_BLOCK:
+        case Constants.FO_LIST_ITEM:
+        case Constants.FO_LIST_ITEM_BODY:
+        case Constants.FO_LIST_ITEM_LABEL:
+        case Constants.FO_TABLE:
+        case Constants.FO_TABLE_BODY:
+        case Constants.FO_TABLE_HEADER:
+        case Constants.FO_TABLE_FOOTER:
+        case Constants.FO_TABLE_CELL:
+        case Constants.FO_TABLE_AND_CAPTION:
+        case Constants.FO_TABLE_CAPTION:
+        case Constants.FO_WRAPPER:
+            return true;
+        default:
+            return false;
+        }
+    }
 }
 
index 7c1bbc773547a33d07a53c5b44e7e20dec857fab..bf23e4d975596b04fd0a05e49c8dbfeba83b78da 100644 (file)
@@ -30,7 +30,7 @@ import org.apache.fop.fo.properties.BorderWidthPropertyMaker;
 import org.apache.fop.fo.properties.BoxPropShorthandParser;
 import org.apache.fop.fo.properties.CharacterProperty;
 import org.apache.fop.fo.properties.ColorProperty;
-import org.apache.fop.fo.properties.ColumnNumberPropertyMaker;
+import org.apache.fop.fo.flow.TableFObj.ColumnNumberPropertyMaker;
 import org.apache.fop.fo.properties.CondLengthProperty;
 import org.apache.fop.fo.properties.CorrespondingPropertyMaker;
 import org.apache.fop.fo.properties.DimensionPropertyMaker;
@@ -326,11 +326,13 @@ public final class FOPropertyMapping implements Constants {
      * @return a propId that matches the property name.
      */
     public static int getPropertyId(String name) {
-        Integer i = (Integer) s_htPropNames.get(name);
-        if (i == null) {
-            return -1;
+        if (name != null) {
+            Integer i = (Integer) s_htPropNames.get(name);
+            if (i != null) {
+                return i.intValue();
+            }
         }
-        return i.intValue();
+        return -1;
     }
 
     /**
@@ -339,11 +341,13 @@ public final class FOPropertyMapping implements Constants {
      * @return a subpropId that matches the subproperty name.
      */
     public static int getSubPropertyId(String name) {
-        Integer i = (Integer) s_htSubPropNames.get(name);
-        if (i == null) {
-            return -1;
+        if (name != null) {
+            Integer i = (Integer) s_htSubPropNames.get(name);
+            if (i != null) {
+                return i.intValue();
+            }
         }
-        return i.intValue();
+        return -1;
     }
 
     // returns a property, compound, or property.compound name
@@ -2263,8 +2267,8 @@ public final class FOPropertyMapping implements Constants {
 
         sub = new LengthProperty.Maker(CP_INLINE_PROGRESSION_DIRECTION);
         sub.setDefault("0pt");
-        m.addSubpropMaker(sub);
         sub.setByShorthand(true);
+        m.addSubpropMaker(sub);
         addPropertyMaker("border-separation", m);
 
         // border-start-precedence
index 8b578b1463d64736cbba9af054da2f26bee02a3a..ec8fcfb7e7e48bdd8a4a5832ccf8d61b8cd7b455 100644 (file)
@@ -150,6 +150,23 @@ public class FOText extends FONode {
         this.ca = nca;
      }
 
+    /**
+     * @see org.apache.fop.fo.FONode#clone(FONode, boolean)
+     */
+    public FONode clone(FONode parent, boolean removeChildren)
+        throws FOPException {
+        FOText ft = (FOText) super.clone(parent, removeChildren);
+        if (removeChildren) {
+            //not really removing, but just make sure the char array 
+            //pointed to is really a different one
+            if (ca != null) {
+                ft.ca = new char[ca.length];
+                System.arraycopy(ca, 0, ft.ca, 0, ca.length);
+            }
+        }
+        return ft;
+    }
+
     /**
      * @see org.apache.fop.fo.FObj#bind(PropertyList)
      */
@@ -171,8 +188,8 @@ public class FOText extends FONode {
 
     /** @see org.apache.fop.fo.FONode#endOfNode() */
     protected void endOfNode() throws FOPException {
-        createBlockPointers();
         textTransform();
+        getFOEventHandler().characters(ca, startIndex, endIndex);
     }
 
     /**
@@ -210,28 +227,11 @@ public class FOText extends FONode {
     }
 
      /**
-     * This method is run as part of the Constructor, to create xref pointers to
+     * This method is run as part of the ancestor Block's flushText(), to create xref pointers to
      * the previous FOText objects within the same Block
      */
-    private void createBlockPointers() {
-        // build pointers between the FOText objects withing the same Block
-        //
-        // find the ancestorBlock of the current node
-        FONode ancestorFONode = this;
-        while (this.ancestorBlock == null) {
-            ancestorFONode = ancestorFONode.parent;
-            if (ancestorFONode instanceof org.apache.fop.fo.pagination.Title) {
-                return;
-            } else if (ancestorFONode instanceof org.apache.fop.fo.flow.Marker) {
-                return;
-            } else if (ancestorFONode instanceof Root) {
-                getLogger().warn("Unexpected: fo:text with no fo:block ancestor. The text is: " 
-                        + new String(ca));
-                return;
-            } else if (ancestorFONode instanceof Block) {
-                this.ancestorBlock = (Block)ancestorFONode;
-            }
-        }
+    protected void createBlockPointers(Block ancestorBlock) {
+        this.ancestorBlock = ancestorBlock;
         // if the last FOText is a sibling, point to it, and have it point here
         if (lastFOTextProcessed != null) {
             if (lastFOTextProcessed.ancestorBlock == this.ancestorBlock) {
@@ -252,7 +252,8 @@ public class FOText extends FONode {
      * text-transform property.
      */
     private void textTransform() {
-        if (textTransform == Constants.EN_NONE) {
+        if (getFOEventHandler().inMarker() 
+                || textTransform == Constants.EN_NONE) {
             return;
         }
         for (int i = 0; i < endIndex; i++) {
index 0f2874b4b3a27733f3ba4f006b40917d18253e1d..4c754520ddedb5c9ce46e58ab99e6f52d588c3d4 100644 (file)
@@ -246,7 +246,7 @@ public class FOTreeBuilder extends DefaultHandler {
      */
     public FormattingResults getResults() {
         if (getEventHandler() instanceof AreaTreeHandler) {
-            return ((AreaTreeHandler)getEventHandler()).getResults();
+            return ((AreaTreeHandler) getEventHandler()).getResults();
         } else {
             //No formatting results available for output formats no 
             //involving the layout engine.
@@ -268,6 +268,11 @@ public class FOTreeBuilder extends DefaultHandler {
          * Current propertyList for the node being handled.
          */
         protected PropertyList currentPropertyList;
+        
+        /**
+         * Current marker nesting-depth
+         */
+        private int nestedMarkerDepth = 0;
 
         /**
          * SAX Handler for the start of an element
@@ -278,16 +283,17 @@ public class FOTreeBuilder extends DefaultHandler {
 
             /* the node found in the FO document */
             FONode foNode;
-            PropertyList propertyList;
+            PropertyList propertyList = null;
 
             // Check to ensure first node encountered is an fo:root
             if (rootFObj == null) {
                 if (!namespaceURI.equals(FOElementMapping.URI) 
                     || !localName.equals("root")) {
-                    throw new SAXException(new ValidationException(
-                        "Error: First element must be the fo:root formatting object. Found " 
-                            + FONode.getNodeString(namespaceURI, localName) + " instead."
-                            + " Please make sure you're producing a valid XSL-FO document."));
+                    throw new ValidationException(
+                        "Error: First element must be the fo:root formatting object. "
+                        + "Found " + FONode.getNodeString(namespaceURI, localName) 
+                        + " instead."
+                        + " Please make sure you're producing a valid XSL-FO document.");
                 }
             } else { // check that incoming node is valid for currentFObj
                 if (namespaceURI.equals(FOElementMapping.URI)) {
@@ -301,7 +307,8 @@ public class FOTreeBuilder extends DefaultHandler {
                 }
             }
             
-            ElementMapping.Maker fobjMaker = findFOMaker(namespaceURI, localName);
+            ElementMapping.Maker fobjMaker = 
+                findFOMaker(namespaceURI, localName);
 
             try {
                 foNode = fobjMaker.make(currentFObj);
@@ -309,8 +316,17 @@ public class FOTreeBuilder extends DefaultHandler {
                     rootFObj = (Root) foNode;
                     rootFObj.setFOEventHandler(foEventHandler);
                 }
-                propertyList = foNode.createPropertyList(currentPropertyList, foEventHandler);
-                foNode.processNode(localName, getEffectiveLocator(), attlist, propertyList);
+                propertyList = foNode.createPropertyList(
+                                    currentPropertyList, foEventHandler);
+                foNode.processNode(localName, getEffectiveLocator(), 
+                                    attlist, propertyList);
+                if (foNode.getNameId() == Constants.FO_MARKER) {
+                    if (foEventHandler.inMarker()) {
+                        nestedMarkerDepth++;
+                    } else {
+                        foEventHandler.switchMarkerContext(true);
+                    }
+                }
                 foNode.startOfNode();
             } catch (IllegalArgumentException e) {
                 throw new SAXException(e);
@@ -321,11 +337,13 @@ public class FOTreeBuilder extends DefaultHandler {
                 ContentHandler subHandler = chFactory.createContentHandler();
                 if (subHandler instanceof ObjectSource 
                         && foNode instanceof ObjectBuiltListener) {
-                    ((ObjectSource)subHandler).setObjectBuiltListener((ObjectBuiltListener)foNode);
+                    ((ObjectSource) subHandler).setObjectBuiltListener(
+                            (ObjectBuiltListener) foNode);
                 }
                 
                 subHandler.startDocument();
-                subHandler.startElement(namespaceURI, localName, rawName, attlist);
+                subHandler.startElement(namespaceURI, localName, 
+                        rawName, attlist);
                 depth = 1;
                 delegate = subHandler;
             }
@@ -335,7 +353,7 @@ public class FOTreeBuilder extends DefaultHandler {
             }
 
             currentFObj = foNode;
-            if (propertyList != null) {
+            if (propertyList != null && !foEventHandler.inMarker()) {
                 currentPropertyList = propertyList;
             }
         }
@@ -356,11 +374,24 @@ public class FOTreeBuilder extends DefaultHandler {
                         + " (" + currentFObj.getNamespaceURI() 
                         + ") vs. " + localName + " (" + uri + ")");
             }
+            
             currentFObj.endOfNode();
-
-            if (currentPropertyList.getFObj() == currentFObj) {
-                currentPropertyList = currentPropertyList.getParentPropertyList();
+            
+            if (currentPropertyList != null
+                    && currentPropertyList.getFObj() == currentFObj
+                    && !foEventHandler.inMarker()) {
+                currentPropertyList = 
+                    currentPropertyList.getParentPropertyList();
+            }
+            
+            if (currentFObj.getNameId() == Constants.FO_MARKER) {
+                if (nestedMarkerDepth == 0) {
+                    foEventHandler.switchMarkerContext(false);
+                } else {
+                    nestedMarkerDepth--;
+                }
             }
+            
             if (currentFObj.getParent() == null) {
                 log.debug("endElement for top-level " + currentFObj.getName());
             }
@@ -373,19 +404,15 @@ public class FOTreeBuilder extends DefaultHandler {
          */
         public void characters(char[] data, int start, int length) 
             throws FOPException {
-                if (currentFObj != null) {
-                    currentFObj.addCharacters(data, start, start + length, 
-                            currentPropertyList, getEffectiveLocator());
-                }
+            if (currentFObj != null) {
+                currentFObj.addCharacters(data, start, start + length, 
+                        currentPropertyList, getEffectiveLocator());
+            }
         }
 
         public void endDocument() throws SAXException {
             currentFObj = null;
-        }
-
-        
-        
+        }        
     }
-    
 }
 
index 29278b1b8da177c0b9679cff2627f267f92d3671..1372b65c4df62c5ff1b8078574095e024f629aa3 100644 (file)
@@ -20,7 +20,6 @@
 package org.apache.fop.fo;
 
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
@@ -119,11 +118,15 @@ public abstract class FObj extends FONode implements Constants {
      * @see org.apache.fop.fo.FONode#processNode
      */
     public void processNode(String elementName, Locator locator, 
-                            Attributes attlist, PropertyList pList) throws FOPException {
+                            Attributes attlist, PropertyList pList) 
+                    throws FOPException {
         setLocator(locator);
         pList.addAttributesToList(attlist);
-        pList.setWritingMode();
-        bind(pList);
+        if (!inMarker()
+                || "marker".equals(elementName)) {
+            pList.setWritingMode();
+            bind(pList);
+        }
     }
 
     /**
@@ -154,7 +157,7 @@ public abstract class FObj extends FONode implements Constants {
      * @throws ValidationException if the ID is already defined elsewhere
      */
     protected void checkId(String id) throws ValidationException {
-        if (!id.equals("")) {
+        if (!inMarker() && !id.equals("")) {
             Set idrefs = getFOEventHandler().getIDReferences();
             if (!idrefs.contains(id)) {
                 idrefs.add(id);
@@ -178,13 +181,15 @@ public abstract class FObj extends FONode implements Constants {
      * @see org.apache.fop.fo.FONode#addChildNode(FONode)
      */
     protected void addChildNode(FONode child) throws FOPException {
-        if (PropertySets.canHaveMarkers(getNameId()) && child.getNameId() == FO_MARKER) {
-            addMarker((Marker)child);
+        if (canHaveMarkers() && child.getNameId() == FO_MARKER) {
+            addMarker((Marker) child);
         } else {
             ExtensionAttachment attachment = child.getExtensionAttachment();
             if (attachment != null) {
-                //This removes the element from the normal children, so no layout manager
-                //is being created for them as they are only additional information.
+                /* This removes the element from the normal children, 
+                 * so no layout manager is being created for them 
+                 * as they are only additional information.
+                 */
                 addExtensionAttachment(attachment);
             } else {
                 if (childNodes == null) {
@@ -195,6 +200,10 @@ public abstract class FObj extends FONode implements Constants {
         }
     }
 
+    protected static void addChildTo(FONode child, FObj parent) throws FOPException {
+        parent.addChildNode(child);
+    }
+    
     /** @see org.apache.fop.fo.FONode#removeChild(org.apache.fop.fo.FONode) */
     public void removeChild(FONode child) {
         if (childNodes != null) {
@@ -250,6 +259,20 @@ public abstract class FObj extends FONode implements Constants {
         return null;
     }
 
+    /**
+     * Return a FONode based on the index in the list of childNodes.
+     * @param nodeIndex index of the node to return
+     * @return the node or null if the index is invalid
+     */
+    public FONode getChildNodeAt(int nodeIndex) {
+        if (childNodes != null) {
+            if (nodeIndex >= 0 && nodeIndex < childNodes.size()) {
+                return (FONode) childNodes.get(nodeIndex);
+            }
+        }
+        return null;
+    }
+
     /**
      * Notifies a FObj that one of it's children is removed.
      * This method is subclassed by Block to clear the firstInlineChild variable.
index 7efcbf59f279ea3a75e8ea3fea25cc256635f971..84f27fbb991399e6a18a8b78e506e140c3078b1f 100644 (file)
@@ -42,7 +42,7 @@ public abstract class FObjMixed extends FObj {
     protected FObjMixed(FONode parent) {
         super(parent);
     }
-
+    
     /** @see org.apache.fop.fo.FONode */
     protected void addCharacters(char[] data, int start, int end,
                                  PropertyList pList,
@@ -50,7 +50,9 @@ public abstract class FObjMixed extends FObj {
         if (ft == null) {
             ft = new FOText(this);
             ft.setLocator(locator);
-            ft.bind(pList);
+            if (!inMarker()) {
+                ft.bind(pList);
+            }
         }
         ft.addCharacters(data, start, end, null, null);
     }
@@ -58,11 +60,26 @@ public abstract class FObjMixed extends FObj {
     /** @see org.apache.fop.fo.FONode#endOfNode() */
     protected void endOfNode() throws FOPException {
         flushText();
-        getFOEventHandler().whiteSpaceHandler
-            .handleWhiteSpace(this, currentTextNode);
+        if (!inMarker()
+                || getNameId() == FO_MARKER) {
+            getFOEventHandler().whiteSpaceHandler
+                .handleWhiteSpace(this, currentTextNode);
+        }
         super.endOfNode();
     }
 
+    /**
+     * Handles white-space for the node that is passed in, 
+     * starting at its current text-node
+     * (used by RetrieveMarker to trigger 'end-of-node' white-space
+     *  handling)
+     * @param fobj  the node for which to handle white-space
+     */
+    protected static void handleWhiteSpaceFor(FObjMixed fobj) {
+        fobj.getFOEventHandler().getXMLWhiteSpaceHandler()
+            .handleWhiteSpace(fobj, fobj.currentTextNode);
+    }
+    
     /**
      * Adds accumulated text as one FOText instance.
      * Makes sure that nested calls to itself do nothing.
@@ -72,8 +89,29 @@ public abstract class FObjMixed extends FObj {
        if (ft != null) {
             FOText lft = ft;
             ft = null;
+            if (getNameId() == FO_BLOCK) {
+                lft.createBlockPointers((org.apache.fop.fo.flow.Block) this);
+            } else if (getNameId() != FO_MARKER
+                    && getNameId() != FO_TITLE
+                    && getNameId() != FO_BOOKMARK_TITLE) {
+                FONode fo = parent;
+                int foNameId = fo.getNameId();
+                while (foNameId != FO_BLOCK
+                        && foNameId != FO_MARKER
+                        && foNameId != FO_TITLE
+                        && foNameId != FO_BOOKMARK_TITLE
+                        && foNameId != FO_PAGE_SEQUENCE) {
+                    fo = fo.getParent();
+                    foNameId = fo.getNameId();
+                }
+                if (foNameId == FO_BLOCK) {
+                    lft.createBlockPointers((org.apache.fop.fo.flow.Block) fo);
+                } else if (foNameId == FO_PAGE_SEQUENCE) {
+                    log.error("Could not create block pointers."
+                            + " FOText w/o Block ancestor.");
+                }
+            }
             lft.endOfNode();
-            getFOEventHandler().characters(lft.ca, lft.startIndex, lft.endIndex);
             addChildNode(lft);
         }
     }
@@ -83,15 +121,18 @@ public abstract class FObjMixed extends FObj {
      */
     protected void addChildNode(FONode child) throws FOPException {
         flushText();
-        if (child instanceof FOText || child.getNameId() == FO_CHARACTER) {
-            if (currentTextNode == null) {
-                currentTextNode = child;
+        if (!inMarker() 
+                || getNameId() == FO_MARKER) {
+            if (child instanceof FOText || child.getNameId() == FO_CHARACTER) {
+                if (currentTextNode == null) {
+                    currentTextNode = child;
+                }
+            } else {
+                // handle white-space for all text up to here
+                getFOEventHandler().whiteSpaceHandler
+                    .handleWhiteSpace(this, currentTextNode, child);
+                currentTextNode = null;
             }
-        } else {
-            // handle white-space for all text up to here
-            getFOEventHandler().whiteSpaceHandler
-                .handleWhiteSpace(this, currentTextNode, child);
-            currentTextNode = null;
         }
         super.addChildNode(child);
     }
index 6fdd1bf2191ba09b718a723422586f86fd5e8921..0600601c75972db33059bcaf5801d15d91c8259f 100644 (file)
@@ -169,8 +169,11 @@ public abstract class PropertyList {
                          boolean bTryDefault) throws PropertyException {
 
         PropertyMaker propertyMaker = findMaker(propId & Constants.PROPERTY_MASK);
-        return propertyMaker.get(propId & Constants.COMPOUND_MASK, this,
-                                     bTryInherit, bTryDefault);
+        if (propertyMaker != null) {
+            return propertyMaker.get(propId & Constants.COMPOUND_MASK, this,
+                                         bTryInherit, bTryDefault);
+        }
+        return null;
     }
 
     /**
@@ -260,8 +263,11 @@ public abstract class PropertyList {
      * Adds the attributes, passed in by the parser to the PropertyList
      * 
      * @param attributes Collection of attributes passed to us from the parser.
+     * @throws ValidationException if there is an attribute that does not
+     *          map to a property id (strict validation only)
      */
-    public void addAttributesToList(Attributes attributes) {
+    public void addAttributesToList(Attributes attributes) 
+                    throws ValidationException {
         /*
          * If column-number/number-columns-spanned are specified, then we 
          * need them before all others (possible from-table-column() on any 
@@ -308,69 +314,104 @@ public abstract class PropertyList {
                 if (factory.getElementMappingRegistry().isKnownNamespace(attributeNS)) {
                     getFObj().addForeignAttribute(attributeNS, attributeName, attributeValue);
                 } else {
-                    handleInvalidProperty(attributeName);
+                    handleInvalidProperty(
+                            "Error processing foreign attribute: "
+                            + attributeNS + "/@" + attributeName, attributeName);
                 }
             }
         }
     }
+    
+    /**
+     * Validates a property name.
+     * @param propertyName  the property name to check
+     * @return true if the base property name and the subproperty name (if any)
+     *           can be correctly mapped to an id
+     * @throws ValidationException in case the property name
+     *          is invalid for the FO namespace
+     */
+    protected boolean isValidPropertyName(String propertyName) 
+                throws ValidationException {
+
+        int propId = FOPropertyMapping.getPropertyId(
+                        findBasePropertyName(propertyName));
+        int subpropId = FOPropertyMapping.getSubPropertyId(
+                        findSubPropertyName(propertyName));
+        
+        if (propId == -1 
+                || (subpropId == -1 
+                        && findSubPropertyName(propertyName) != null)) {
+            StringBuffer errorMessage = new StringBuffer().append(
+                        "Invalid property name \'").append(propertyName);
+            handleInvalidProperty(errorMessage.toString(), propertyName);
+            return false;
+        }
+        return true;
+    }
 
     /**
      *
      * @param attributes Collection of attributes
      * @param attributeName Attribute name to convert
      * @param attributeValue Attribute value to assign to property
+     * @throws ValidationException in case the property name is invalid 
+     *          for the FO namespace
      */
     private void convertAttributeToProperty(Attributes attributes,
                                             String attributeName,
-                                            String attributeValue) {
-                                                
-        PropertyMaker propertyMaker = null;
-        FObj parentFO = fobj.findNearestAncestorFObj();
+                                            String attributeValue) 
+                    throws ValidationException {
         
-        /* Handle "compound" properties, ex. space-before.minimum */
-        String basePropertyName = findBasePropertyName(attributeName);
-        String subPropertyName = findSubPropertyName(attributeName);
+        if (attributeValue != null) {
+
+            if (!isValidPropertyName(attributeName)) {
+                //will log an error or throw an exception
+                return;
+            }
+            FObj parentFO = fobj.findNearestAncestorFObj();
+            
+    
+            /* Handle "compound" properties, ex. space-before.minimum */
+            String basePropertyName = findBasePropertyName(attributeName);
+            String subPropertyName = findSubPropertyName(attributeName);
 
-        int propId = FOPropertyMapping.getPropertyId(basePropertyName);
+            int propId = FOPropertyMapping.getPropertyId(basePropertyName);
+            int subpropId = FOPropertyMapping.getSubPropertyId(subPropertyName);
+    
+            PropertyMaker propertyMaker = findMaker(propId);
+            if (propertyMaker == null) {
+                log.warn("No PropertyMaker registered for " + attributeName
+                        + ". Ignoring property.");
+                return;
+            }
 
-        propertyMaker = findMaker(propId);
-        if (propertyMaker == null) {
-            handleInvalidProperty(attributeName);
-            return;
-        }
-        if (attributeValue == null) {
-            return;
-        }
-        try {
-            Property prop = null;
-            if (subPropertyName == null) { // base attribute only found
-                /* Do nothing if the base property has already been created.
-                 * This is e.g. the case when a compound attribute was
-                 * specified before the base attribute; in these cases
-                 * the base attribute was already created in 
-                 * findBaseProperty()
-                 */
-                if (getExplicit(propId) != null) {
-                    return;
+            try {
+                Property prop = null;
+                if (subPropertyName == null) { // base attribute only found
+                    /* Do nothing if the base property has already been created.
+                     * This is e.g. the case when a compound attribute was
+                     * specified before the base attribute; in these cases
+                     * the base attribute was already created in 
+                     * findBaseProperty()
+                     */
+                    if (getExplicit(propId) != null) {
+                        return;
+                    }
+                    prop = propertyMaker.make(this, attributeValue, parentFO);
+                } else { // e.g. "leader-length.maximum"
+                    Property baseProperty = 
+                        findBaseProperty(attributes, parentFO, propId, 
+                                basePropertyName, propertyMaker);
+                    prop = propertyMaker.make(baseProperty, subpropId,
+                            this, attributeValue, parentFO);
                 }
-                prop = propertyMaker.make(this, attributeValue, parentFO);
-            } else { // e.g. "leader-length.maximum"
-                Property baseProperty = findBaseProperty(attributes,
-                        parentFO, propId, basePropertyName, propertyMaker);
-                int subpropertyId = FOPropertyMapping.getSubPropertyId(subPropertyName);
-                if (subpropertyId == -1) {
-                    handleInvalidProperty(attributeName);
-                    return;
+                if (prop != null) {
+                    putExplicit(propId, prop);
                 }
-                prop = propertyMaker.make(baseProperty, subpropertyId,
-                        this, attributeValue, parentFO);
-            }
-            if (prop != null) {
-                putExplicit(propId, prop);
+            } catch (PropertyException e) {
+                log.error("Ignoring property: " 
+                        + attributeName + "=\"" + attributeValue + "\"");
             }
-        } catch (PropertyException e) {
-            // TODO: Add strict validation.
-            log.error(e.getMessage());
         }
     }
 
@@ -405,9 +446,19 @@ public abstract class PropertyList {
         return null;  // could not find base property
     }
 
-    private void handleInvalidProperty(String attributeName) {
-        if (!attributeName.startsWith("xmlns")) {
-            log.error("property '" + attributeName + "' ignored");
+    /**
+     * @param message ...
+     * @param propName ...
+     * @throws ValidationException ...
+     */
+    protected void handleInvalidProperty(String message, String propName) 
+                    throws ValidationException {
+        if (!propName.startsWith("xmlns")) {
+            if (fobj.getUserAgent().validateStrictly()) {
+                fobj.attributeError(message);
+            } else {
+                log.error(message + " Property ignored.");
+            }
         }
     }
 
@@ -418,7 +469,7 @@ public abstract class PropertyList {
      * @param attributeName String to be atomized
      * @return the base portion of the attribute
      */
-    private static String findBasePropertyName(String attributeName) {
+    protected static String findBasePropertyName(String attributeName) {
         int separatorCharIndex = attributeName.indexOf('.');
         String basePropertyName = attributeName;
         if (separatorCharIndex > -1) {
@@ -434,7 +485,7 @@ public abstract class PropertyList {
      * @param attributeName String to be atomized
      * @return the sub portion of the attribute
      */
-    private static String findSubPropertyName(String attributeName) {
+    protected static String findSubPropertyName(String attributeName) {
         int separatorCharIndex = attributeName.indexOf('.');
         String subpropertyName = null;
         if (separatorCharIndex > -1) {
diff --git a/src/java/org/apache/fop/fo/PropertySets.java b/src/java/org/apache/fop/fo/PropertySets.java
deleted file mode 100644 (file)
index 29455ad..0000000
+++ /dev/null
@@ -1,1192 +0,0 @@
-/*
- * 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.fo;
-
-import org.apache.fop.fo.Constants;
-import java.util.BitSet;
-import java.util.ArrayList;
-
-public class PropertySets {
-    private static short[][] mapping = null;
-    private static BitSet can_have_markers = null;
-    private static BitSet no_inline_areas = null;
-
-    private Element[] elements = new Element[Constants.FRM_OBJ_COUNT + 1];
-    private BitSet block_elems = new BitSet();
-    private BitSet inline_elems = new BitSet();
-
-    BitSet CommonAccessibilityProperties = new BitSet();
-    BitSet CommonAbsolutePositionProperties = new BitSet();
-    BitSet CommonAuralProperties = new BitSet();
-    BitSet CommonBorderPaddingBackgroundProperties = new BitSet();
-    BitSet CommonFontProperties = new BitSet();
-    BitSet CommonHyphenationProperties = new BitSet();
-    BitSet CommonMarginPropertiesBlock = new BitSet();
-    BitSet CommonMarginPropertiesInline = new BitSet();
-    BitSet CommonRelativePositionProperties = new BitSet();
-
-    public void initializeElements() {
-        block_elems.set(Constants.FO_BLOCK);
-        block_elems.set(Constants.FO_BLOCK_CONTAINER);
-        block_elems.set(Constants.FO_TABLE_AND_CAPTION);
-        block_elems.set(Constants.FO_TABLE);
-        block_elems.set(Constants.FO_LIST_BLOCK);
-
-        inline_elems.set(Constants.FO_BIDI_OVERRIDE);
-        inline_elems.set(Constants.FO_CHARACTER);
-        inline_elems.set(Constants.FO_EXTERNAL_GRAPHIC);
-        inline_elems.set(Constants.FO_INSTREAM_FOREIGN_OBJECT);
-        inline_elems.set(Constants.FO_INLINE);
-        inline_elems.set(Constants.FO_INLINE_CONTAINER);
-        inline_elems.set(Constants.FO_LEADER);
-        inline_elems.set(Constants.FO_PAGE_NUMBER);
-        inline_elems.set(Constants.FO_PAGE_NUMBER_CITATION);
-        inline_elems.set(Constants.FO_PAGE_NUMBER_CITATION_LAST);
-        inline_elems.set(Constants.FO_BASIC_LINK);
-        inline_elems.set(Constants.FO_MULTI_TOGGLE);
-    }
-
-    public void initializeCommon() {
-        CommonAccessibilityProperties.set(Constants.PR_SOURCE_DOCUMENT);
-        CommonAccessibilityProperties.set(Constants.PR_ROLE);
-
-        CommonAbsolutePositionProperties.set(Constants.PR_ABSOLUTE_POSITION);
-        CommonAbsolutePositionProperties.set(Constants.PR_POSITION);
-        CommonAbsolutePositionProperties.set(Constants.PR_TOP);
-        CommonAbsolutePositionProperties.set(Constants.PR_RIGHT);
-        CommonAbsolutePositionProperties.set(Constants.PR_BOTTOM);
-        CommonAbsolutePositionProperties.set(Constants.PR_LEFT);
-
-        CommonAuralProperties.set(Constants.PR_AZIMUTH);
-        CommonAuralProperties.set(Constants.PR_CUE_AFTER);
-        CommonAuralProperties.set(Constants.PR_CUE_BEFORE);
-        CommonAuralProperties.set(Constants.PR_CUE);
-        CommonAuralProperties.set(Constants.PR_ELEVATION);
-        CommonAuralProperties.set(Constants.PR_PAUSE_AFTER);
-        CommonAuralProperties.set(Constants.PR_PAUSE_BEFORE);
-        CommonAuralProperties.set(Constants.PR_PAUSE);
-        CommonAuralProperties.set(Constants.PR_PITCH);
-        CommonAuralProperties.set(Constants.PR_PITCH_RANGE);
-        CommonAuralProperties.set(Constants.PR_PLAY_DURING);
-        CommonAuralProperties.set(Constants.PR_RICHNESS);
-        CommonAuralProperties.set(Constants.PR_SPEAK);
-        CommonAuralProperties.set(Constants.PR_SPEAK_HEADER);
-        CommonAuralProperties.set(Constants.PR_SPEAK_NUMERAL);
-        CommonAuralProperties.set(Constants.PR_SPEAK_PUNCTUATION);
-        CommonAuralProperties.set(Constants.PR_SPEECH_RATE);
-        CommonAuralProperties.set(Constants.PR_STRESS);
-        CommonAuralProperties.set(Constants.PR_VOICE_FAMILY);
-        CommonAuralProperties.set(Constants.PR_VOLUME);
-
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BACKGROUND_ATTACHMENT);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BACKGROUND_COLOR);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BACKGROUND_IMAGE);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BACKGROUND_REPEAT);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BACKGROUND_POSITION_HORIZONTAL);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BACKGROUND_POSITION_VERTICAL);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_BEFORE_COLOR);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_BEFORE_STYLE);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_BEFORE_WIDTH);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_AFTER_COLOR);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_AFTER_STYLE);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_AFTER_WIDTH);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_START_COLOR);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_START_STYLE);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_START_WIDTH);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_END_COLOR);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_END_STYLE);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_END_WIDTH);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_TOP_COLOR);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_TOP_STYLE);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_TOP_WIDTH);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_BOTTOM_COLOR);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_BOTTOM_STYLE);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_BOTTOM_WIDTH);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_LEFT_COLOR);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_LEFT_STYLE);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_LEFT_WIDTH);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_RIGHT_COLOR);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_RIGHT_STYLE);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_RIGHT_WIDTH);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_BEFORE);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_AFTER);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_START);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_END);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_TOP);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_BOTTOM);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_LEFT);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING_RIGHT);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_STYLE);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_WIDTH);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_COLOR);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_TOP);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_BOTTOM);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_LEFT);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_BORDER_RIGHT);
-        CommonBorderPaddingBackgroundProperties.set(Constants.PR_PADDING);
-
-        CommonFontProperties.set(Constants.PR_FONT);
-        CommonFontProperties.set(Constants.PR_FONT_FAMILY);
-        CommonFontProperties.set(Constants.PR_FONT_SELECTION_STRATEGY);
-        CommonFontProperties.set(Constants.PR_FONT_SIZE);
-        CommonFontProperties.set(Constants.PR_FONT_STRETCH);
-        CommonFontProperties.set(Constants.PR_FONT_SIZE_ADJUST);
-        CommonFontProperties.set(Constants.PR_FONT_STYLE);
-        CommonFontProperties.set(Constants.PR_FONT_VARIANT);
-        CommonFontProperties.set(Constants.PR_FONT_WEIGHT);
-
-        CommonHyphenationProperties.set(Constants.PR_COUNTRY);
-        CommonHyphenationProperties.set(Constants.PR_LANGUAGE);
-        CommonHyphenationProperties.set(Constants.PR_SCRIPT);
-        CommonHyphenationProperties.set(Constants.PR_HYPHENATE);
-        CommonHyphenationProperties.set(Constants.PR_HYPHENATION_CHARACTER);
-        CommonHyphenationProperties.set(Constants.PR_HYPHENATION_PUSH_CHARACTER_COUNT);
-        CommonHyphenationProperties.set(Constants.PR_HYPHENATION_REMAIN_CHARACTER_COUNT);
-
-        CommonMarginPropertiesBlock.set(Constants.PR_MARGIN);
-        CommonMarginPropertiesBlock.set(Constants.PR_MARGIN_TOP);
-        CommonMarginPropertiesBlock.set(Constants.PR_MARGIN_BOTTOM);
-        CommonMarginPropertiesBlock.set(Constants.PR_MARGIN_LEFT);
-        CommonMarginPropertiesBlock.set(Constants.PR_MARGIN_RIGHT);
-        CommonMarginPropertiesBlock.set(Constants.PR_SPACE_BEFORE);
-        CommonMarginPropertiesBlock.set(Constants.PR_SPACE_AFTER);
-        CommonMarginPropertiesBlock.set(Constants.PR_START_INDENT);
-        CommonMarginPropertiesBlock.set(Constants.PR_END_INDENT);
-
-        CommonMarginPropertiesInline.set(Constants.PR_SPACE_END);
-        CommonMarginPropertiesInline.set(Constants.PR_SPACE_START);
-
-        CommonRelativePositionProperties.set(Constants.PR_RELATIVE_POSITION);
-
-
-    }
-
-    public void initialize() {
-        // define the fo: elements
-        for (int i = 1; i < elements.length; i++) {
-            elements[i] = new Element(i);
-        }
-
-        // populate the elements with properties and content elements.
-        Element elem;
-        elem = elements[Constants.FO_ROOT];
-        elem.addProperty(Constants.PR_MEDIA_USAGE);
-        elem.addContent(Constants.FO_LAYOUT_MASTER_SET);
-        elem.addContent(Constants.FO_DECLARATIONS);
-        elem.addContent(Constants.FO_PAGE_SEQUENCE);
-
-        elem = elements[Constants.FO_DECLARATIONS];
-        elem.addContent(Constants.FO_COLOR_PROFILE);
-
-        elem = elements[Constants.FO_COLOR_PROFILE];
-        elem.addProperty(Constants.PR_SRC);
-        elem.addProperty(Constants.PR_COLOR_PROFILE_NAME);
-        elem.addProperty(Constants.PR_RENDERING_INTENT);
-
-        elem = elements[Constants.FO_BOOKMARK_TREE];
-        elem.addContent(Constants.FO_BOOKMARK);
-
-        elem = elements[Constants.FO_BOOKMARK];
-        elem.addContent(Constants.FO_BOOKMARK_TITLE);
-        elem.addContent(Constants.FO_BOOKMARK);
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperty(Constants.PR_EXTERNAL_DESTINATION);
-        elem.addProperty(Constants.PR_INTERNAL_DESTINATION);
-        elem.addProperty(Constants.PR_STARTING_STATE);
-
-        elem = elements[Constants.FO_BOOKMARK_TITLE];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperty(Constants.PR_FONT_STYLE);
-        elem.addProperty(Constants.PR_FONT_WEIGHT);
-
-        elem = elements[Constants.FO_PAGE_SEQUENCE_WRAPPER];
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_INDEX_CLASS);
-        elem.addProperty(Constants.PR_INDEX_KEY);
-
-        elem = elements[Constants.FO_PAGE_SEQUENCE];
-        elem.addProperty(Constants.PR_COUNTRY);
-        elem.addProperty(Constants.PR_FORMAT);
-        elem.addProperty(Constants.PR_LANGUAGE);
-        elem.addProperty(Constants.PR_LETTER_VALUE);
-        elem.addProperty(Constants.PR_GROUPING_SEPARATOR);
-        elem.addProperty(Constants.PR_GROUPING_SIZE);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_INITIAL_PAGE_NUMBER);
-        elem.addProperty(Constants.PR_FORCE_PAGE_COUNT);
-        elem.addProperty(Constants.PR_MASTER_REFERENCE);
-        elem.addContent(Constants.FO_TITLE);
-        elem.addContent(Constants.FO_STATIC_CONTENT);
-        elem.addContent(Constants.FO_FLOW);
-
-        elem = elements[Constants.FO_LAYOUT_MASTER_SET];
-        elem.addProperty(Constants.PR_MASTER_NAME);
-        elem.addContent(Constants.FO_SIMPLE_PAGE_MASTER);
-        elem.addContent(Constants.FO_PAGE_SEQUENCE_MASTER);
-
-        elem = elements[Constants.FO_PAGE_SEQUENCE_MASTER];
-        elem.addProperty(Constants.PR_MASTER_NAME);
-        elem.addContent(Constants.FO_SINGLE_PAGE_MASTER_REFERENCE);
-        elem.addContent(Constants.FO_REPEATABLE_PAGE_MASTER_REFERENCE);
-        elem.addContent(Constants.FO_REPEATABLE_PAGE_MASTER_ALTERNATIVES);
-
-        elem = elements[Constants.FO_SINGLE_PAGE_MASTER_REFERENCE];
-        elem.addProperty(Constants.PR_MASTER_REFERENCE);
-
-        elem = elements[Constants.FO_REPEATABLE_PAGE_MASTER_REFERENCE];
-        elem.addProperty(Constants.PR_MASTER_REFERENCE);
-        elem.addProperty(Constants.PR_MAXIMUM_REPEATS);
-
-        elem = elements[Constants.FO_REPEATABLE_PAGE_MASTER_ALTERNATIVES];
-        elem.addProperty(Constants.PR_MAXIMUM_REPEATS);
-        elem.addContent(Constants.FO_CONDITIONAL_PAGE_MASTER_REFERENCE);
-
-        elem = elements[Constants.FO_CONDITIONAL_PAGE_MASTER_REFERENCE];
-        elem.addProperty(Constants.PR_MASTER_REFERENCE);
-        elem.addProperty(Constants.PR_PAGE_POSITION);
-        elem.addProperty(Constants.PR_ODD_OR_EVEN);
-        elem.addProperty(Constants.PR_BLANK_OR_NOT_BLANK);
-
-        elem = elements[Constants.FO_SIMPLE_PAGE_MASTER];
-        elem.addProperties(CommonMarginPropertiesBlock);
-        elem.addProperty(Constants.PR_MASTER_NAME);
-        elem.addProperty(Constants.PR_PAGE_HEIGHT);
-        elem.addProperty(Constants.PR_PAGE_WIDTH);
-        elem.addProperty(Constants.PR_REFERENCE_ORIENTATION);
-        elem.addProperty(Constants.PR_WRITING_MODE);
-        elem.addContent(Constants.FO_REGION_BODY);
-        elem.addContent(Constants.FO_REGION_BEFORE);
-        elem.addContent(Constants.FO_REGION_AFTER);
-        elem.addContent(Constants.FO_REGION_START);
-        elem.addContent(Constants.FO_REGION_END);
-
-        elem = elements[Constants.FO_REGION_BODY];
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonMarginPropertiesBlock);
-        elem.addProperty(Constants.PR_CLIP);
-        elem.addProperty(Constants.PR_COLUMN_COUNT);
-        elem.addProperty(Constants.PR_COLUMN_GAP);
-        elem.addProperty(Constants.PR_DISPLAY_ALIGN);
-        elem.addProperty(Constants.PR_OVERFLOW);
-        elem.addProperty(Constants.PR_REGION_NAME);
-        elem.addProperty(Constants.PR_REFERENCE_ORIENTATION);
-        elem.addProperty(Constants.PR_WRITING_MODE);
-
-        elem = elements[Constants.FO_REGION_BEFORE];
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperty(Constants.PR_CLIP);
-        elem.addProperty(Constants.PR_DISPLAY_ALIGN);
-        elem.addProperty(Constants.PR_EXTENT);
-        elem.addProperty(Constants.PR_OVERFLOW);
-        elem.addProperty(Constants.PR_PRECEDENCE);
-        elem.addProperty(Constants.PR_REGION_NAME);
-        elem.addProperty(Constants.PR_REFERENCE_ORIENTATION);
-        elem.addProperty(Constants.PR_WRITING_MODE);
-
-        elem = elements[Constants.FO_REGION_AFTER];
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperty(Constants.PR_CLIP);
-        elem.addProperty(Constants.PR_DISPLAY_ALIGN);
-        elem.addProperty(Constants.PR_EXTENT);
-        elem.addProperty(Constants.PR_OVERFLOW);
-        elem.addProperty(Constants.PR_PRECEDENCE);
-        elem.addProperty(Constants.PR_REGION_NAME);
-        elem.addProperty(Constants.PR_REFERENCE_ORIENTATION);
-        elem.addProperty(Constants.PR_WRITING_MODE);
-
-        elem = elements[Constants.FO_REGION_START];
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperty(Constants.PR_CLIP);
-        elem.addProperty(Constants.PR_DISPLAY_ALIGN);
-        elem.addProperty(Constants.PR_EXTENT);
-        elem.addProperty(Constants.PR_OVERFLOW);
-        elem.addProperty(Constants.PR_REGION_NAME);
-        elem.addProperty(Constants.PR_REFERENCE_ORIENTATION);
-        elem.addProperty(Constants.PR_WRITING_MODE);
-
-        elem = elements[Constants.FO_REGION_END];
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperty(Constants.PR_CLIP);
-        elem.addProperty(Constants.PR_DISPLAY_ALIGN);
-        elem.addProperty(Constants.PR_EXTENT);
-        elem.addProperty(Constants.PR_OVERFLOW);
-        elem.addProperty(Constants.PR_REGION_NAME);
-        elem.addProperty(Constants.PR_REFERENCE_ORIENTATION);
-        elem.addProperty(Constants.PR_WRITING_MODE);
-
-        elem = elements[Constants.FO_FLOW];
-        elem.addProperty(Constants.PR_FLOW_NAME);
-        elem.addContent(block_elems);
-
-        elem = elements[Constants.FO_STATIC_CONTENT];
-        elem.addProperty(Constants.PR_FLOW_NAME);
-        elem.addContent(block_elems);
-
-        elem = elements[Constants.FO_TITLE];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonFontProperties);
-        elem.addProperties(CommonMarginPropertiesInline);
-        elem.addProperty(Constants.PR_COLOR);
-        elem.addProperty(Constants.PR_LINE_HEIGHT);
-        elem.addProperty(Constants.PR_VISIBILITY);
-        elem.addContent(inline_elems);
-
-        elem = elements[Constants.FO_BLOCK];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonFontProperties);
-        elem.addProperties(CommonHyphenationProperties);
-        elem.addProperties(CommonMarginPropertiesBlock);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_PAGE_BREAK_AFTER);
-        elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE);
-        elem.addProperty(Constants.PR_BREAK_AFTER);
-        elem.addProperty(Constants.PR_BREAK_BEFORE);
-        elem.addProperty(Constants.PR_COLOR);
-        elem.addProperty(Constants.PR_TEXT_DEPTH);
-        elem.addProperty(Constants.PR_TEXT_ALTITUDE);
-        elem.addProperty(Constants.PR_HYPHENATION_KEEP);
-        elem.addProperty(Constants.PR_HYPHENATION_LADDER_COUNT);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
-        elem.addProperty(Constants.PR_KEEP_TOGETHER);
-        elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
-        elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
-        elem.addProperty(Constants.PR_LAST_LINE_END_INDENT);
-        elem.addProperty(Constants.PR_LINEFEED_TREATMENT);
-        elem.addProperty(Constants.PR_LINE_HEIGHT);
-        elem.addProperty(Constants.PR_LINE_HEIGHT_SHIFT_ADJUSTMENT);
-        elem.addProperty(Constants.PR_LINE_STACKING_STRATEGY);
-        elem.addProperty(Constants.PR_ORPHANS);
-        elem.addProperty(Constants.PR_WHITE_SPACE_TREATMENT);
-        elem.addProperty(Constants.PR_SPAN);
-        elem.addProperty(Constants.PR_TEXT_ALIGN);
-        elem.addProperty(Constants.PR_TEXT_ALIGN_LAST);
-        elem.addProperty(Constants.PR_TEXT_INDENT);
-        elem.addProperty(Constants.PR_VISIBILITY);
-        elem.addProperty(Constants.PR_WHITE_SPACE_COLLAPSE);
-        elem.addProperty(Constants.PR_WIDOWS);
-        elem.addProperty(Constants.PR_WRAP_OPTION);
-        elem.addContent(inline_elems);
-        elem.addContent(block_elems);
-
-        elem = elements[Constants.FO_BLOCK_CONTAINER];
-        elem.addProperties(CommonAbsolutePositionProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonMarginPropertiesBlock);
-        elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION);
-        elem.addProperty(Constants.PR_X_BLOCK_PROGRESSION_UNIT);
-        elem.addProperty(Constants.PR_PAGE_BREAK_AFTER);
-        elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE);
-        elem.addProperty(Constants.PR_BREAK_AFTER);
-        elem.addProperty(Constants.PR_BREAK_BEFORE);
-        elem.addProperty(Constants.PR_CLIP);
-        elem.addProperty(Constants.PR_DISPLAY_ALIGN);
-        elem.addProperty(Constants.PR_HEIGHT);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION);
-        elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
-        elem.addProperty(Constants.PR_KEEP_TOGETHER);
-        elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
-        elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
-        elem.addProperty(Constants.PR_OVERFLOW);
-        elem.addProperty(Constants.PR_REFERENCE_ORIENTATION);
-        elem.addProperty(Constants.PR_SPAN);
-        elem.addProperty(Constants.PR_WIDTH);
-        elem.addProperty(Constants.PR_WRITING_MODE);
-        elem.addProperty(Constants.PR_Z_INDEX);
-        elem.addContent(block_elems);
-
-        elem = elements[Constants.FO_BIDI_OVERRIDE];
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonFontProperties);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_COLOR);
-        elem.addProperty(Constants.PR_DIRECTION);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_LETTER_SPACING);
-        elem.addProperty(Constants.PR_LINE_HEIGHT);
-        elem.addProperty(Constants.PR_SCORE_SPACES);
-        elem.addProperty(Constants.PR_UNICODE_BIDI);
-        elem.addProperty(Constants.PR_WORD_SPACING);
-        elem.addContent(inline_elems);
-        elem.addContent(block_elems);
-
-        elem = elements[Constants.FO_CHARACTER];
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonFontProperties);
-        elem.addProperties(CommonHyphenationProperties);
-        elem.addProperties(CommonMarginPropertiesInline);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_VERTICAL_ALIGN);
-        elem.addProperty(Constants.PR_ALIGNMENT_ADJUST);
-        elem.addProperty(Constants.PR_TREAT_AS_WORD_SPACE);
-        elem.addProperty(Constants.PR_ALIGNMENT_BASELINE);
-        elem.addProperty(Constants.PR_BASELINE_SHIFT);
-        elem.addProperty(Constants.PR_CHARACTER);
-        elem.addProperty(Constants.PR_COLOR);
-        elem.addProperty(Constants.PR_DOMINANT_BASELINE);
-        elem.addProperty(Constants.PR_TEXT_DEPTH);
-        elem.addProperty(Constants.PR_TEXT_ALTITUDE);
-        elem.addProperty(Constants.PR_GLYPH_ORIENTATION_HORIZONTAL);
-        elem.addProperty(Constants.PR_GLYPH_ORIENTATION_VERTICAL);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
-        elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
-        elem.addProperty(Constants.PR_LETTER_SPACING);
-        elem.addProperty(Constants.PR_LINE_HEIGHT);
-        elem.addProperty(Constants.PR_SCORE_SPACES);
-        elem.addProperty(Constants.PR_SUPPRESS_AT_LINE_BREAK);
-        elem.addProperty(Constants.PR_TEXT_DECORATION);
-        elem.addProperty(Constants.PR_TEXT_SHADOW);
-        elem.addProperty(Constants.PR_TEXT_TRANSFORM);
-        elem.addProperty(Constants.PR_VISIBILITY);
-        elem.addProperty(Constants.PR_WORD_SPACING);
-
-        elem = elements[Constants.FO_INITIAL_PROPERTY_SET];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonFontProperties);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_COLOR);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_LETTER_SPACING);
-        elem.addProperty(Constants.PR_LINE_HEIGHT);
-        elem.addProperty(Constants.PR_SCORE_SPACES);
-        elem.addProperty(Constants.PR_TEXT_DECORATION);
-        elem.addProperty(Constants.PR_TEXT_SHADOW);
-        elem.addProperty(Constants.PR_TEXT_TRANSFORM);
-        elem.addProperty(Constants.PR_WORD_SPACING);
-
-        elem = elements[Constants.FO_EXTERNAL_GRAPHIC];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonMarginPropertiesInline);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_VERTICAL_ALIGN);
-        elem.addProperty(Constants.PR_ALIGNMENT_ADJUST);
-        elem.addProperty(Constants.PR_ALIGNMENT_BASELINE);
-        elem.addProperty(Constants.PR_BASELINE_SHIFT);
-        elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION);
-        elem.addProperty(Constants.PR_CLIP);
-        elem.addProperty(Constants.PR_CONTENT_HEIGHT);
-        elem.addProperty(Constants.PR_CONTENT_TYPE);
-        elem.addProperty(Constants.PR_CONTENT_WIDTH);
-        elem.addProperty(Constants.PR_DISPLAY_ALIGN);
-        elem.addProperty(Constants.PR_DOMINANT_BASELINE);
-        elem.addProperty(Constants.PR_HEIGHT);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION);
-        elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
-        elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
-        elem.addProperty(Constants.PR_LINE_HEIGHT);
-        elem.addProperty(Constants.PR_OVERFLOW);
-        elem.addProperty(Constants.PR_SCALING);
-        elem.addProperty(Constants.PR_SCALING_METHOD);
-        elem.addProperty(Constants.PR_SRC);
-        elem.addProperty(Constants.PR_TEXT_ALIGN);
-        elem.addProperty(Constants.PR_WIDTH);
-
-        elem = elements[Constants.FO_INSTREAM_FOREIGN_OBJECT];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonMarginPropertiesInline);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_VERTICAL_ALIGN);
-        elem.addProperty(Constants.PR_ALIGNMENT_ADJUST);
-        elem.addProperty(Constants.PR_ALIGNMENT_BASELINE);
-        elem.addProperty(Constants.PR_BASELINE_SHIFT);
-        elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION);
-        elem.addProperty(Constants.PR_CLIP);
-        elem.addProperty(Constants.PR_CONTENT_HEIGHT);
-        elem.addProperty(Constants.PR_CONTENT_TYPE);
-        elem.addProperty(Constants.PR_CONTENT_WIDTH);
-        elem.addProperty(Constants.PR_DISPLAY_ALIGN);
-        elem.addProperty(Constants.PR_DOMINANT_BASELINE);
-        elem.addProperty(Constants.PR_HEIGHT);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION);
-        elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
-        elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
-        elem.addProperty(Constants.PR_LINE_HEIGHT);
-        elem.addProperty(Constants.PR_OVERFLOW);
-        elem.addProperty(Constants.PR_SCALING);
-        elem.addProperty(Constants.PR_SCALING_METHOD);
-        elem.addProperty(Constants.PR_TEXT_ALIGN);
-        elem.addProperty(Constants.PR_WIDTH);
-
-        elem = elements[Constants.FO_INLINE];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonFontProperties);
-        elem.addProperties(CommonMarginPropertiesInline);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_VERTICAL_ALIGN);
-        elem.addProperty(Constants.PR_ALIGNMENT_ADJUST);
-        elem.addProperty(Constants.PR_ALIGNMENT_BASELINE);
-        elem.addProperty(Constants.PR_BASELINE_SHIFT);
-        elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION);
-        elem.addProperty(Constants.PR_COLOR);
-        elem.addProperty(Constants.PR_DOMINANT_BASELINE);
-        elem.addProperty(Constants.PR_HEIGHT);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION);
-        elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
-        elem.addProperty(Constants.PR_KEEP_TOGETHER);
-        elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
-        elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
-        elem.addProperty(Constants.PR_LINE_HEIGHT);
-        elem.addProperty(Constants.PR_TEXT_DECORATION);
-        elem.addProperty(Constants.PR_VISIBILITY);
-        elem.addProperty(Constants.PR_WIDTH);
-        elem.addProperty(Constants.PR_WRAP_OPTION);
-        elem.addContent(inline_elems);
-        elem.addContent(block_elems);
-
-        elem = elements[Constants.FO_INLINE_CONTAINER];
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonMarginPropertiesInline);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_VERTICAL_ALIGN);
-        elem.addProperty(Constants.PR_ALIGNMENT_ADJUST);
-        elem.addProperty(Constants.PR_ALIGNMENT_BASELINE);
-        elem.addProperty(Constants.PR_BASELINE_SHIFT);
-        elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION);
-        elem.addProperty(Constants.PR_CLIP);
-        elem.addProperty(Constants.PR_DISPLAY_ALIGN);
-        elem.addProperty(Constants.PR_DOMINANT_BASELINE);
-        elem.addProperty(Constants.PR_HEIGHT);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION);
-        elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
-        elem.addProperty(Constants.PR_KEEP_TOGETHER);
-        elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
-        elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
-        elem.addProperty(Constants.PR_LINE_HEIGHT);
-        elem.addProperty(Constants.PR_OVERFLOW);
-        elem.addProperty(Constants.PR_REFERENCE_ORIENTATION);
-        elem.addProperty(Constants.PR_WIDTH);
-        elem.addProperty(Constants.PR_WRITING_MODE);
-        elem.addContent(block_elems);
-
-        elem = elements[Constants.FO_LEADER];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonFontProperties);
-        elem.addProperties(CommonMarginPropertiesInline);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_VERTICAL_ALIGN);
-        elem.addProperty(Constants.PR_ALIGNMENT_ADJUST);
-        elem.addProperty(Constants.PR_ALIGNMENT_BASELINE);
-        elem.addProperty(Constants.PR_BASELINE_SHIFT);
-        elem.addProperty(Constants.PR_COLOR);
-        elem.addProperty(Constants.PR_DOMINANT_BASELINE);
-        elem.addProperty(Constants.PR_TEXT_DEPTH);
-        elem.addProperty(Constants.PR_TEXT_ALTITUDE);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
-        elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
-        elem.addProperty(Constants.PR_LEADER_ALIGNMENT);
-        elem.addProperty(Constants.PR_LEADER_LENGTH);
-        elem.addProperty(Constants.PR_LEADER_PATTERN);
-        elem.addProperty(Constants.PR_LEADER_PATTERN_WIDTH);
-        elem.addProperty(Constants.PR_RULE_STYLE);
-        elem.addProperty(Constants.PR_RULE_THICKNESS);
-        elem.addProperty(Constants.PR_LETTER_SPACING);
-        elem.addProperty(Constants.PR_LINE_HEIGHT);
-        elem.addProperty(Constants.PR_TEXT_SHADOW);
-        elem.addProperty(Constants.PR_VISIBILITY);
-        elem.addProperty(Constants.PR_WORD_SPACING);
-        elem.addContent(inline_elems);
-
-        elem = elements[Constants.FO_PAGE_NUMBER];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonFontProperties);
-        elem.addProperties(CommonMarginPropertiesInline);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_VERTICAL_ALIGN);
-        elem.addProperty(Constants.PR_ALIGNMENT_ADJUST);
-        elem.addProperty(Constants.PR_ALIGNMENT_BASELINE);
-        elem.addProperty(Constants.PR_BASELINE_SHIFT);
-        elem.addProperty(Constants.PR_DOMINANT_BASELINE);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
-        elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
-        elem.addProperty(Constants.PR_LETTER_SPACING);
-        elem.addProperty(Constants.PR_LINE_HEIGHT);
-        elem.addProperty(Constants.PR_SCORE_SPACES);
-        elem.addProperty(Constants.PR_TEXT_ALTITUDE);
-        elem.addProperty(Constants.PR_TEXT_DECORATION);
-        elem.addProperty(Constants.PR_TEXT_DEPTH);
-        elem.addProperty(Constants.PR_TEXT_SHADOW);
-        elem.addProperty(Constants.PR_TEXT_TRANSFORM);
-        elem.addProperty(Constants.PR_VISIBILITY);
-        elem.addProperty(Constants.PR_WORD_SPACING);
-        elem.addProperty(Constants.PR_WRAP_OPTION);
-
-        elem = elements[Constants.FO_PAGE_NUMBER_CITATION];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonFontProperties);
-        elem.addProperties(CommonMarginPropertiesInline);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_VERTICAL_ALIGN);
-        elem.addProperty(Constants.PR_ALIGNMENT_ADJUST);
-        elem.addProperty(Constants.PR_ALIGNMENT_BASELINE);
-        elem.addProperty(Constants.PR_BASELINE_SHIFT);
-        elem.addProperty(Constants.PR_DOMINANT_BASELINE);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
-        elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
-        elem.addProperty(Constants.PR_LETTER_SPACING);
-        elem.addProperty(Constants.PR_LINE_HEIGHT);
-        elem.addProperty(Constants.PR_REF_ID);
-        elem.addProperty(Constants.PR_SCORE_SPACES);
-        elem.addProperty(Constants.PR_TEXT_ALTITUDE);
-        elem.addProperty(Constants.PR_TEXT_DECORATION);
-        elem.addProperty(Constants.PR_TEXT_DEPTH);
-        elem.addProperty(Constants.PR_TEXT_SHADOW);
-        elem.addProperty(Constants.PR_TEXT_TRANSFORM);
-        elem.addProperty(Constants.PR_VISIBILITY);
-        elem.addProperty(Constants.PR_WORD_SPACING);
-        elem.addProperty(Constants.PR_WRAP_OPTION);
-        
-        elem = elements[Constants.FO_PAGE_NUMBER_CITATION_LAST];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonFontProperties);
-        elem.addProperties(CommonMarginPropertiesInline);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_VERTICAL_ALIGN);
-        elem.addProperty(Constants.PR_ALIGNMENT_ADJUST);
-        elem.addProperty(Constants.PR_ALIGNMENT_BASELINE);
-        elem.addProperty(Constants.PR_BASELINE_SHIFT);
-        elem.addProperty(Constants.PR_DOMINANT_BASELINE);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
-        elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
-        elem.addProperty(Constants.PR_LETTER_SPACING);
-        elem.addProperty(Constants.PR_LINE_HEIGHT);
-        elem.addProperty(Constants.PR_REF_ID);
-        elem.addProperty(Constants.PR_SCORE_SPACES);
-        elem.addProperty(Constants.PR_TEXT_ALTITUDE);
-        elem.addProperty(Constants.PR_TEXT_DECORATION);
-        elem.addProperty(Constants.PR_TEXT_DEPTH);
-        elem.addProperty(Constants.PR_TEXT_SHADOW);
-        elem.addProperty(Constants.PR_TEXT_TRANSFORM);
-        elem.addProperty(Constants.PR_VISIBILITY);
-        elem.addProperty(Constants.PR_WORD_SPACING);
-        elem.addProperty(Constants.PR_WRAP_OPTION);
-
-        elem = elements[Constants.FO_TABLE_AND_CAPTION];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonMarginPropertiesBlock);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_PAGE_BREAK_AFTER);
-        elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE);
-        elem.addProperty(Constants.PR_BREAK_AFTER);
-        elem.addProperty(Constants.PR_BREAK_BEFORE);
-        elem.addProperty(Constants.PR_CAPTION_SIDE);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
-        elem.addProperty(Constants.PR_KEEP_TOGETHER);
-        elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
-        elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
-        elem.addProperty(Constants.PR_TEXT_ALIGN);
-        elem.addContent(Constants.FO_TABLE_CAPTION);
-        elem.addContent(Constants.FO_TABLE);
-
-        elem = elements[Constants.FO_TABLE];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonMarginPropertiesBlock);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION);
-        elem.addProperty(Constants.PR_BORDER_AFTER_PRECEDENCE);
-        elem.addProperty(Constants.PR_BORDER_BEFORE_PRECEDENCE);
-        elem.addProperty(Constants.PR_BORDER_SPACING);
-        elem.addProperty(Constants.PR_BORDER_COLLAPSE);
-        elem.addProperty(Constants.PR_BORDER_END_PRECEDENCE);
-        elem.addProperty(Constants.PR_BORDER_SEPARATION);
-        elem.addProperty(Constants.PR_BORDER_START_PRECEDENCE);
-        elem.addProperty(Constants.PR_PAGE_BREAK_AFTER);
-        elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE);
-        elem.addProperty(Constants.PR_BREAK_AFTER);
-        elem.addProperty(Constants.PR_BREAK_BEFORE);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION);
-        elem.addProperty(Constants.PR_HEIGHT);
-        elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
-        elem.addProperty(Constants.PR_KEEP_TOGETHER);
-        elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
-        elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
-        elem.addProperty(Constants.PR_TABLE_LAYOUT);
-        elem.addProperty(Constants.PR_TABLE_OMIT_FOOTER_AT_BREAK);
-        elem.addProperty(Constants.PR_TABLE_OMIT_HEADER_AT_BREAK);
-        elem.addProperty(Constants.PR_WIDTH);
-        elem.addProperty(Constants.PR_WRITING_MODE);
-        elem.addContent(Constants.FO_TABLE_COLUMN);
-        elem.addContent(Constants.FO_TABLE_HEADER);
-        elem.addContent(Constants.FO_TABLE_FOOTER);
-        elem.addContent(Constants.FO_TABLE_BODY);
-
-        elem = elements[Constants.FO_TABLE_COLUMN];
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperty(Constants.PR_BORDER_AFTER_PRECEDENCE);
-        elem.addProperty(Constants.PR_BORDER_BEFORE_PRECEDENCE);
-        elem.addProperty(Constants.PR_BORDER_END_PRECEDENCE);
-        elem.addProperty(Constants.PR_BORDER_START_PRECEDENCE);
-        elem.addProperty(Constants.PR_COLUMN_NUMBER);
-        elem.addProperty(Constants.PR_COLUMN_WIDTH);
-        elem.addProperty(Constants.PR_NUMBER_COLUMNS_REPEATED);
-        elem.addProperty(Constants.PR_NUMBER_COLUMNS_SPANNED);
-        elem.addProperty(Constants.PR_VISIBILITY);
-
-        elem = elements[Constants.FO_TABLE_CAPTION];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION);
-        elem.addProperty(Constants.PR_HEIGHT);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION);
-        elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
-        elem.addProperty(Constants.PR_KEEP_TOGETHER);
-        elem.addProperty(Constants.PR_WIDTH);
-        elem.addContent(block_elems);
-
-        elem = elements[Constants.FO_TABLE_HEADER];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_BORDER_AFTER_PRECEDENCE);
-        elem.addProperty(Constants.PR_BORDER_BEFORE_PRECEDENCE);
-        elem.addProperty(Constants.PR_BORDER_END_PRECEDENCE);
-        elem.addProperty(Constants.PR_BORDER_START_PRECEDENCE);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_VISIBILITY);
-        elem.addContent(Constants.FO_TABLE_ROW);
-        elem.addContent(Constants.FO_TABLE_CELL);
-
-        elem = elements[Constants.FO_TABLE_FOOTER];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_BORDER_AFTER_PRECEDENCE);
-        elem.addProperty(Constants.PR_BORDER_BEFORE_PRECEDENCE);
-        elem.addProperty(Constants.PR_BORDER_END_PRECEDENCE);
-        elem.addProperty(Constants.PR_BORDER_START_PRECEDENCE);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_VISIBILITY);
-        elem.addContent(Constants.FO_TABLE_ROW);
-        elem.addContent(Constants.FO_TABLE_CELL);
-
-        elem = elements[Constants.FO_TABLE_BODY];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_BORDER_AFTER_PRECEDENCE);
-        elem.addProperty(Constants.PR_BORDER_BEFORE_PRECEDENCE);
-        elem.addProperty(Constants.PR_BORDER_END_PRECEDENCE);
-        elem.addProperty(Constants.PR_BORDER_START_PRECEDENCE);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_VISIBILITY);
-        elem.addContent(Constants.FO_TABLE_ROW);
-        elem.addContent(Constants.FO_TABLE_CELL);
-
-        elem = elements[Constants.FO_TABLE_ROW];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION);
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_BORDER_AFTER_PRECEDENCE);
-        elem.addProperty(Constants.PR_BORDER_BEFORE_PRECEDENCE);
-        elem.addProperty(Constants.PR_BORDER_END_PRECEDENCE);
-        elem.addProperty(Constants.PR_BORDER_START_PRECEDENCE);
-        elem.addProperty(Constants.PR_PAGE_BREAK_AFTER);
-        elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE);
-        elem.addProperty(Constants.PR_BREAK_AFTER);
-        elem.addProperty(Constants.PR_BREAK_BEFORE);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_HEIGHT);
-        elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
-        elem.addProperty(Constants.PR_KEEP_TOGETHER);
-        elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
-        elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
-        elem.addProperty(Constants.PR_VISIBILITY);
-        elem.addContent(Constants.FO_TABLE_CELL);
-
-        elem = elements[Constants.FO_TABLE_CELL];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_BORDER_AFTER_PRECEDENCE);
-        elem.addProperty(Constants.PR_BORDER_BEFORE_PRECEDENCE);
-        elem.addProperty(Constants.PR_BORDER_END_PRECEDENCE);
-        elem.addProperty(Constants.PR_BORDER_START_PRECEDENCE);
-        elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION);
-        elem.addProperty(Constants.PR_COLUMN_NUMBER);
-        elem.addProperty(Constants.PR_DISPLAY_ALIGN);
-        elem.addProperty(Constants.PR_RELATIVE_ALIGN);
-        elem.addProperty(Constants.PR_EMPTY_CELLS);
-        elem.addProperty(Constants.PR_ENDS_ROW);
-        elem.addProperty(Constants.PR_HEIGHT);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_INLINE_PROGRESSION_DIMENSION);
-        elem.addProperty(Constants.PR_NUMBER_COLUMNS_SPANNED);
-        elem.addProperty(Constants.PR_NUMBER_ROWS_SPANNED);
-        elem.addProperty(Constants.PR_STARTS_ROW);
-        elem.addProperty(Constants.PR_WIDTH);
-        elem.addContent(block_elems);
-
-        elem = elements[Constants.FO_LIST_BLOCK];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonMarginPropertiesBlock);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_PAGE_BREAK_AFTER);
-        elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE);
-        elem.addProperty(Constants.PR_BREAK_AFTER);
-        elem.addProperty(Constants.PR_BREAK_BEFORE);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
-        elem.addProperty(Constants.PR_KEEP_TOGETHER);
-        elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
-        elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
-        elem.addProperty(Constants.PR_PROVISIONAL_DISTANCE_BETWEEN_STARTS);
-        elem.addProperty(Constants.PR_PROVISIONAL_LABEL_SEPARATION);
-        elem.addContent(Constants.FO_LIST_ITEM);
-
-        elem = elements[Constants.FO_LIST_ITEM];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonMarginPropertiesBlock);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_PAGE_BREAK_AFTER);
-        elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE);
-        elem.addProperty(Constants.PR_BREAK_AFTER);
-        elem.addProperty(Constants.PR_BREAK_BEFORE);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
-        elem.addProperty(Constants.PR_KEEP_TOGETHER);
-        elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
-        elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
-        elem.addProperty(Constants.PR_RELATIVE_ALIGN);
-        elem.addContent(Constants.FO_LIST_ITEM_LABEL);
-        elem.addContent(Constants.FO_LIST_ITEM_BODY);
-
-        elem = elements[Constants.FO_LIST_ITEM_BODY];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
-        elem.addProperty(Constants.PR_KEEP_TOGETHER);
-        elem.addContent(block_elems);
-
-        elem = elements[Constants.FO_LIST_ITEM_LABEL];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
-        elem.addProperty(Constants.PR_KEEP_TOGETHER);
-        elem.addContent(block_elems);
-
-        elem = elements[Constants.FO_BASIC_LINK];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperties(CommonAuralProperties);
-        elem.addProperties(CommonBorderPaddingBackgroundProperties);
-        elem.addProperties(CommonMarginPropertiesInline);
-        elem.addProperties(CommonRelativePositionProperties);
-        elem.addProperty(Constants.PR_VERTICAL_ALIGN);
-        elem.addProperty(Constants.PR_ALIGNMENT_ADJUST);
-        elem.addProperty(Constants.PR_ALIGNMENT_BASELINE);
-        elem.addProperty(Constants.PR_BASELINE_SHIFT);
-        elem.addProperty(Constants.PR_DESTINATION_PLACEMENT_OFFSET);
-        elem.addProperty(Constants.PR_DOMINANT_BASELINE);
-        elem.addProperty(Constants.PR_EXTERNAL_DESTINATION);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_INDICATE_DESTINATION);
-        elem.addProperty(Constants.PR_INTERNAL_DESTINATION);
-        elem.addProperty(Constants.PR_PAGE_BREAK_INSIDE);
-        elem.addProperty(Constants.PR_KEEP_TOGETHER);
-        elem.addProperty(Constants.PR_KEEP_WITH_NEXT);
-        elem.addProperty(Constants.PR_KEEP_WITH_PREVIOUS);
-        elem.addProperty(Constants.PR_LINE_HEIGHT);
-        elem.addProperty(Constants.PR_SHOW_DESTINATION);
-        elem.addProperty(Constants.PR_TARGET_PROCESSING_CONTEXT);
-        elem.addProperty(Constants.PR_TARGET_PRESENTATION_CONTEXT);
-        elem.addProperty(Constants.PR_TARGET_STYLESHEET);
-        elem.addContent(inline_elems);
-        elem.addContent(block_elems);
-
-        elem = elements[Constants.FO_MULTI_SWITCH];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperty(Constants.PR_AUTO_RESTORE);
-        elem.addProperty(Constants.PR_ID);
-        elem.addContent(Constants.FO_MULTI_CASE);
-
-        elem = elements[Constants.FO_MULTI_CASE];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_STARTING_STATE);
-        elem.addProperty(Constants.PR_CASE_NAME);
-        elem.addProperty(Constants.PR_CASE_TITLE);
-        elem.addContent(inline_elems);
-        elem.addContent(block_elems);
-
-        elem = elements[Constants.FO_MULTI_TOGGLE];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_SWITCH_TO);
-        elem.addContent(inline_elems);
-        elem.addContent(block_elems);
-
-        elem = elements[Constants.FO_MULTI_PROPERTIES];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addProperty(Constants.PR_ID);
-        elem.addContent(Constants.FO_MULTI_PROPERTY_SET);
-        elem.addContent(Constants.FO_WRAPPER);
-
-        elem = elements[Constants.FO_MULTI_PROPERTY_SET];
-        elem.addProperty(Constants.PR_ID);
-        elem.addProperty(Constants.PR_ACTIVE_STATE);
-
-        elem = elements[Constants.FO_FLOAT];
-        elem.addProperty(Constants.PR_FLOAT);
-        elem.addProperty(Constants.PR_CLEAR);
-        elem.addContent(block_elems);
-
-        elem = elements[Constants.FO_FOOTNOTE];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addContent(Constants.FO_INLINE);
-        elem.addContent(Constants.FO_FOOTNOTE_BODY);
-
-        elem = elements[Constants.FO_FOOTNOTE_BODY];
-        elem.addProperties(CommonAccessibilityProperties);
-        elem.addContent(block_elems);
-
-        elem = elements[Constants.FO_WRAPPER];
-        elem.addProperty(Constants.PR_ID);
-        elem.addContent(inline_elems);
-        elem.addContent(block_elems);
-
-        elem = elements[Constants.FO_MARKER];
-        elem.addProperty(Constants.PR_MARKER_CLASS_NAME);
-        elem.addContent(inline_elems);
-        elem.addContent(block_elems);
-
-        elem = elements[Constants.FO_RETRIEVE_MARKER];
-        elem.addProperty(Constants.PR_RETRIEVE_CLASS_NAME);
-        elem.addProperty(Constants.PR_RETRIEVE_POSITION);
-        elem.addProperty(Constants.PR_RETRIEVE_BOUNDARY);
-
-        // Merge the attributes from the children into the parent.
-        for (boolean dirty = true; dirty;) {
-            dirty = false;
-            for (int i = 1; i < elements.length; i++) {
-                dirty = dirty || elements[i].merge();
-            }
-        }
-        // Calculate the sparse indices for each element.
-        for (int i = 1; i < elements.length; i++) {
-            mapping[i] = makeSparseIndices(elements[i].valid);
-        }
-    }
-
-    /**
-     * Turn a BitSet into an array of shorts with the first element
-     * on the array the number of set bits in the BitSet.
-     */
-    private static short[] makeSparseIndices(BitSet set) {
-        short[] indices = new short[Constants.PROPERTY_COUNT + 1];
-        int j = 1;
-        for (int i = 0; i < Constants.PROPERTY_COUNT + 1; i++) {
-            if (set.get(i)) {
-                indices[i] = (short) j++;
-            }
-        }
-        indices[0] = (short)j;
-        return indices;
-    }
-
-    public static short[] getPropertySet(int elementId) {
-        if (mapping == null) {
-            mapping = new short[Constants.FRM_OBJ_COUNT + 1][];
-            PropertySets ps = new PropertySets();
-            ps.initializeElements();
-            ps.initializeCommon();
-            ps.initialize();
-        }
-        return mapping[elementId];
-    }
-
-    /**
-     * Determines if fo:markers are allowed as children for the given FO
-     * @param elementId Constants enumeration ID of the FO (e.g., FO_ROOT)
-     * @return true if fo:markers allowed, false otherwise
-     * @todo check if still needed after validateChildNode() fully implemented
-     */
-    public static boolean canHaveMarkers(int elementId) {
-        if (can_have_markers == null) {
-            can_have_markers = new BitSet();
-            can_have_markers.set(Constants.FO_BASIC_LINK);
-            can_have_markers.set(Constants.FO_BIDI_OVERRIDE);
-            can_have_markers.set(Constants.FO_BLOCK);
-            can_have_markers.set(Constants.FO_BLOCK_CONTAINER);
-            can_have_markers.set(Constants.FO_FLOW);
-            can_have_markers.set(Constants.FO_INLINE);
-            can_have_markers.set(Constants.FO_INLINE_CONTAINER);
-            can_have_markers.set(Constants.FO_LIST_BLOCK);
-            can_have_markers.set(Constants.FO_LIST_ITEM);
-            can_have_markers.set(Constants.FO_LIST_ITEM_BODY);
-            can_have_markers.set(Constants.FO_LIST_ITEM_LABEL);
-            can_have_markers.set(Constants.FO_TABLE);
-            can_have_markers.set(Constants.FO_TABLE_BODY);
-            can_have_markers.set(Constants.FO_TABLE_HEADER);
-            can_have_markers.set(Constants.FO_TABLE_FOOTER);
-            can_have_markers.set(Constants.FO_TABLE_CELL);
-            can_have_markers.set(Constants.FO_TABLE_AND_CAPTION);
-            can_have_markers.set(Constants.FO_TABLE_CAPTION);
-            can_have_markers.set(Constants.FO_WRAPPER);
-        }
-        return can_have_markers.get(elementId);
-    }
-
-    /**
-     * Determines if the FO generates inline areas.  Used only within flow.Block
-     * for whitespace handling
-     * @param elementId Constants enumeration ID of the FO (e.g., FO_ROOT)
-     * @return true if id property is applicable, false otherwise
-     * @todo see if more values need to be entered here (copied values over
-     *      from legacy code, list of FO's below probably incomplete)
-     * @todo see if still needed (LM has a similar generatesInlineAreas()
-     *      method)
-     */
-    public static boolean generatesInlineAreas(int elementId) {
-        if (no_inline_areas == null) {
-            no_inline_areas = new BitSet();
-            no_inline_areas.set(Constants.FO_UNKNOWN_NODE);
-            no_inline_areas.set(Constants.FO_BLOCK);
-            no_inline_areas.set(Constants.FO_BLOCK_CONTAINER);
-            no_inline_areas.set(Constants.FO_LIST_BLOCK);
-            no_inline_areas.set(Constants.FO_LIST_ITEM);
-            no_inline_areas.set(Constants.FO_TABLE);
-            no_inline_areas.set(Constants.FO_TABLE_AND_CAPTION);
-        }
-        return !(no_inline_areas.get(elementId));
-    }
-
-    /**
-     * An object that represent the properties and contents of a fo element
-     */
-    class Element {
-        BitSet relevant = new BitSet();
-        BitSet valid = new BitSet();
-        int elementId;
-        ArrayList childFOs;
-
-        Element(int elementId) {
-            this.elementId = elementId;
-        }
-
-        /**
-         * Add a single property to the element.
-         */
-        public void addProperty(int propId) {
-            relevant.set(propId);
-            valid.set(propId);
-        }
-
-        /**
-         * Add a set of properties to the element.
-         */
-        public void addProperties(BitSet properties) {
-            relevant.or(properties);
-            valid.or(properties);
-        }
-
-        /**
-         * Add a single fo element as a content child.
-         */
-        public void addContent(int elementId) {
-            if (childFOs == null) {
-                childFOs = new ArrayList();
-            }
-            childFOs.add(elements[elementId]);
-        }
-
-        /**
-         * Add a set of fo elements as content childFOs.
-         */
-        public void addContent(BitSet elements) {
-            for (int i = 0; i < elements.size(); i++) {
-                if (elements.get(i)) {
-                    addContent(i);
-                }
-            }
-        }
-
-        /**
-         * Merge the properties from the child FO's into the set of valid
-         * properties. Return true if at least one property could be added.
-         */
-        public boolean merge() {
-            if (childFOs == null) {
-                return false;
-            }
-            boolean dirty = false;
-            for (int i = 0; i < childFOs.size(); i++) {
-                Element child = (Element) childFOs.get(i);
-                BitSet childValid = child.valid;
-                int n = childValid.length();
-                for (int j = 0; j < n; j++) {
-                    if (childValid.get(j) && !valid.get(j)) {
-                        dirty = true;
-                        valid.set(j);
-                    }
-                }
-            }
-            return dirty;
-        }
-    }
-}
index f4b8f7ad6b8fbccc8f410442c6b48d74384bca6f..1ef3abd73c9480c648d5895e0083c11bb3637dce 100755 (executable)
@@ -21,7 +21,7 @@ import org.apache.fop.fo.properties.Property;
 
 /**
  * A very fast implementation of PropertyList that uses arrays to store
- * the explit set properties and another array to store cached values.
+ * the explicit set properties and another array to store cached values.
  */
 public class StaticPropertyList extends PropertyList {
     private Property[] explicit;
index 9055057a9c5b709ffb1569cb3c24e5939a349e41..183224ce1d737b1bcfbcab1ed77d32ae3e77a74f 100644 (file)
@@ -72,11 +72,34 @@ public class XMLWhiteSpaceHandler {
      * @param firstTextNode the node at which to start
      */
     public void handleWhiteSpace(FObjMixed fo, FONode firstTextNode) {
-        if (fo.getNameId() == Constants.FO_BLOCK) {
-            this.currentBlock = (Block) fo;
-            this.linefeedTreatment = currentBlock.getLinefeedTreatment();
-            this.whiteSpaceCollapse = currentBlock.getWhitespaceCollapse();
-            this.whiteSpaceTreatment = currentBlock.getWhitespaceTreatment();
+        if (fo.getNameId() == Constants.FO_BLOCK
+                || fo.getNameId() == Constants.FO_RETRIEVE_MARKER) {
+            if (fo.getNameId() == Constants.FO_BLOCK) {
+                this.currentBlock = (Block) fo;
+            } else {
+                FONode ancestor = fo.parent;
+                while (ancestor.getNameId() != Constants.FO_BLOCK
+                        && ancestor.getNameId() != Constants.FO_STATIC_CONTENT) {
+                    ancestor = ancestor.getParent();
+                }
+                if (ancestor.getNameId() == Constants.FO_BLOCK) {
+                    this.currentBlock = (Block) ancestor;
+                }
+            }
+            if (currentBlock != null) {
+                this.linefeedTreatment = currentBlock.getLinefeedTreatment();
+                this.whiteSpaceCollapse = currentBlock.getWhitespaceCollapse();
+                this.whiteSpaceTreatment = 
+                    currentBlock.getWhitespaceTreatment();
+            } else {
+                /* fo:retrieve-marker as direct child of static-content
+                 * set properties to their initial values
+                 */
+                this.linefeedTreatment = Constants.EN_TREAT_AS_SPACE;
+                this.whiteSpaceCollapse = Constants.EN_TRUE;
+                this.whiteSpaceTreatment = 
+                    Constants.EN_IGNORE_IF_SURROUNDING_LINEFEED;
+            }
         } else if (fo.getNameId() == Constants.FO_TITLE
                 || fo.getNameId() == Constants.FO_BOOKMARK_TITLE) {
             /* Two special types of FO that can contain #PCDATA
@@ -84,7 +107,8 @@ public class XMLWhiteSpaceHandler {
              */
             this.linefeedTreatment = Constants.EN_TREAT_AS_SPACE;
             this.whiteSpaceCollapse = Constants.EN_TRUE;
-            this.whiteSpaceTreatment = Constants.EN_IGNORE_IF_SURROUNDING_LINEFEED;
+            this.whiteSpaceTreatment = 
+                Constants.EN_IGNORE_IF_SURROUNDING_LINEFEED;
         }
         currentFO = fo;
         if (firstTextNode == null) {
@@ -94,7 +118,10 @@ public class XMLWhiteSpaceHandler {
         charIter = new RecursiveCharIterator(fo, firstTextNode);
         inWhiteSpace = false;
         int textNodeIndex = -1;
-        if (currentFO == currentBlock) {
+        if (currentFO == currentBlock
+                || currentBlock == null
+                || (currentFO.getNameId() == Constants.FO_RETRIEVE_MARKER
+                        && currentFO.getParent() == currentBlock)) {
             textNodeIndex = fo.childNodes.indexOf(firstTextNode);
             afterLinefeed = ((textNodeIndex == 0)
                     || (textNodeIndex > 0
@@ -104,7 +131,8 @@ public class XMLWhiteSpaceHandler {
         endOfBlock = (nextChild == null && currentFO == currentBlock);
         if (nextChild != null) {
             int nextChildId = nextChild.getNameId();
-            nextChildIsBlockLevel = (nextChildId == Constants.FO_BLOCK
+            nextChildIsBlockLevel = (
+                    nextChildId == Constants.FO_BLOCK
                     || nextChildId == Constants.FO_TABLE_AND_CAPTION
                     || nextChildId == Constants.FO_TABLE
                     || nextChildId == Constants.FO_LIST_BLOCK
@@ -149,6 +177,11 @@ public class XMLWhiteSpaceHandler {
                 addPendingInline(fo);
             }
         }
+        
+        if (currentFO == currentBlock && nextChild == null) {
+            /* end of block: clear the reference */
+            currentBlock = null;
+        }
     }
     
     /**
diff --git a/src/java/org/apache/fop/fo/expr/FopPropValFunction.java b/src/java/org/apache/fop/fo/expr/FopPropValFunction.java
deleted file mode 100644 (file)
index 47f5afd..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.fo.expr;
-
-import org.apache.fop.fo.FOPropertyMapping;
-import org.apache.fop.fo.properties.Property;
-
-
-/**
- * This appears to be an artificial function, which handles the specified
- * or initial value of the property on this object.
- */
-public class FopPropValFunction extends FunctionBase {
-
-    /**
-     * @return 1 (the maximum number of arguments)
-     */
-    public int nbArgs() {
-        return 1;
-    }
-
-    /**
-     *
-     * @param args array of arguments, which should either be empty, or the
-     * first of which should be an NCName corresponding to a property name
-     * @param pInfo PropertyInfo object to be evaluated
-     * @return the Property corresponding to the input
-     * @throws PropertyException for incorrect parameters
-     */
-    public Property eval(Property[] args,
-                         PropertyInfo pInfo) throws PropertyException {
-        String propName = args[0].getString();
-        if (propName == null) {
-            throw new PropertyException("Incorrect parameter to _int-property-value function");
-        }
-
-        int propId = FOPropertyMapping.getPropertyId(propName);
-        return pInfo.getPropertyList().get(propId);
-    }
-
-}
index 731b403f14590b183f1a00165a0442731eca32c2..8b14a0c64c9c938fe09d588e3bd727f7598a12cc 100644 (file)
@@ -24,9 +24,9 @@ import org.apache.fop.fo.Constants;
 import org.apache.fop.fo.FObj;
 import org.apache.fop.fo.FOPropertyMapping;
 import org.apache.fop.fo.flow.Table;
-import org.apache.fop.fo.flow.TableFObj;
 import org.apache.fop.fo.flow.TableCell;
 import org.apache.fop.fo.flow.TableColumn;
+import org.apache.fop.fo.flow.TableFObj;
 import org.apache.fop.fo.properties.Property;
 
 /**
index ba9ae2ddf2befcd72bf739495fe0ba08304b158a..856dd4b0392ddc953ad28a42e6985d8efbfa6944 100644 (file)
@@ -19,7 +19,8 @@
 
 package org.apache.fop.fo.expr;
 
-
+import org.apache.fop.fo.PropertyList;
+import org.apache.fop.fo.flow.Table;
 import org.apache.fop.fo.properties.Property;
 import org.apache.fop.fo.properties.TableColLength;
 
@@ -51,13 +52,20 @@ public class PPColWidthFunction extends FunctionBase {
         Number d = args[0].getNumber();
         if (d == null) {
             throw new PropertyException("Non numeric operand to "
-                    + "proportional-column-width function");
+                    + "proportional-column-width() function.");
+        }
+        PropertyList pList = pInfo.getPropertyList();
+        if (!"fo:table-column".equals(pList.getFObj().getName())) {
+            throw new PropertyException("proportional-column-width() function "
+                    + "may only be used on fo:table-column.");
         }
-        if (!pInfo.getPropertyList().getFObj().getName().equals("fo:table-column")) {
-            throw new PropertyException("proportional-column-width function "
-                    + "may only be used on table-column FO");
+        
+        Table t = (Table) pList.getParentFObj();
+        if (t.isAutoLayout()) {
+            throw new PropertyException("proportional-column-width() function "
+                    + "may only be used when fo:table has "
+                    + "table-layout=\"fixed\".");
         }
-        // Check if table-layout is "fixed"...
         return new TableColLength(d.doubleValue(), pInfo.getFO());
     }
 
index 77b35a845b4066db96f8a3033be5dc7a7c738001..699094cc76689d01432c8e38053b2a27b35bc82c 100644 (file)
@@ -62,8 +62,6 @@ public final class PropertyParser extends PropertyTokenizer {
                           new PPColWidthFunction());
         FUNCTION_TABLE.put("label-end", new LabelEndFunction());
         FUNCTION_TABLE.put("body-start", new BodyStartFunction());
-        // NOTE: used from code generated for corresponding properties
-        FUNCTION_TABLE.put("_fop-property-value", new FopPropValFunction());
 
         /**
          * * NOT YET IMPLEMENTED!!!
index 7ef26adbdcc40eec5b687396d00497bf9721ee77..03b9e96a7861ed327e542e7f4c3094977808d226 100644 (file)
@@ -86,8 +86,19 @@ public abstract class AbstractListItemPart extends FObj {
      * @see org.apache.fop.fo.FONode#endOfNode
      */
     protected void endOfNode() throws FOPException {
-        if (!blockItemFound) {
-            missingChildElementError("marker* (%block;)+");
+        if (!this.blockItemFound) {
+            String contentModel = "marker* (%block;)+";
+            if (getUserAgent().validateStrictly()) {
+                missingChildElementError(contentModel);
+            } else {
+                StringBuffer message = new StringBuffer(
+                        errorText(getLocator()));
+                message.append(getName())
+                    .append(" is missing child elements. ")
+                    .append("Required Content Model: ")
+                    .append(contentModel);
+                getLogger().warn(message.toString());
+            }
         }
     }
 
@@ -100,6 +111,6 @@ public abstract class AbstractListItemPart extends FObj {
     public String getId() {
         return id;
     }
-
+    
 }
 
index 6e9e5d662cb82254090fb01dc03fdecb050a4f32..b5dd2626f7c1331278ab7462432fbea1c1e6e7b0 100644 (file)
@@ -42,20 +42,6 @@ import org.apache.fop.fo.properties.CommonRelativePosition;
 import org.apache.fop.fo.properties.KeepProperty;
 import org.apache.fop.fo.properties.SpaceProperty;
 
-/*
-  Modified by Mark Lillywhite mark-fop@inomial.com. The changes
-  here are based on memory profiling and do not change functionality.
-  Essentially, the Block object had a pointer to a BlockArea object
-  that it created. The BlockArea was not referenced after the Block
-  was finished except to determine the size of the BlockArea, however
-  a reference to the BlockArea was maintained and this caused a lot of
-  GC problems, and was a major reason for FOP memory leaks. So,
-  the reference to BlockArea was made local, the required information
-  is now stored (instead of a reference to the complex BlockArea object)
-  and it appears that there are a lot of changes in this file, in fact
-  there are only a few sematic changes; mostly I just got rid of
-  "this." from blockArea since BlockArea is now local.
- */
  /**
   * Class modelling the fo:block object.
   */
@@ -361,6 +347,48 @@ public class Block extends FObjMixed {
         return whiteSpaceCollapse;
     }
     
+    /**
+     * @return Returns the commonAccessibility.
+     */
+    public CommonAccessibility getCommonAccessibility() {
+        return this.commonAccessibility;
+    }
+
+    /**
+     * @return Returns the commonAural.
+     */
+    public CommonAural getCommonAural() {
+        return this.commonAural;
+    }
+
+    /**
+     * @return Returns the commonRelativePosition.
+     */
+    public CommonRelativePosition getCommonRelativePosition() {
+        return this.commonRelativePosition;
+    }
+
+    /**
+     * @return Returns the hyphenationKeep.
+     */
+    public int getHyphenationKeep() {
+        return this.hyphenationKeep;
+    }
+
+    /**
+     * @return Returns the intrusionDisplace.
+     */
+    public int getIntrusionDisplace() {
+        return this.intrusionDisplace;
+    }
+
+    /**
+     * @return Returns the lineHeightShiftAdjustment.
+     */
+    public int getLineHeightShiftAdjustment() {
+        return this.lineHeightShiftAdjustment;
+    }
+
     /** @see org.apache.fop.fo.FONode#charIterator() */
     public CharIterator charIterator() {
         return NullCharIterator.getInstance();
@@ -377,4 +405,5 @@ public class Block extends FObjMixed {
     public int getNameId() {
         return FO_BLOCK;
     }
+    
 }
index d848e281dd809e4ae49f67391f61c10f623a22c5..f9c61d90f35e8c7b2385346523ff7ba5b2c9616b 100644 (file)
@@ -20,9 +20,8 @@
 package org.apache.fop.fo.flow;
 
 import java.util.HashMap;
-import java.util.Iterator;
-
 
+import org.xml.sax.Attributes;
 import org.xml.sax.Locator;
 
 import org.apache.fop.apps.FOPException;
@@ -30,6 +29,7 @@ import org.apache.fop.fo.FOEventHandler;
 import org.apache.fop.fo.FONode;
 import org.apache.fop.fo.FObj;
 import org.apache.fop.fo.FObjMixed;
+import org.apache.fop.fo.FOPropertyMapping;
 import org.apache.fop.fo.PropertyList;
 import org.apache.fop.fo.PropertyListMaker;
 import org.apache.fop.fo.ValidationException;
@@ -44,7 +44,7 @@ public class Marker extends FObjMixed {
     // End of property values
 
     private PropertyListMaker savePropertyListMaker;
-    private HashMap descPLists = new HashMap();
+    private HashMap descendantPropertyLists = new HashMap();
 
     /**
      * Create a marker fo.
@@ -76,14 +76,11 @@ public class Marker extends FObjMixed {
      * @param foNode the FO node whose property list is requested
      * @return the MarkerPropertyList of foNode
      */
-    protected MarkerPropertyList getPList(FONode foNode) {
-        return (MarkerPropertyList) descPLists.get(foNode);
-    }
-
-    protected PropertyList createPropertyList(PropertyList parent, FOEventHandler foEventHandler) throws FOPException {
-        return new MarkerPropertyList(this, parent);
+    protected MarkerPropertyList getPropertyListFor(FONode foNode) {
+        return (MarkerPropertyList) 
+            descendantPropertyLists.get(foNode);
     }
-
+    
     /** @see org.apache.fop.fo.FONode#startOfNode() */
     protected void startOfNode() {
         FOEventHandler foEventHandler = getFOEventHandler(); 
@@ -92,30 +89,18 @@ public class Marker extends FObjMixed {
         foEventHandler.setPropertyListMaker(new PropertyListMaker() {
             public PropertyList make(FObj fobj, PropertyList parentPropertyList) {
                 PropertyList pList = new MarkerPropertyList(fobj, parentPropertyList);
-                descPLists.put(fobj, pList);
+                descendantPropertyLists.put(fobj, pList);
                 return pList;
             }
         });
     }
-
+    
     /** @see org.apache.fop.fo.FONode#endOfNode() */
     protected void endOfNode() throws FOPException {
         super.endOfNode();
         // Pop the MarkerPropertyList maker.
         getFOEventHandler().setPropertyListMaker(savePropertyListMaker);
         savePropertyListMaker = null;
-        // unparent the child property lists
-        Iterator iter = getChildNodes();
-        if (iter != null) {
-            while (iter.hasNext()) {
-                FONode child = (FONode) iter.next();
-                MarkerPropertyList pList
-                    = (MarkerPropertyList) descPLists.get(child);
-                if (pList != null) {
-                    pList.setParentPropertyList(null);
-                }
-            }
-        }
     }
 
     /**
@@ -132,7 +117,11 @@ public class Marker extends FObjMixed {
             invalidChildError(loc, nsURI, localName);
         }
     }
-
+    
+    protected boolean inMarker() {
+        return true;
+    }
+    
     /**
      * Return the "marker-class-name" property.
      */
@@ -160,30 +149,252 @@ public class Marker extends FObjMixed {
     }
     
     /**
-     * An implementation of PropertyList which only stores the explicit
-     * assigned properties. It is memory efficient but slow. 
+     * An implementation of PropertyList which only stores the explicitly
+     * specified properties/attributes as bundles of name-value-namespace
+     * strings
      */
-    public class MarkerPropertyList extends PropertyList {
-        HashMap explicit = new HashMap();
-        public MarkerPropertyList(FObj fobj, PropertyList parentPropertyList) {
-            super(fobj, parentPropertyList);
+    protected class MarkerPropertyList extends PropertyList 
+            implements Attributes {
+        
+        protected class MarkerAttribute {
+            
+            protected String namespace;
+            protected String qname;
+            protected String name;
+            protected String value;
+            
+            /**
+             * Main constructor
+             * @param namespace the namespace URI
+             * @param qname the qualified name
+             * @param name  the name
+             * @param value the value
+             */
+            public MarkerAttribute(String namespace, String qname, 
+                    String name, String value) {
+                this.namespace = namespace;
+                this.qname = qname;
+                this.name = (name == null ? qname : name);
+                this.value = value;
+            }
+            
+            /**
+             * Convenience constructor for FO attributes
+             * @param name  the attribute name
+             * @param value the attribute value
+             */
+            public MarkerAttribute(String name, String value) {
+                this.namespace = null;
+                this.qname = name;
+                this.name = name;
+                this.value = value;
+            }
         }
         
+        /** the array of attributes **/
+        private MarkerAttribute[] attribs;
+        
         /**
-         * Set the parent property list. Used to assign a new parent 
-         * before re-binding all the child elements.   
+         * Overriding default constructor
+         * 
+         * @param fobj  the FObj to attach
+         * @param parentPropertyList    ignored
          */
-        public void setParentPropertyList(PropertyList parentPropertyList) {
-            this.parentPropertyList = parentPropertyList;
+        public MarkerPropertyList(FObj fobj, PropertyList parentPropertyList) {
+            /* ignore parentPropertyList
+             * won't be used because the attributes will be stored
+             * without resolving
+             */
+            super(fobj, null);
         }
+        
+        /**
+         * Override that doesn't convert the attributes to Property instances,
+         * but simply stores the attributes for later processing;
+         * 
+         * @see org.apache.fop.fo.PropertyList#addAttributesToList(Attributes)
+         */
+        public void addAttributesToList(Attributes attributes) 
+                    throws ValidationException {
+            
+            this.attribs = new MarkerAttribute[attributes.getLength()];
 
+            String name;
+            String value;
+            String namespace;
+            String qname;
+            
+            for (int i = attributes.getLength(); --i >= 0;) {
+                namespace = attributes.getURI(i);
+                qname = attributes.getQName(i);
+                name = attributes.getLocalName(i);
+                value = attributes.getValue(i);
+                
+                this.attribs[i] = 
+                    new MarkerAttribute(namespace, qname, name, value);
+            }
+        }
+        
+        /**
+         * Null implementation; not used by this type of PropertyList
+         * @see org.apache.fop.fo.PropertyList#putExplicit(int, Property)
+         */
         public void putExplicit(int propId, Property value) {
-            explicit.put(new Integer(propId), value);
+            //nop
         }
 
+        /**
+         * Null implementation; not used by this type of PropertyList
+         * @see org.apache.fop.fo.PropertyList#getExplicit(int)
+         */
         public Property getExplicit(int propId) {
-            return (Property) explicit.get(new Integer(propId));
+            return null;
+        }
+
+        /**
+         * @see org.xml.sax.Attributes#getLength()
+         */
+        public int getLength() {
+            if (attribs == null) {
+                return 0;
+            } else {
+                return attribs.length;
+            }
+        }
+
+        /**
+         * @see org.xml.sax.Attributes#getURI()
+         */
+        public String getURI(int index) {
+            if (attribs != null 
+                    && index < attribs.length
+                    && index >= 0
+                    && attribs[index] != null) {
+                return attribs[index].namespace;
+            } else {
+                return null;
+            }
+        }
+
+        /**
+         * @see org.xml.sax.Attributes#getLocalName()
+         */
+        public String getLocalName(int index) {
+            if (attribs != null 
+                    && index < attribs.length
+                    && index >= 0
+                    && attribs[index] != null) {
+                return attribs[index].name;
+            } else {
+                return null;
+            }
+        }
+
+        /**
+         * @see org.xml.sax.Attributes#getQName()
+         */
+        public String getQName(int index) {
+            if (attribs != null 
+                    && index < attribs.length
+                    && index >= 0
+                    && attribs[index] != null) {
+                return attribs[index].qname;
+            } else {
+                return null;
+            }
         }
-    }
 
-}
+        /**
+         * Default implementation; not used
+         * @see org.xml.sax.Attributes#getType()
+         */
+        public String getType(int index) {
+            return "CDATA";
+        }
+
+        /**
+         * @see org.xml.sax.Attributes#getValue()
+         */
+        public String getValue(int index) {
+            if (attribs != null 
+                    && index < attribs.length
+                    && index >= 0
+                    && attribs[index] != null) {
+                return attribs[index].value;
+            } else {
+                return null;
+            }
+        }
+
+        /**
+         * @see org.xml.sax.Attributes#getIndex()
+         */
+        public int getIndex(String name, String namespace) {
+            int index = -1;
+            if (attribs != null && name != null && namespace != null) {
+                for (int i = attribs.length; --i >= 0;) {
+                    if (attribs[i] != null
+                            && namespace.equals(attribs[i].namespace)
+                            && name.equals(attribs[i].name)) {
+                        break;
+                    }
+                }
+            }
+            return index;
+        }
+
+        /**
+         * @see org.xml.sax.Attributes#getIndex()
+         */
+        public int getIndex(String qname) {
+            int index = -1;
+            if (attribs != null && qname != null) {
+                for (int i = attribs.length; --i >= 0;) {
+                    if (attribs[i] != null 
+                            && qname.equals(attribs[i].qname)) {
+                        break;
+                    }
+                }
+            }
+            return index;
+        }
+
+        /**
+         * Default implementation; not used
+         * @see org.xml.sax.Attributes#getType()
+         */
+        public String getType(String name, String namespace) {
+            return "CDATA";
+        }
+
+        /**
+         * Default implementation; not used
+         * @see org.xml.sax.Attributes#getType()
+         */
+        public String getType(String qname) {
+            return "CDATA";
+        }
+
+        /**
+         * @see org.xml.sax.Attributes#getValue()
+         */
+        public String getValue(String name, String namespace) {
+            int index = getIndex(name, namespace);
+            if (index > 0) {
+                return getValue(index);
+            }
+            return null;
+        }
+
+        /**
+         * @see org.xml.sax.Attributes#getValue()
+         */
+        public String getValue(String qname) {
+            int index = getIndex(qname);
+            if (index > 0) {
+                return getValue(index);
+            }
+            return null;
+        }
+    }
+}
\ No newline at end of file
index bbdaa24a78a6889c39356b680e84271a95397f30..8665ed81034adc7018b844b89d60bec2a378cd9e 100644 (file)
 
 package org.apache.fop.fo.flow;
 
-import java.util.Map;
 import java.util.HashMap;
 import java.util.Iterator;
-import java.util.ArrayList;
-
-import org.xml.sax.Locator;
-
-import org.apache.commons.logging.Log;
 
 import org.apache.fop.apps.FOPException;
-import org.apache.fop.fo.FOEventHandler;
 import org.apache.fop.fo.FONode;
-import org.apache.fop.fo.FOText;
+import org.apache.fop.fo.FOPropertyMapping;
 import org.apache.fop.fo.FObj;
 import org.apache.fop.fo.FObjMixed;
+import org.apache.fop.fo.FOText;
 import org.apache.fop.fo.PropertyList;
 import org.apache.fop.fo.StaticPropertyList;
 import org.apache.fop.fo.ValidationException;
+import org.apache.fop.fo.expr.PropertyException;
+import org.apache.fop.fo.properties.Property;
+import org.apache.fop.fo.properties.PropertyMaker;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+
 
 
 /**
@@ -44,7 +44,7 @@ import org.apache.fop.fo.ValidationException;
  * This will create a layout manager that will retrieve
  * a marker based on the information.
  */
-public class RetrieveMarker extends FObj {
+public class RetrieveMarker extends FObjMixed {
     // The value of properties relevant for fo:retrieve-marker.
     private String retrieveClassName;
     private int retrievePosition;
@@ -78,7 +78,9 @@ public class RetrieveMarker extends FObj {
         
         if (retrieveClassName == null || retrieveClassName.equals("")) {
             missingPropertyError("retrieve-class-name");
-        }        
+        }
+        
+        propertyList = pList.getParentPropertyList();
     }
     
     /**
@@ -90,40 +92,70 @@ public class RetrieveMarker extends FObj {
             invalidChildError(loc, nsURI, localName);
     }
 
-    protected PropertyList createPropertyList(PropertyList parent, 
-            FOEventHandler foEventHandler) throws FOPException {
-        // TODO: A special RetrieveMarkerPropertyList would be more memory
-        // efficient. Storing a StaticPropertyList like this will keep all
-        // the parent PropertyLists alive.
-        propertyList = new StaticPropertyList(this, parent);
-        return propertyList;
-    }
-
-    public PropertyList getPropertyList() {
-        return propertyList;
-    }
-
     /**
-     * Return the "retrieve-class-name" property.
+     * @return the "retrieve-class-name" property.
      */
     public String getRetrieveClassName() {
         return retrieveClassName;
     }
 
     /**
-     * Return the "retrieve-position" property.
+     * @return the "retrieve-position" property (enum value).
      */
     public int getRetrievePosition() {
         return retrievePosition;
     }
 
     /**
-     * Return the "retrieve-boundry" property.
+     * @return the "retrieve-boundary" property (enum value).
      */
     public int getRetrieveBoundary() {
         return retrieveBoundary;
     }
+    
+    private PropertyList createPropertyListFor(FObj fo, PropertyList parent) {
+        return getFOEventHandler().getPropertyListMaker().make(fo, parent);
+    }
+
+    private void cloneSingleNode(FONode child, FONode newParent,
+                            Marker marker, PropertyList parentPropertyList)
+        throws FOPException {
 
+        if (child != null) {
+            FONode newChild = child.clone(newParent, true);
+            if (child instanceof FObj) {
+                Marker.MarkerPropertyList pList;
+                PropertyList newPropertyList = createPropertyListFor(
+                            (FObj) newChild, parentPropertyList);
+                
+                pList = marker.getPropertyListFor(child);
+                newChild.processNode(
+                        child.getLocalName(),
+                        getLocator(),
+                        pList,
+                        newPropertyList);
+                if (newChild.getNameId() == FO_TABLE) {
+                    Table t = (Table) child;
+                    cloneSubtree(t.getColumns().listIterator(),
+                            newChild, marker, newPropertyList);
+                    cloneSingleNode(t.getTableHeader(),
+                            newChild, marker, newPropertyList);
+                    cloneSingleNode(t.getTableFooter(),
+                            newChild, marker, newPropertyList);
+                }
+                cloneSubtree(child.getChildNodes(), newChild,
+                        marker, newPropertyList);
+                if (newChild instanceof FObjMixed) {
+                    handleWhiteSpaceFor((FObjMixed) newChild);
+                }
+            } else if (child instanceof FOText) {
+                FOText ft = (FOText) newChild;
+                ft.bind(parentPropertyList);
+            }
+            addChildTo(newChild, (FObj) newParent);
+        }
+    }
+    
     /**
      * Clone the FO nodes in the parent iterator,
      * attach the new nodes to the new parent,
@@ -136,94 +168,48 @@ public class RetrieveMarker extends FObj {
      * @param descPLists the map of the new nodes to property lists
      */
     private void cloneSubtree(Iterator parentIter, FONode newParent,
-                              Marker marker, Map descPLists)
-        throws FOPException {
-        if (parentIter == null) return;
-        while (parentIter.hasNext()) {
-            FONode child = (FONode) parentIter.next();
-            FONode newChild = child.clone(newParent, true);
-            descPLists.put(newChild, marker.getPList(child));
-            cloneSubtree(child.getChildNodes(), newChild, marker, descPLists);
-        }
-    }
-
-    /**
-     * Clone the subtree of marker,
-     * and attach the new subtree to this node.
-     * The property lists are not cloned;
-     * the existing property lists of the direct children
-     * are reparented to the property list of this node.
-     * @param marker the marker that is to be cloned
-     * @param descPLists the map of the new nodes to property lists
-     */
-    private void cloneFromMarker(Marker marker, Map descPLists)
+                              Marker marker, PropertyList parentPropertyList)
         throws FOPException {
-        // release child nodes from a possible earlier layout
-        childNodes = new ArrayList();
-        Iterator markerIter = marker.getChildNodes();
-        cloneSubtree(markerIter, this, marker, descPLists);
-        // reparent the property lists of the direct children
-        for (Iterator iter = getChildNodes(); iter.hasNext(); ) {
-            FONode child = (FONode) iter.next();
-            Marker.MarkerPropertyList pList
-                = (Marker.MarkerPropertyList) descPLists.get(child);
-            if (pList != null) {
-                pList.setParentPropertyList(propertyList);
+        if (parentIter != null) {
+            FONode child;
+            while (parentIter.hasNext()) {
+                child = (FONode) parentIter.next();
+                cloneSingleNode(child, newParent, 
+                        marker, parentPropertyList);
             }
         }
     }
 
-    /**
-     * Bind the new nodes to the property values in this context
-     * @param descPLists the map of the new nodes to property lists
-     */
-    private void bindChildren(Map descPLists) throws FOPException {
-        for (Iterator i = descPLists.keySet().iterator(); i.hasNext(); ) {
-            FONode desc = (FONode) i.next();
-            PropertyList descPList;
-            if (desc instanceof FObj) {
-                descPList = (PropertyList) descPLists.get(desc);
-                ((FObj) desc).bind(descPList);
-            } else if (desc instanceof FOText) {
-                descPList = (PropertyList) descPLists.get(desc.getParent());
-                if (descPList == null) {
-                    descPList = propertyList;
-                }
-                ((FOText) desc).bind(descPList);
-            }
+    private void cloneFromMarker(Marker marker)
+        throws FOPException {
+        // clean up remnants from a possible earlier layout
+        if (childNodes != null) {
+            currentTextNode = null;
+            childNodes.removeAll(childNodes);
         }
+        cloneSubtree(marker.getChildNodes(), this, 
+                        marker, propertyList);
     }
 
     /**
-     * Clone the subtree of marker
-     * and bind the nodes to the property values in this context.
-     * The property lists are not cloned,
-     * but the subtree is attached to the property list of this node.
-     * This is only needed for the binding of the FO nodes.
-     * After that a subsequent retrieve-marker
-     * may reparent the property lists.
+     * Clone the subtree of the given marker
+     * 
      * @param marker the marker that is to be cloned
      */
     public void bindMarker(Marker marker) {
-        // assert(marker != null);
-        // catch empty marker
-        if (marker.getChildNodes() == null) {
-            return;
-        }
-        HashMap descPLists = new HashMap();
-        try {
-            cloneFromMarker(marker, descPLists);
-        } catch (FOPException exc) {
-            Log log = getLogger();
-            log.error("fo:retrieve-marker unable to clone subtree of fo:marker", exc);
-            return;
-        }
-        try {
-            bindChildren(descPLists);
-        } catch (FOPException exc) {
-            Log log = getLogger();
-            log.error("fo:retrieve-marker unable to rebind property values", exc);
+        if (marker.getChildNodes() != null) {
+            try {
+                cloneFromMarker(marker);
+            } catch (FOPException exc) {
+                log.error("fo:retrieve-marker unable to clone "
+                        + "subtree of fo:marker (marker-class-name="
+                        + marker.getMarkerClassName() + ")", exc);
+                return;
+            }
+        } else if (log.isInfoEnabled()) {
+            log.info("Empty marker retrieved...");
         }
+        return;
     }
 
     /** @see org.apache.fop.fo.FONode#getLocalName() */
@@ -236,5 +222,5 @@ public class RetrieveMarker extends FObj {
      */
     public int getNameId() {
         return FO_RETRIEVE_MARKER;
-    }
-}
+    }    
+}
\ No newline at end of file
index 954c3ba0b0627debe84305c7d67321f42dbd28e5..9a4724ed4b53da3f5ec415602db5ec8149a34022 100644 (file)
@@ -27,6 +27,7 @@ import org.xml.sax.Locator;
 import org.apache.fop.apps.FOPException;
 import org.apache.fop.datatypes.ValidationPercentBaseContext;
 import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.FObj;
 import org.apache.fop.fo.PropertyList;
 import org.apache.fop.fo.StaticPropertyList;
 import org.apache.fop.fo.ValidationException;
@@ -209,15 +210,19 @@ public class Table extends TableFObj {
      * @see org.apache.fop.fo.FONode#endOfNode
      */
     protected void endOfNode() throws FOPException {
+        
         if (!tableBodyFound) {
            missingChildElementError(
                    "(marker*,table-column*,table-header?,table-footer?"
                        + ",table-body+)");
         }
-        if (columns != null && !columns.isEmpty()) {
-            for (int i = columns.size(); --i >= 0;) {
-                if (isColumnNumberUsed(i + 1)) {
-                    ((TableColumn) columns.get(i)).releasePropertyList();
+        if (!inMarker()) {
+            if (columns != null && !columns.isEmpty()) {
+                for (int i = columns.size(); --i >= 0;) {
+                    TableColumn col = (TableColumn) columns.get(i);
+                    if (col != null) {
+                        col.releasePropertyList();
+                    }
                 }
             }
         }
@@ -229,7 +234,14 @@ public class Table extends TableFObj {
      */
     protected void addChildNode(FONode child) throws FOPException {
         if ("fo:table-column".equals(child.getName())) {
-            addColumnNode((TableColumn) child);
+            if (columns == null) {
+                columns = new java.util.ArrayList();
+            }
+            if (!inMarker()) {
+                addColumnNode((TableColumn) child);
+            } else {
+                columns.add((TableColumn) child);
+            }
         } else {
             if ("fo:table-footer".equals(child.getName())) {
                 tableFooter = (TableBody) child;
@@ -252,9 +264,6 @@ public class Table extends TableFObj {
     private void addColumnNode(TableColumn col) {
         int colNumber = col.getColumnNumber();
         int colRepeat = col.getNumberColumnsRepeated();
-        if (columns == null) {
-            columns = new java.util.ArrayList();
-        }
         if (columns.size() < colNumber) {
             //add nulls for non-occupied indices between
             //the last column up to and including the current one
@@ -273,6 +282,10 @@ public class Table extends TableFObj {
                 columns.add(col);
             }
         }
+        //flag column indices used by this column
+        int startIndex = columnIndex - 1;
+        int endIndex = startIndex + colRepeat;
+        flagColumnIndices(startIndex, endIndex);
     }
 
     /** @return true of table-layout="auto" */
@@ -458,9 +471,17 @@ public class Table extends TableFObj {
     }
     
     /**
-     * @see org.apache.fop.fo.flow.TableFObj#existsUsedColumnIndices()
+     * @see org.apache.fop.fo.FONode#clone(FONode, boolean)
      */
-    protected boolean existsUsedColumnIndices() {
-        return (usedColumnIndices != null);
+    public FONode clone(FONode parent, boolean removeChildren)
+        throws FOPException {
+        FObj fobj = (FObj) super.clone(parent, removeChildren);
+        if (removeChildren) {
+            Table t = (Table) fobj;
+            t.columns = null;
+            t.tableHeader = null;
+            t.tableFooter = null;
+        }
+        return fobj;
     }
 }
index 655279ee8709293c6c0c8834d8efd1e01357e488..0a50b946d0e1db6a444813abd660f6b0829f362b 100644 (file)
@@ -25,6 +25,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
 
+import org.xml.sax.Attributes;
 import org.xml.sax.Locator;
 
 import org.apache.fop.apps.FOPException;
@@ -33,6 +34,7 @@ import org.apache.fop.fo.FObj;
 import org.apache.fop.fo.PropertyList;
 import org.apache.fop.fo.StaticPropertyList;
 import org.apache.fop.fo.ValidationException;
+import org.apache.fop.fo.flow.TableFObj.PendingSpan;
 import org.apache.fop.fo.properties.CommonAccessibility;
 import org.apache.fop.fo.properties.CommonAural;
 import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
@@ -62,7 +64,7 @@ public class TableBody extends TableFObj {
      * used for initial values of column-number property
      */
     protected List pendingSpans;
-    protected BitSet usedColumnIndices = new BitSet();
+    protected BitSet usedColumnIndices;
     private int columnIndex = 1;
     protected boolean firstRow = true;
     
@@ -87,11 +89,30 @@ public class TableBody extends TableFObj {
         savedPropertyList = pList;
     }
     
+    /**
+     * @see org.apache.fop.fo.FONode#processNode()
+     */
+    public void processNode(String elementName, Locator locator, 
+                            Attributes attlist, PropertyList pList) 
+                    throws FOPException {
+        if (!inMarker()) {
+            if (getTable().columns != null) {
+                int cap = getTable().columns.size();
+                pendingSpans = new java.util.ArrayList(cap);
+                usedColumnIndices = new java.util.BitSet(cap);
+            } else {
+                pendingSpans = new java.util.ArrayList();
+                usedColumnIndices = new java.util.BitSet();
+            }
+            setNextColumnIndex();
+        }
+        super.processNode(elementName, locator, attlist, pList);
+    }
+
     /**
      * @see org.apache.fop.fo.FONode#startOfNode
      */
     protected void startOfNode() throws FOPException {
-        initPendingSpans();
         getFOEventHandler().startBody(this);
     }
 
@@ -99,7 +120,16 @@ public class TableBody extends TableFObj {
      * @see org.apache.fop.fo.FONode#endOfNode
      */
     protected void endOfNode() throws FOPException {
+        
+        if (!inMarker()) {
+            // clean up
+            savedPropertyList = null;
+            pendingSpans = null;
+            usedColumnIndices = null;
+        }
+        
         getFOEventHandler().endBody(this);
+        
         if (!(tableRowsFound || tableCellsFound)) {
             if (getUserAgent().validateStrictly()) {
                 missingChildElementError("marker* (table-row+|table-cell+)");
@@ -110,18 +140,11 @@ public class TableBody extends TableFObj {
             }
         }
         
+        /*
         if (tableCellsFound) {
             convertCellsToRows();
         }
-        
-        //reset column index (so that it would be
-        //correct if the table is cloned during
-        //marker retrieval)
-        resetColumnIndex();
-        //release references
-        savedPropertyList = null;
-        pendingSpans = null;
-        usedColumnIndices = null;
+        */
     }
 
     /**
@@ -158,6 +181,18 @@ public class TableBody extends TableFObj {
         }
     }
     
+    /**
+     * @see org.apache.fop.fo.FONode#addChildNode(FONode)
+     */
+    protected void addChildNode(FONode child) throws FOPException {
+        if (!inMarker()) {
+            if (firstRow && child.getNameId() == FO_TABLE_ROW) {
+                firstRow = false;
+            }
+        }
+        super.addChildNode(child);
+    }
+    
     /**
      * If table-cells are used as direct children of a table-body|header|footer
      * they are replaced in this method by proper table-rows.
@@ -243,15 +278,17 @@ public class TableBody extends TableFObj {
      *  column-number of 2, since the first column is already 
      *  occupied...)
      */
-    protected void initPendingSpans() {
-        if (getTable().columns != null) {
-            List tableCols = getTable().columns;
-            pendingSpans = new java.util.ArrayList(tableCols.size());
-            for (int i = tableCols.size(); --i >= 0;) {
-                pendingSpans.add(null);
-            }
-        } else {
-            if (firstRow && pendingSpans == null) {
+    protected void initPendingSpans(FONode child) {
+        if (child.getNameId() == FO_TABLE_ROW) {
+            pendingSpans = ((TableRow) child).pendingSpans;
+        } else if (pendingSpans == null) {
+            if (getTable().columns != null) {
+                List tableCols = getTable().columns;
+                pendingSpans = new java.util.ArrayList(tableCols.size());
+                for (int i = tableCols.size(); --i >= 0;) {
+                    pendingSpans.add(null);
+                }
+            } else {
                 pendingSpans = new java.util.ArrayList();
             }
         }
@@ -262,7 +299,7 @@ public class TableBody extends TableFObj {
      * 
      * @return the next column number to use
      */
-    public int getCurrentColumnIndex() {
+    protected int getCurrentColumnIndex() {
         return columnIndex;
     }
 
@@ -273,7 +310,7 @@ public class TableBody extends TableFObj {
      * 
      * @param newIndex  the new column index
      */
-    public void setCurrentColumnIndex(int newIndex) {
+    protected void setCurrentColumnIndex(int newIndex) {
         columnIndex = newIndex;
     }
 
@@ -281,11 +318,12 @@ public class TableBody extends TableFObj {
      * Resets the current column index for the TableBody
      *
      */
-    public void resetColumnIndex() {
+    protected void resetColumnIndex() {
         columnIndex = 1;
-        for (int i = 0; i < usedColumnIndices.size(); i++) {
+        for (int i = usedColumnIndices.length(); --i >= 0;) {
             usedColumnIndices.clear(i);
         }
+        
         PendingSpan pSpan;
         for (int i = pendingSpans.size(); --i >= 0;) {
             pSpan = (PendingSpan) pendingSpans.get(i);
@@ -293,13 +331,10 @@ public class TableBody extends TableFObj {
                 pSpan.rowsLeft--;
                 if (pSpan.rowsLeft == 0) {
                     pendingSpans.set(i, null);
+                } else {
+                    usedColumnIndices.set(i);
                 }
             }
-            if (pendingSpans.get(i) != null) {
-                usedColumnIndices.set(i);
-            } else {
-                usedColumnIndices.clear(i);
-            }
         }
         if (!firstRow) {
             setNextColumnIndex();
@@ -310,19 +345,19 @@ public class TableBody extends TableFObj {
      * Increases columnIndex to the next available value
      *
      */
-    private void setNextColumnIndex() {
+    protected void setNextColumnIndex() {
         while (usedColumnIndices.get(columnIndex - 1)) {
             //increment columnIndex
             columnIndex++;
-            //if the table has explicit columns, and
-            //the updated index is not assigned to any
-            //column, increment further until the next
-            //index occupied by a column...
-            if (getTable().columns != null) {
-                while (columnIndex <= getTable().columns.size()
-                        && !getTable().isColumnNumberUsed(columnIndex) ) {
-                    columnIndex++;
-                }
+        }
+        //if the table has explicit columns, and
+        //the index is not assigned to any
+        //column, increment further until the next
+        //index occupied by a column...
+        if (getTable().columns != null) {
+            while (columnIndex <= getTable().columns.size()
+                    && !getTable().isColumnNumberUsed(columnIndex) ) {
+                columnIndex++;
             }
         }
     }
@@ -337,11 +372,10 @@ public class TableBody extends TableFObj {
      *          b) there is no previous cell (implicit 
      *             start of row)
      */
-    protected boolean lastCellEndedRow(TableCell currentCell) {
-        if (childNodes != null && childNodes.indexOf(currentCell) > 0) {
-            FONode prevNode = (FONode) childNodes.get(
-                    childNodes.indexOf(currentCell) - 1);
-            if (prevNode != null && prevNode.getNameId() == FO_TABLE_CELL) {
+    protected boolean previousCellEndedRow() {
+        if (childNodes != null) {
+            FONode prevNode = (FONode) childNodes.get(childNodes.size() - 1);
+            if (prevNode.getNameId() == FO_TABLE_CELL) {
                 return ((TableCell) prevNode).endsRow();
             }
         }
@@ -368,11 +402,4 @@ public class TableBody extends TableFObj {
         }
         setNextColumnIndex();
     }
-    
-    /**
-     * @see org.apache.fop.fo.flow.TableFObj#existsUsedColumnIndices()
-     */
-    protected boolean existsUsedColumnIndices() {
-        return (usedColumnIndices != null);
-    }
 }
index 15cbac391fe667053c21972afd872d8176055b83..467a2a8561261ccc4722d13f60e6926e6676baa1 100644 (file)
@@ -22,6 +22,7 @@ package org.apache.fop.fo.flow;
 import java.util.BitSet;
 import java.util.List;
 
+import org.xml.sax.Attributes;
 import org.xml.sax.Locator;
 
 import org.apache.fop.apps.FOPException;
@@ -30,10 +31,12 @@ import org.apache.fop.datatypes.Numeric;
 import org.apache.fop.fo.FONode;
 import org.apache.fop.fo.PropertyList;
 import org.apache.fop.fo.ValidationException;
+import org.apache.fop.fo.expr.PropertyException;
 import org.apache.fop.fo.properties.CommonAccessibility;
 import org.apache.fop.fo.properties.CommonAural;
 import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
 import org.apache.fop.fo.properties.CommonRelativePosition;
+import org.apache.fop.fo.properties.KeepProperty;
 import org.apache.fop.fo.properties.LengthRangeProperty;
 
 /**
@@ -59,6 +62,9 @@ public class TableCell extends TableFObj {
     private Numeric numberRowsSpanned;
     private int startsRow;
     private Length width;
+    private KeepProperty keepTogether;
+    private KeepProperty keepWithNext;
+    private KeepProperty keepWithPrevious;
     // End of property values
 
     /** used for FO validation */
@@ -94,11 +100,6 @@ public class TableCell extends TableFObj {
     /** Ypos of cell ??? */
     protected int top;
 
-    /**
-     * Set to true if all content completely laid out.
-     */
-    private boolean bDone = false;
-    
     /**
      * @param parent FONode that is the parent of this object
      */
@@ -122,16 +123,14 @@ public class TableCell extends TableFObj {
         height = pList.get(PR_HEIGHT).getLength();
         id = pList.get(PR_ID).getString();
         inlineProgressionDimension = pList.get(PR_INLINE_PROGRESSION_DIMENSION).getLengthRange();
+        columnNumber = pList.get(PR_COLUMN_NUMBER).getNumeric();
         numberColumnsSpanned = pList.get(PR_NUMBER_COLUMNS_SPANNED).getNumeric();
         numberRowsSpanned = pList.get(PR_NUMBER_ROWS_SPANNED).getNumeric();
         startsRow = pList.get(PR_STARTS_ROW).getEnum();
         width = pList.get(PR_WIDTH).getLength();
-        
-        //Check to make sure we're not in retrieve-marker context
-        //TODO: Can this be generalized/extended to other FOs/Properties?
-        if (((TableFObj) parent).existsUsedColumnIndices()) {
-            columnNumber = pList.get(PR_COLUMN_NUMBER).getNumeric();
-        }
+        keepTogether = pList.get(PR_KEEP_TOGETHER).getKeep();
+        keepWithNext = pList.get(PR_KEEP_WITH_NEXT).getKeep();
+        keepWithPrevious = pList.get(PR_KEEP_WITH_PREVIOUS).getKeep();
         
         super.bind(pList);
     }
@@ -163,112 +162,9 @@ public class TableCell extends TableFObj {
             getLogger().warn("starts-row/ends-row for fo:table-cells "
                     + "non-applicable for children of an fo:table-row.");
         }
-        updateParentColumnIndex();
         getFOEventHandler().endCell(this);
     }
 
-    private void updateParentColumnIndex() {
-        
-        int rowSpan = getNumberRowsSpanned();
-        int colSpan = getNumberColumnsSpanned();
-        int columnIndex = ((TableFObj) parent).getCurrentColumnIndex();
-        
-        int i = -1;
-        while (++i < colSpan) {
-            //if table has explicit columns and the column-number isn't
-            //assigned to any column, increment further until the next
-            //column is encountered
-            if (getTable().getColumns() != null) {
-                while (columnIndex <= getTable().getColumns().size()
-                        && !getTable().isColumnNumberUsed(columnIndex)) {
-                    columnIndex++;
-                }
-            }
-            //if column-number is already in use by another cell
-            //in the current row => error!
-            if (((TableFObj) parent).isColumnNumberUsed(columnIndex + i)) {
-                log.error("fo:table-cell overlaps in column "
-                        + (columnIndex + i));
-            }
-        }
-
-        if (parent.getNameId() == FO_TABLE_ROW) {
-            /* parent is a fo:table-row */
-            TableRow row = (TableRow) parent;
-            TableBody body = (TableBody) parent.getParent();
-            
-            if (body.isFirst(row) && getTable().columns == null ) {
-                row.pendingSpans.add(null);
-                if (row.usedColumnIndices == null) {
-                    row.usedColumnIndices = new BitSet();
-                }
-            }
-            //if the current cell spans more than one row,
-            //update pending span list for the next row
-            if (rowSpan > 1) {
-                for (i = colSpan; --i >= 0;) {
-                    row.pendingSpans.set(columnIndex - 1 + i, 
-                            new PendingSpan(rowSpan));
-                }
-            }
-        } else {
-            /* parent is (should be) a fo:table-body/-header/-footer */
-            TableBody body = (TableBody) parent;
-            
-            /* if body.firstRow is still true, and :
-             * a) the cell starts a row,
-             * b) there was a previous cell 
-             * c) that previous cell didn't explicitly end the previous row
-             *  => set firstRow flag to false
-             */
-            if (startsRow() && body.firstRow) {
-                if (!body.lastCellEndedRow(this)) {
-                    body.firstRow = false;
-                }
-            }
-            
-            /* if there were no explicit columns, pendingSpans
-             * will not be properly initialized for the first row...
-             */
-            if (body.firstRow && getTable().columns == null) {
-                for (i = colSpan; --i >= 0;) {
-                    body.pendingSpans.add(null);
-                }
-            }
-            
-            /* if the current cell spans more than one row,
-             * update pending span list for the next row
-             */
-            if (rowSpan > 1) {
-                for (i = colSpan; --i >= 0;) {
-                    body.pendingSpans.set(columnIndex - 1 + i, 
-                            new PendingSpan(rowSpan));
-                }
-            }
-        }
-        //flag column indices used by this cell,
-        //take into account that possibly not all column-numbers
-        //are used by columns in the parent table (if any),
-        //so a cell spanning three columns, might actually
-        //take up more than three columnIndices...
-        int startIndex = columnIndex - 1;
-        int endIndex = startIndex + colSpan;
-        if (getTable().columns != null) {
-            List cols = getTable().columns;
-            int tmpIndex = endIndex;
-            for (i = startIndex; i <= tmpIndex; ++i) {
-                if (i < cols.size() && cols.get(i) == null) {
-                    endIndex++;
-                }
-            }
-        }
-        ((TableFObj) parent).flagColumnIndices(startIndex, endIndex);
-        if (endsRow() && parent.getNameId() != FO_TABLE_ROW) {
-            ((TableBody) parent).firstRow = false;
-            ((TableBody) parent).resetColumnIndex();
-        }
-    }
-    
     /**
      * @see org.apache.fop.fo.FONode#validateChildNode(Locator, String, String)
      * XSL Content Model: marker* (%block;)+
@@ -304,7 +200,7 @@ public class TableCell extends TableFObj {
      * @return the Common Border, Padding, and Background Properties.
      */
     public CommonBorderPaddingBackground getCommonBorderPaddingBackground() {
-        return commonBorderPaddingBackground;
+        return this.commonBorderPaddingBackground;
     }
 
     /**
index 9fbce18493a80b17e064607671783eff6110e229..c82eb474a691673a05d73bc3537c2e6428762ec0 100644 (file)
@@ -78,10 +78,6 @@ public class TableColumn extends TableFObj {
         visibility = pList.get(PR_VISIBILITY).getEnum();
         super.bind(pList);
         
-        if (getTable().isColumnNumberUsed(columnNumber.getValue())) {
-            throw new PropertyException("column-number \"" + columnNumber 
-                    + "\" has already been assigned to a previous column");
-        }
         if (numberColumnsRepeated.getValue() <= 0) {
             throw new PropertyException("number-columns-repeated must be 1 or bigger, "
                     + "but got " + numberColumnsRepeated.getValue());
@@ -104,10 +100,6 @@ public class TableColumn extends TableFObj {
      * @see org.apache.fop.fo.FONode#endOfNode
      */
     protected void endOfNode() throws FOPException {
-        //flag column indices used by this column
-        int startIndex = getColumnNumber() - 1;
-        int endIndex = startIndex + getNumberColumnsRepeated();
-        getTable().flagColumnIndices(startIndex, endIndex);
         getFOEventHandler().endColumn(this);
     }
 
@@ -211,6 +203,4 @@ public class TableColumn extends TableFObj {
     protected void releasePropertyList() {
         this.pList = null;
     }
-
-}
-
+}
\ No newline at end of file
index 1497f2c6edb99e490e19bf493d75695908c8f64d..d2ea03d351e34050ef40a475a40663895ab60e62 100644 (file)
 
 package org.apache.fop.fo.flow;
 
+import java.util.BitSet;
+import java.util.Iterator;
+import java.util.List;
+
 import org.apache.fop.apps.FOPException;
 import org.apache.fop.datatypes.Numeric;
 import org.apache.fop.datatypes.ValidationPercentBaseContext;
+import org.apache.fop.fo.Constants;
 import org.apache.fop.fo.FONode;
 import org.apache.fop.fo.FObj;
 import org.apache.fop.fo.PropertyList;
+import org.apache.fop.fo.ValidationException;
+import org.apache.fop.fo.expr.PropertyException;
 import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
+import org.apache.fop.fo.properties.NumberProperty;
+import org.apache.fop.fo.properties.Property;
+import org.apache.fop.fo.properties.PropertyMaker;
 
 /**
  * Superclass for table-related FOs
@@ -89,12 +99,128 @@ public abstract class TableFObj extends FObj {
         if (getNameId() != FO_TABLE //Separate check for fo:table in Table.java
                 && getNameId() != FO_TABLE_CELL
                 && getCommonBorderPaddingBackground().hasPadding(
-                        ValidationPercentBaseContext.getPseudoContextForValidationPurposes())) {
-            attributeWarning("padding-* properties are not applicable to " + getName() 
+                        ValidationPercentBaseContext
+                            .getPseudoContextForValidationPurposes())) {
+            attributeWarning(
+                    "padding-* properties are not applicable to " + getName()
                     + ", but a non-zero value for padding was found.");
         }
     }
     
+    /**
+     * @see org.apache.fop.fo.FONode#addChildNode(FONode)
+     */
+    protected void addChildNode(FONode child) throws FOPException {
+        if (!inMarker() 
+                && child.getNameId() == FO_TABLE_CELL) {
+            /* update current column index for the table-body/table-row */
+            updateColumnIndex((TableCell) child);
+        }
+        super.addChildNode(child);
+    }
+    
+    private void updateColumnIndex(TableCell cell)
+            throws ValidationException {
+        
+        int rowSpan = cell.getNumberRowsSpanned();
+        int colSpan = cell.getNumberColumnsSpanned();
+        int columnIndex = getCurrentColumnIndex();
+        
+        int i = -1;
+        while (++i < colSpan) {
+            if (isColumnNumberUsed(columnIndex + i)) {
+                /* if column-number is already in use by another cell
+                 * in the current row => error!
+                 */
+                StringBuffer errorMessage = new StringBuffer();
+                errorMessage.append("fo:table-cell overlaps in column ")
+                       .append(columnIndex + i);
+                if (locator.getLineNumber() != -1) {
+                    errorMessage.append(" (line #")
+                        .append(locator.getLineNumber()).append(", column #")
+                        .append(locator.getColumnNumber()).append(")");
+                }
+                throw new ValidationException(errorMessage.toString());
+            }
+        }
+
+        if (getNameId() == FO_TABLE_ROW) {
+            
+            TableRow row = (TableRow) this;
+            TableBody body = (TableBody) parent;
+            
+            for (i = colSpan; --i >= 0;) {
+                row.pendingSpans.add(null);
+            }
+            
+            /* if the current cell spans more than one row,
+             * update pending span list for the next row
+             */
+            if (rowSpan > 1) {
+                for (i = colSpan; --i >= 0;) {
+                    row.pendingSpans.set(columnIndex - 1 + i, 
+                            new PendingSpan(rowSpan));
+                }
+            }
+        } else {
+            
+            TableBody body = (TableBody) this;
+            
+            /* if body.firstRow is still true, and :
+             * a) the cell starts a row,
+             * b) there was a previous cell 
+             * c) that previous cell didn't explicitly end the previous row
+             *  => set firstRow flag to false
+             */
+            if (body.firstRow && cell.startsRow()) {
+                if (!body.previousCellEndedRow()) {
+                    body.firstRow = false;
+                }
+            }
+            
+            /* pendingSpans not initialized for the first row...
+             */
+            if (body.firstRow) {
+                for (i = colSpan; --i >= 0;) {
+                    body.pendingSpans.add(null);
+                }
+            }
+            
+            /* if the current cell spans more than one row,
+             * update pending span list for the next row
+             */
+            if (rowSpan > 1) {
+                for (i = colSpan; --i >= 0;) {
+                    body.pendingSpans.set(columnIndex - 1 + i, 
+                            new PendingSpan(rowSpan));
+                }
+            }
+        }
+
+        /* flag column indices used by this cell,
+         * take into account that possibly not all column-numbers
+         * are used by columns in the parent table (if any),
+         * so a cell spanning three columns, might actually
+         * take up more than three columnIndices...
+         */
+        int startIndex = columnIndex - 1;
+        int endIndex = startIndex + colSpan;
+        if (getTable().columns != null) {
+            List cols = getTable().columns;
+            int tmpIndex = endIndex;
+            for (i = startIndex; i <= tmpIndex; ++i) {
+                if (i < cols.size() && cols.get(i) == null) {
+                    endIndex++;
+                }
+            }
+        }
+        flagColumnIndices(startIndex, endIndex);
+        if (getNameId() != FO_TABLE_ROW && cell.endsRow()) {
+            ((TableBody) this).firstRow = false;
+            ((TableBody) this).resetColumnIndex();
+        }
+    }
+    
     /**
      * 
      * @param side  the side for which to return the border precedence
@@ -121,7 +247,7 @@ public abstract class TableFObj extends FObj {
      * 
      * @return the next column number to use
      */
-    public int getCurrentColumnIndex() {
+    protected int getCurrentColumnIndex() {
         return 0;
     }
     
@@ -133,10 +259,10 @@ public abstract class TableFObj extends FObj {
      * 
      * @param   newIndex    new value for column index
      */
-    public void setCurrentColumnIndex(int newIndex) {
+    protected void setCurrentColumnIndex(int newIndex) {
         //do nothing by default
     }
-    
+        
     /**
      * Checks if a certain column-number is already occupied
      * (overridden for Table, TableBody, TableRow)
@@ -184,10 +310,83 @@ public abstract class TableFObj extends FObj {
     }
     
     /**
-     * Overridden for Table, TableBody, TableRow
-     * @return true if the usedColumnIndices BitSet exists, and is initialized
+     * PropertyMaker subclass for the column-number property
+     *
      */
-    protected boolean existsUsedColumnIndices() {
-        return false;
+    public static class ColumnNumberPropertyMaker extends NumberProperty.Maker {
+
+        /**
+         * Constructor
+         * @param propId    the id of the property for which the maker should 
+         *                  be created
+         */
+        public ColumnNumberPropertyMaker(int propId) {
+            super(propId);
+        }
+
+        /**
+         * @see PropertyMaker#make(PropertyList)
+         */
+        public Property make(PropertyList propertyList) 
+                throws PropertyException {
+            FObj fo = propertyList.getFObj();
+
+            if (fo.getNameId() == Constants.FO_TABLE_CELL
+                    || fo.getNameId() == Constants.FO_TABLE_COLUMN) {
+                if (fo.getNameId() == Constants.FO_TABLE_CELL
+                        && fo.getParent().getNameId() != Constants.FO_TABLE_ROW
+                        && (propertyList.get(Constants.PR_STARTS_ROW).getEnum() 
+                                == Constants.EN_TRUE)) {
+                    TableBody parent = (TableBody) fo.getParent();
+                    if (!parent.previousCellEndedRow()) {
+                        parent.resetColumnIndex();
+                    }
+                }
+                return new NumberProperty(((TableFObj) fo.getParent())
+                                            .getCurrentColumnIndex());
+            } else {
+                throw new PropertyException(
+                        "column-number property is only allowed"
+                        + " on fo:table-cell or fo:table-column, not on "
+                        + fo.getName());
+            }
+        }
+        
+        /**
+         * Check the value of the column-number property. 
+         * Return the parent's column index (initial value) in case 
+         * of a negative or zero value
+         * 
+         * @see org.apache.fop.fo.properties.PropertyMaker#get(
+         *                      int, PropertyList, boolean, boolean)
+         */
+        public Property get(int subpropId, PropertyList propertyList,
+                            boolean tryInherit, boolean tryDefault) 
+                throws PropertyException {
+            
+            Property p = super.get(0, propertyList, tryInherit, tryDefault);
+            TableFObj fo = (TableFObj) propertyList.getFObj();
+            TableFObj parent = (TableFObj) propertyList.getParentFObj();
+            int columnIndex = p.getNumeric().getValue();
+            
+            if (columnIndex <= 0) {
+                fo.getLogger().warn("Specified negative or zero value for "
+                        + "column-number on " + fo.getName() + ": "
+                        + columnIndex + " forced to " 
+                        + parent.getCurrentColumnIndex());
+                return new NumberProperty(parent.getCurrentColumnIndex());
+            }
+            //TODO: check for non-integer value and round
+                    
+            /* if column-number was explicitly specified, force the 
+             * parent's current column index to the specified value, 
+             * so that the updated index will be the correct initial 
+             * value for the next cell/column (see Rec 7.26.8)
+             */
+            if (propertyList.getExplicit(Constants.PR_COLUMN_NUMBER) != null) {
+                parent.setCurrentColumnIndex(p.getNumeric().getValue());
+            }
+            return p;
+        }
     }
 }
index e698b1d55c9c83c7b537cb944cb1dcf83dcdc40b..82c698c0f3ed0ede2ff5f7afa3c9794bceeb57c7 100644 (file)
@@ -40,7 +40,6 @@ public class TableFooter extends TableBody {
      * @see org.apache.fop.fo.FONode#startOfNode
      */
     protected void startOfNode() throws FOPException {
-        initPendingSpans();
         //getFOEventHandler().startBody(this);
     }
 
index ac6e968713b142bbc748361b844234a9bf1a99ac..a487dd072866d203e12021d020c0ec3c85eed2d4 100644 (file)
@@ -40,7 +40,6 @@ public class TableHeader extends TableBody {
      * @see org.apache.fop.fo.FONode#startOfNode
      */
     protected void startOfNode() throws FOPException {
-        initPendingSpans();
         //getFOEventHandler().startHeader(this);
     }
 
index f93720545c752c9e492101ef088bebc4041d9378..1f0cb14ae41381f865fa86ca711feb5c0ebd3661 100644 (file)
@@ -22,6 +22,7 @@ package org.apache.fop.fo.flow;
 import java.util.BitSet;
 import java.util.List;
 
+import org.xml.sax.Attributes;
 import org.xml.sax.Locator;
 
 import org.apache.fop.apps.FOPException;
@@ -104,24 +105,30 @@ public class TableRow extends TableFObj {
         childNodes.add(cell);
     }
     
+    /**
+     * @see org.apache.fop.fo.FONode#processNode(String, Locator, 
+     *                                  Attributes, PropertyList)
+     */
+    public void processNode(String elementName, Locator locator, 
+            Attributes attlist, PropertyList pList) throws FOPException {
+        if (!inMarker()) {
+            TableBody body = (TableBody) parent;
+            body.resetColumnIndex();
+            pendingSpans = body.pendingSpans;
+            usedColumnIndices = body.usedColumnIndices;
+            while (usedColumnIndices.get(columnIndex - 1)) {
+                columnIndex++;
+            }
+        }
+        super.processNode(elementName, locator, attlist, pList);
+    }
+
     /**
      * @see org.apache.fop.fo.FONode#startOfNode
      */
     protected void startOfNode() throws FOPException {
-        pendingSpans = ((TableBody) parent).pendingSpans;
-        usedColumnIndices = ((TableBody) parent).usedColumnIndices;
-        while (usedColumnIndices.get(columnIndex - 1)) {
-            columnIndex++;
-        }
-        
         checkId(id);
         getFOEventHandler().startRow(this);
-        if (((TableBody) parent).isFirst(this) 
-                && getTable().columns == null ) {
-            if (pendingSpans == null) {
-                pendingSpans = new java.util.ArrayList();
-            }
-        }
     }
 
     /**
@@ -131,17 +138,10 @@ public class TableRow extends TableFObj {
         if (childNodes == null) {
             missingChildElementError("(table-cell+)");
         }
-        if (((TableBody) parent).isFirst(this) 
-                && getTable().columns == null ) {
-            //force parent body's pendingSpans
-            //to the one accumulated after processing this row
-            ((TableBody) parent).pendingSpans = pendingSpans;
+        if (!inMarker()) {
+            pendingSpans = null;
+            usedColumnIndices = null;
         }
-        ((TableBody) parent).resetColumnIndex();
-        columnIndex = 1;
-        //release references
-        pendingSpans = null;
-        usedColumnIndices = null;
         getFOEventHandler().endRow(this);
     }
 
@@ -155,7 +155,7 @@ public class TableRow extends TableFObj {
         if (!(FO_URI.equals(nsURI) && localName.equals("table-cell"))) {
             invalidChildError(loc, nsURI, localName);
         }
-    }
+    }    
     
     /**
      * @return the "id" property.
@@ -292,12 +292,5 @@ public class TableRow extends TableFObj {
         while (usedColumnIndices.get(columnIndex - 1)) {
             columnIndex++;
         }
-    }
-    
-    /**
-     * @see org.apache.fop.fo.flow.TableFObj#existsUsedColumnIndices()
-     */
-    protected boolean existsUsedColumnIndices() {
-        return (usedColumnIndices != null);
-    }
+    }    
 }
index 56796f0da61a7e5171779195a1fe65e0b80eb8e9..386ed694b12f494ea6c5666c02ca281785b58306 100644 (file)
@@ -539,4 +539,12 @@ public class PageSequence extends FObj {
         return this.language;
     }
 
+    /**
+     * Releases a page-sequence's children after the page-sequence has been fully processed.
+     */
+    public void releasePageSequence() {
+        this.mainFlow = null;
+        this.flowMap.clear();
+    }
+    
 }
diff --git a/src/java/org/apache/fop/fo/properties/ColumnNumberPropertyMaker.java b/src/java/org/apache/fop/fo/properties/ColumnNumberPropertyMaker.java
deleted file mode 100644 (file)
index e1f6928..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * 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.fo.properties;
-
-import java.util.Iterator;
-
-import org.apache.fop.fo.Constants;
-import org.apache.fop.fo.FObj;
-import org.apache.fop.fo.PropertyList;
-import org.apache.fop.fo.expr.PropertyException;
-import org.apache.fop.fo.flow.TableBody;
-import org.apache.fop.fo.flow.TableCell;
-import org.apache.fop.fo.flow.TableFObj;
-
-/**
- * Maker class for the column-number property on table-cells and
- * table-columns
- *
- */
-public class ColumnNumberPropertyMaker extends NumberProperty.Maker {
-
-    /**
-     * Constructor
-     * @param propId    the id of the property for which the maker should be created
-     */
-    public ColumnNumberPropertyMaker(int propId) {
-        super(propId);
-    }
-
-    /**
-     * @see PropertyMaker#make(PropertyList)
-     */
-    public Property make(PropertyList propertyList) throws PropertyException {
-        FObj fo = propertyList.getFObj();
-
-        if (fo.getNameId() == Constants.FO_TABLE_CELL
-                || fo.getNameId() == Constants.FO_TABLE_COLUMN) {
-            if (fo.getNameId() == Constants.FO_TABLE_CELL
-                    && fo.getParent().getNameId() != Constants.FO_TABLE_ROW
-                    && (propertyList.get(Constants.PR_STARTS_ROW).getEnum() 
-                            == Constants.EN_TRUE)) {
-                TableBody parent = (TableBody) fo.getParent();
-                TableCell prevCell = null;                
-                for (Iterator i = parent.getChildNodes(); 
-                        (i != null && i.hasNext());) {
-                    prevCell = (TableCell) i.next();
-                }
-                if (prevCell != null && !prevCell.endsRow()) {
-                    parent.resetColumnIndex();
-                }
-            }
-            return new NumberProperty(((TableFObj) fo.getParent())
-                                        .getCurrentColumnIndex());
-        } else {
-            throw new PropertyException("column-number property is only allowed"
-                    + " on fo:table-cell or fo:table-column, not on "
-                    + fo.getName());
-        }
-    }
-    
-    /**
-     * Check the value of the column-number property. 
-     * Return the parent's column index (initial value) in case 
-     * of a negative or zero value
-     * 
-     * @see org.apache.fop.fo.properties.PropertyMaker#get(
-     *                      int, PropertyList, boolean, boolean)
-     */
-    public Property get(int subpropId, PropertyList propertyList,
-                        boolean tryInherit, boolean tryDefault) 
-            throws PropertyException {
-        
-        Property p = super.get(0, propertyList, tryInherit, tryDefault);
-        TableFObj fo = (TableFObj) propertyList.getFObj();
-        TableFObj parent = (TableFObj) propertyList.getParentFObj();
-        int columnIndex = p.getNumeric().getValue();
-        
-        if (columnIndex <= 0) {
-            fo.getLogger().warn("Specified negative or zero value for "
-                    + "column-number on " + fo.getName() + ": "
-                    + columnIndex + " forced to " 
-                    + parent.getCurrentColumnIndex());
-            return new NumberProperty(parent.getCurrentColumnIndex());
-        }
-        //TODO: check for non-integer value and round
-                
-        //if column-number was explicitly specified, force the parent's current
-        //column index to the specified value, so that the updated index will
-        //be the correct initial value for the next cell/column (see Rec 7.26.8)
-        if (propertyList.getExplicit(Constants.PR_COLUMN_NUMBER) != null) {
-            parent.setCurrentColumnIndex(p.getNumeric().getValue());
-        }
-        return p;
-    }
-}
index e652d153db7d50760384a934db96e6c8c91b6fa0..69177ad29b68db1893b38029269a713adac39f7f 100644 (file)
@@ -53,136 +53,142 @@ public class FontShorthandProperty extends ListProperty {
         public Property make(PropertyList propertyList, 
                 String value, FObj fo) throws PropertyException {
             
-            FontShorthandProperty newProp = new FontShorthandProperty();
-            newProp.setSpecifiedValue(value);
-            
-            String specVal = value;
-            Property prop = null;
-            if ("inherit".equals(specVal)) {
-                for (int i = PROP_IDS.length; --i >= 0;) {
-                    prop = propertyList.getFromParent(PROP_IDS[i]);
-                    newProp.addProperty(prop, i);
-                }
-            } else {
-                /* initialize list with nulls */
-                for (int pos = 6; --pos >= 0;) {
-                    newProp.addProperty(null, pos);
-                }
-                prop = checkEnumValues(specVal);
-                if (prop == null) {
-                    /* not an enum:
-                     * value should consist at least of font-size and font-family
-                     * separated by a space
-                     * mind the possible spaces from quoted font-family names
-                     */
-                    int spaceIndex = value.indexOf(' ');
-                    int quoteIndex = (value.indexOf('\'') == -1)
-                        ? value.indexOf('\"') : value.indexOf('\'');
-                    if (spaceIndex == -1 
-                            || (quoteIndex != -1 && spaceIndex > quoteIndex)) {
-                        /* no spaces or first space appears after the first
-                         * single/double quote, so malformed value string
+            try {
+                FontShorthandProperty newProp = new FontShorthandProperty();
+                newProp.setSpecifiedValue(value);
+                
+                String specVal = value;
+                Property prop = null;
+                if ("inherit".equals(specVal)) {
+                    /* fill the list with the individual properties from the parent */
+                    for (int i = PROP_IDS.length; --i >= 0;) {
+                        prop = propertyList.getFromParent(PROP_IDS[i]);
+                        newProp.addProperty(prop, i);
+                    }
+                } else {
+                    /* initialize list with nulls */
+                    for (int pos = PROP_IDS.length; --pos >= 0;) {
+                        newProp.addProperty(null, pos);
+                    }
+                    prop = checkEnumValues(specVal);
+                    if (prop == null) {
+                        /* not an enum:
+                         * value should consist at least of font-size and font-family
+                         * separated by a space
+                         * mind the possible spaces from quoted font-family names
                          */
-                        throw new PropertyException("Invalid property value: "
-                                + "font=\"" + value + "\"");                        
-                    } 
-                    PropertyMaker m = null;
-                    int fromIndex = spaceIndex + 1;
-                    int toIndex = specVal.length();
-                    /* at least one space that appears before the first
-                     * single/double quote, so extract the individual properties
-                     */
-                    boolean fontFamilyParsed = false;
-                    int commaIndex = value.indexOf(',');
-                    while (!fontFamilyParsed) {
-                        /* value contains a (list of) possibly quoted 
-                         * font-family name(s) 
+                        int spaceIndex = value.indexOf(' ');
+                        int quoteIndex = (value.indexOf('\'') == -1)
+                            ? value.indexOf('\"') : value.indexOf('\'');
+                        if (spaceIndex == -1 
+                                || (quoteIndex != -1 && spaceIndex > quoteIndex)) {
+                            /* no spaces or first space appears after the first
+                             * single/double quote, so malformed value string
+                             */
+                            throw new PropertyException("Invalid property value: "
+                                    + "font=\"" + value + "\"");                        
+                        } 
+                        PropertyMaker m = null;
+                        int fromIndex = spaceIndex + 1;
+                        int toIndex = specVal.length();
+                        /* at least one space that appears before the first
+                         * single/double quote, so extract the individual properties
                          */
-                        if (commaIndex == -1) {
-                            /* no list, just a single name 
-                             * (or first name in the list)
+                        boolean fontFamilyParsed = false;
+                        int commaIndex = value.indexOf(',');
+                        while (!fontFamilyParsed) {
+                            /* value contains a (list of) possibly quoted 
+                             * font-family name(s) 
                              */
-                            if (quoteIndex != -1) {
-                                /* a single name, quoted
+                            if (commaIndex == -1) {
+                                /* no list, just a single name 
+                                 * (or first name in the list)
                                  */
-                                fromIndex = quoteIndex;
-                            }
-                            m = FObj.getPropertyMakerFor(PROP_IDS[1]);
-                            prop = m.make(propertyList, specVal.substring(fromIndex), fo);
-                            newProp.addProperty(prop, 1);
-                            fontFamilyParsed = true;                            
-                        } else {
-                            if (quoteIndex != -1 && quoteIndex < commaIndex) {
-                                /* a quoted font-family name as first name
-                                 * in the comma-separated list
-                                 * fromIndex = index of the first quote
-                                 */
-                                fromIndex = quoteIndex;
-                                quoteIndex = -1;
+                                if (quoteIndex != -1) {
+                                    /* a single name, quoted
+                                     */
+                                    fromIndex = quoteIndex;
+                                }
+                                m = FObj.getPropertyMakerFor(PROP_IDS[1]);
+                                prop = m.make(propertyList, specVal.substring(fromIndex), fo);
+                                newProp.addProperty(prop, 1);
+                                fontFamilyParsed = true;                            
                             } else {
-                                fromIndex = value.lastIndexOf(' ', commaIndex) + 1;
+                                if (quoteIndex != -1 && quoteIndex < commaIndex) {
+                                    /* a quoted font-family name as first name
+                                     * in the comma-separated list
+                                     * fromIndex = index of the first quote
+                                     */
+                                    fromIndex = quoteIndex;
+                                    quoteIndex = -1;
+                                } else {
+                                    fromIndex = value.lastIndexOf(' ', commaIndex) + 1;
+                                }
+                                commaIndex = -1;
                             }
-                            commaIndex = -1;
                         }
-                    }
-                    toIndex = fromIndex - 1;
-                    fromIndex = value.lastIndexOf(' ', toIndex - 1) + 1;
-                    value = specVal.substring(fromIndex, toIndex);
-                    int slashIndex = value.indexOf('/');
-                    String fontSize = value.substring(0, 
-                            (slashIndex == -1) ? value.length() : slashIndex);
-                    m = FObj.getPropertyMakerFor(PROP_IDS[0]);
-                    prop = m.make(propertyList, fontSize, fo);
-                    /* need to make sure subsequent call to LineHeightPropertyMaker.make()
-                     * doesn't generate the default font-size property...
-                     */
-                    propertyList.putExplicit(PROP_IDS[0], prop);
-                    newProp.addProperty(prop, 0);
-                    if (slashIndex != -1) {
-                        /* line-height */
-                        String lineHeight = value.substring(slashIndex + 1);
-                        m = FObj.getPropertyMakerFor(PROP_IDS[2]);
-                        prop = m.make(propertyList, lineHeight, fo);
-                        newProp.addProperty(prop, 2);
-                    }
-                    if (fromIndex != 0) {
                         toIndex = fromIndex - 1;
-                        value = specVal.substring(0, toIndex);
-                        fromIndex = 0;
-                        spaceIndex = value.indexOf(' ');
-                        do {
-                            toIndex = (spaceIndex == -1) ? value.length() : spaceIndex;
-                            String val = value.substring(fromIndex, toIndex);
-                            for (int i = 6; --i >= 3;) {
-                                if (newProp.list.get(i) == null) {
-                                    /* not set */
-                                    m = FObj.getPropertyMakerFor(PROP_IDS[i]);
-                                    val = m.checkValueKeywords(val);
-                                    prop = m.checkEnumValues(val);
-                                    if (prop != null) {
-                                        newProp.addProperty(prop, i);
+                        fromIndex = value.lastIndexOf(' ', toIndex - 1) + 1;
+                        value = specVal.substring(fromIndex, toIndex);
+                        int slashIndex = value.indexOf('/');
+                        String fontSize = value.substring(0, 
+                                (slashIndex == -1) ? value.length() : slashIndex);
+                        m = FObj.getPropertyMakerFor(PROP_IDS[0]);
+                        prop = m.make(propertyList, fontSize, fo);
+                        /* need to make sure subsequent call to LineHeightPropertyMaker.make()
+                         * doesn't generate the default font-size property...
+                         */
+                        propertyList.putExplicit(PROP_IDS[0], prop);
+                        newProp.addProperty(prop, 0);
+                        if (slashIndex != -1) {
+                            /* line-height */
+                            String lineHeight = value.substring(slashIndex + 1);
+                            m = FObj.getPropertyMakerFor(PROP_IDS[2]);
+                            prop = m.make(propertyList, lineHeight, fo);
+                            newProp.addProperty(prop, 2);
+                        }
+                        if (fromIndex != 0) {
+                            toIndex = fromIndex - 1;
+                            value = specVal.substring(0, toIndex);
+                            fromIndex = 0;
+                            spaceIndex = value.indexOf(' ');
+                            do {
+                                toIndex = (spaceIndex == -1) ? value.length() : spaceIndex;
+                                String val = value.substring(fromIndex, toIndex);
+                                for (int i = 6; --i >= 3;) {
+                                    if (newProp.list.get(i) == null) {
+                                        /* not set */
+                                        m = FObj.getPropertyMakerFor(PROP_IDS[i]);
+                                        val = m.checkValueKeywords(val);
+                                        prop = m.checkEnumValues(val);
+                                        if (prop != null) {
+                                            newProp.addProperty(prop, i);
+                                        }
                                     }
                                 }
-                            }
-                            fromIndex = toIndex + 1;
-                            spaceIndex = value.indexOf(' ', fromIndex);
-                        } while (toIndex != value.length());
+                                fromIndex = toIndex + 1;
+                                spaceIndex = value.indexOf(' ', fromIndex);
+                            } while (toIndex != value.length());
+                        }
+                    } else {
+                        //TODO: implement enum values
+                        log.warn("Enum values other than \"inherit\""
+                                + " not yet supported for the font shorthand.");
+                        return null;
                     }
-                } else {
-                    //TODO: implement enum values
-                    log.warn("Enum values other than \"inherit\""
-                            + " not yet supported for the font shorthand.");
-                    return null;
                 }
-            }
-            if (newProp.list.get(0) == null || newProp.list.get(1) == null) {
-                throw new PropertyException("Invalid property value: "
-                        + "font-size and font-family are required for the font shorthand"
-                        + "\nfont=" + value);
-            }
-            return newProp;
+                if (newProp.list.get(0) == null || newProp.list.get(1) == null) {
+                    throw new PropertyException("Invalid property value: "
+                            + "font-size and font-family are required for the font shorthand"
+                            + "\nfont=\"" + value + "\"");
+                }
+                return newProp;
+           } catch (PropertyException pe) {
+               pe.setLocator(propertyList.getFObj().getLocator());
+               pe.setPropertyName(getName());
+               throw pe;
+           }
         }
-
     }
     
     private void addProperty(Property prop, int pos) {
index fffcf9c621a91f9c0efc4103c148997cad3bac60..4c9e3b768c0d483f812f227a2b08ba004baa5d9c 100644 (file)
@@ -46,6 +46,9 @@ import org.apache.avalon.framework.configuration.ConfigurationException;
 // Java
 import java.util.List;
 
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+
 /**
  * Default fonts for FOP application; currently this uses PDF's fonts
  * by default.
@@ -194,6 +197,11 @@ public class FontSetup {
             return; //No fonts to process
         }
 
+        if (resolver == null) {
+            //Ensure that we have minimal font resolution capabilities
+            resolver = createMinimalFontResolver();
+        }
+        
         String internalName = null;
         //FontReader reader = null;
 
@@ -229,6 +237,18 @@ public class FontSetup {
         }
     }
 
+    /** @return a new FontResolver to be used by the font subsystem */
+    private static FontResolver createMinimalFontResolver() {
+        return new FontResolver() {
+
+            /** @see org.apache.fop.fonts.FontResolver#resolve(java.lang.String) */
+            public Source resolve(String href) {
+                //Minimal functionality here
+                return new StreamSource(href);
+            }
+            
+        };
+    }
     /**
      * Builds a list of EmbedFontInfo objects for use with the setup() method.
      * @param cfg Configuration object
index 9e51715c6194e81b434610cd5a478e064b496115..651392e87338bab924821eeb433061df647eccd3 100644 (file)
@@ -202,6 +202,15 @@ public abstract class AbstractBreaker {
         return null;
     }
     
+    /**
+     * Returns a PageBreakingLayoutListener for the PageBreakingAlgorithm to notify about layout
+     * problems.
+     * @return the listener instance or null if no notifications are needed
+     */
+    protected PageBreakingAlgorithm.PageBreakingLayoutListener getLayoutListener() {
+        return null;
+    }
+    
     /*
      * This method is to contain the logic to determine the LM's
      * getNextKnuthElements() implementation(s) that are to be called. 
@@ -316,7 +325,7 @@ public abstract class AbstractBreaker {
                 log.debug("PLM> start of algorithm (" + this.getClass().getName() 
                         + "), flow BPD =" + flowBPD);
                 PageBreakingAlgorithm alg = new PageBreakingAlgorithm(getTopLevelLM(),
-                        getPageProvider(),
+                        getPageProvider(), getLayoutListener(),
                         alignment, alignmentLast, footnoteSeparatorLength, floatSeparatorLength,
                         isPartOverflowRecoveryActivated(), autoHeight, isSinglePartFavored());
                 int iOptPageCount;
index b82a6a6c45762d1da696504de5696555be1d7eca..632a3781dc30dd1a8016236ee6227dc01d643ed7 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.fop.layoutmgr;
 \r
 import org.apache.commons.logging.Log;\r
 import org.apache.commons.logging.LogFactory;\r
+import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener;\r
 import org.apache.fop.traits.MinOptMax;\r
 \r
 /**\r
@@ -37,12 +38,14 @@ public class BalancingColumnBreakingAlgorithm extends PageBreakingAlgorithm {
     \r
     public BalancingColumnBreakingAlgorithm(LayoutManager topLevelLM,\r
             PageSequenceLayoutManager.PageProvider pageProvider,\r
+            PageBreakingLayoutListener layoutListener,\r
             int alignment, int alignmentLast,\r
             MinOptMax footnoteSeparatorLength,\r
             MinOptMax floatSeparatorLength,\r
             boolean partOverflowRecovery,\r
             int columnCount) {\r
-        super(topLevelLM, pageProvider, alignment, alignmentLast, \r
+        super(topLevelLM, pageProvider, layoutListener,\r
+                alignment, alignmentLast, \r
                 footnoteSeparatorLength, floatSeparatorLength, partOverflowRecovery, false, false);\r
         this.columnCount = columnCount;\r
         this.considerTooShort = true; //This is important!\r
index 70104a787b690727a303a242b4d735da53a6a0fe..7d6c778368e5d71be399e01950f4142ff643c494 100644 (file)
@@ -50,7 +50,7 @@ public abstract class BreakingAlgorithm {
     /** Maximum adjustment ration */
     protected static final int INFINITE_RATIO = 1000;
 
-    private static final int MAX_RECOVERY_ATTEMPTS = 50;
+    private static final int MAX_RECOVERY_ATTEMPTS = 5;
 
     // constants identifying a subset of the feasible breaks
     /** All feasible breaks are ok. */
@@ -76,7 +76,7 @@ public abstract class BreakingAlgorithm {
      * The threshold for considering breaks to be acceptable. The adjustment ratio must be
      * inferior to this threshold.
      */
-    private double threshold;
+    protected double threshold;
 
     /**
      * The paragraph of KnuthElements.
@@ -91,22 +91,22 @@ public abstract class BreakingAlgorithm {
     /** Force the algorithm to find a set of breakpoints, even if no feasible breakpoints
      * exist.
      */
-    private boolean force =  false;
+    boolean force =  false;
     /** If set to true, doesn't ignore break possibilities which are definitely too short. */
     protected boolean considerTooShort = false;
 
     /** When in forced mode, the best node leading to a too long line. The line will be
      * too long anyway, but this one will lead to a paragraph with fewest demerits.
      */
-    private KnuthNode lastTooLong;
+    protected KnuthNode lastTooLong;
     /** When in forced mode, the best node leading to a too short line. The line will be
      * too short anyway, but this one will lead to a paragraph with fewest demerits.
      */
-    private KnuthNode lastTooShort;
+    protected KnuthNode lastTooShort;
     /** The node to be reactivated if no set of feasible breakpoints can be found for this
      * paragraph.
      */
-    private KnuthNode lastDeactivated;
+    protected KnuthNode lastDeactivated;
 
     /** Alignment of the paragraph/page. One of EN_START, EN_JUSTIFY, etc. */
     protected int alignment;
@@ -152,10 +152,13 @@ public abstract class BreakingAlgorithm {
      */
     protected int totalShrink = 0;
 
+    protected SumsAfter sumsAfter = new SumsAfter();
+
     protected BestRecords best;
 
     /** @see #isPartOverflowRecoveryActivated() */
     private boolean partOverflowRecoveryActivated = true;
+    private KnuthNode lastRecovered;
 
     /**
      * Create a new instance.
@@ -175,15 +178,62 @@ public abstract class BreakingAlgorithm {
         alignmentLast = alignLast;
         bFirst = first;
         this.partOverflowRecoveryActivated = partOverflowRecovery;
-        this.best = new BestRecords();
+        this.best = new BestRecords(log);
         maxFlaggedPenaltiesCount = maxFlagCount;
     }
 
 
+    protected static class SumsAfter {
+        private KnuthSequence par;
+        private int widthAfter;
+        private int stretchAfter;
+        private int shrinkAfter;
+        private int currentElementIndex = -1;
+
+        void compute(int elementIndex, int totalWidth, int totalStretch, int totalShrink) {
+            if (currentElementIndex == elementIndex) {
+                return;
+            }
+            currentElementIndex = elementIndex;
+            widthAfter = totalWidth;
+            stretchAfter = totalStretch;
+            shrinkAfter = totalShrink;
+            for (int i = elementIndex; i < par.size(); i++) {
+                KnuthElement element = (KnuthElement) par.get(i);
+                if (element.isBox()) {
+                    break;
+                } else if (element.isGlue()) {
+                    widthAfter += element.getW();
+                    stretchAfter += element.getY();
+                    shrinkAfter += element.getZ();
+                } else if (element.isForcedBreak() && i != elementIndex) {
+                    break;
+                }
+            }
+        }
+
+        void initialize(KnuthSequence par) {
+            this.par = par;
+            currentElementIndex = -1;
+        }
+
+        int getWidthAfter() {
+            return widthAfter;
+        }
+
+        int getStretchAfter() {
+            return stretchAfter;
+        }
+
+        int getShrinkAfter() {
+            return shrinkAfter;
+        }
+    }
+
     /**
      * Class recording all the informations of a feasible breaking point.
      */
-    public class KnuthNode {
+    public static class KnuthNode {
         /** index of the breakpoint represented by this node */
         public int position;
 
@@ -258,10 +308,11 @@ public abstract class BreakingAlgorithm {
     /** Class that stores, for each fitness class, the best active node that could start
      * a line of the corresponding fitness ending at the current element.
      */
-    protected class BestRecords {
+    protected static class BestRecords {
         private static final double INFINITE_DEMERITS = Double.POSITIVE_INFINITY;
         //private static final double INFINITE_DEMERITS = 1E11;
 
+        private Log log;
         private double[] bestDemerits = new double[4];
         private KnuthNode[] bestNode = new KnuthNode[4];
         private double[] bestAdjust = new double[4];
@@ -271,7 +322,8 @@ public abstract class BreakingAlgorithm {
         /** Points to the fitness class which currently leads to the best demerits. */
         private int bestIndex = -1;
 
-        public BestRecords() {
+        public BestRecords(Log log) {
+            this.log = log;
             reset();
         }
 
@@ -446,7 +498,7 @@ public abstract class BreakingAlgorithm {
 
         // create an active node representing the starting point
         activeLines = new KnuthNode[20];
-        addNode(0, createNode(firstBoxIndex, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, null));
+        registerActiveNode(createNode(firstBoxIndex, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, null));
 
         if (log.isTraceEnabled()) {
             log.trace("Looping over " + (par.size() - startIndex) + " elements");
@@ -495,6 +547,16 @@ public abstract class BreakingAlgorithm {
                 }
                 if (lastTooShort == null || lastForced.position == lastTooShort.position) {
                     if (isPartOverflowRecoveryActivated()) {
+                        if (this.lastRecovered == null) {
+                            this.lastRecovered = lastTooLong;
+                            if (log.isDebugEnabled()) {
+                                log.debug("Recovery point: " + lastRecovered);
+                            }
+                        }
+                        if (lastTooLong == null) {
+                            log.debug("lastTooLong null! lastTooShort: "
+                                    + new Boolean(lastTooShort != null));
+                        }
                         // content would overflow, insert empty line/page and try again
                         KnuthNode node = createNode(
                                 lastTooLong.previous.position, lastTooLong.previous.line + 1, 1,
@@ -503,23 +565,34 @@ public abstract class BreakingAlgorithm {
                                 0, 0, lastTooLong.previous);
                         lastForced = node;
                         node.fitRecoveryCounter = lastTooLong.previous.fitRecoveryCounter + 1;
-                        log.debug("first part doesn't fit into line, recovering: " 
-                                + node.fitRecoveryCounter);
+                        if (log.isDebugEnabled()) {
+                            log.debug("first part doesn't fit into line, recovering: " 
+                                    + node.fitRecoveryCounter);
+                        }
                         if (node.fitRecoveryCounter > getMaxRecoveryAttempts()) {
-                            FONode contextFO = findContextFO(par, node.position + 1);
-                            throw new RuntimeException(FONode.decorateWithContextInfo(
-                                    "Some content could not fit "
-                                    + "into a line/page after " + getMaxRecoveryAttempts() 
-                                    + " attempts. Giving up to avoid an endless loop.", contextFO));
+                            while (lastForced.fitRecoveryCounter > 0) {
+                                lastForced = lastForced.previous;
+                                lastDeactivated = lastForced.previous;
+                                startLine--;
+                                endLine--;
+                            }
+                            lastForced = this.lastRecovered;
+                            this.lastRecovered = null;
+                            startLine = lastForced.line;
+                            endLine = lastForced.line;
+                            log.debug("rolled back...");
                         }
                     } else {
                         lastForced = lastTooLong;
                     }
                 } else {
                     lastForced = lastTooShort;
+                    this.lastRecovered = null;
                 }
 
-                log.debug("Restarting at node " + lastForced);
+                if (log.isDebugEnabled()) {
+                    log.debug("Restarting at node " + lastForced);
+                }
                 i = restartFrom(lastForced, i);
             }
         }
@@ -577,6 +650,7 @@ public abstract class BreakingAlgorithm {
         this.totalWidth = 0;
         this.totalStretch = 0;
         this.totalShrink = 0;
+        sumsAfter.initialize(par);
     }
 
     /** Creates a new active node for a feasible breakpoint at the given position. Only
@@ -626,7 +700,7 @@ public abstract class BreakingAlgorithm {
 
     protected int restartFrom(KnuthNode restartingNode, int currentIndex) {
         restartingNode.totalDemerits = 0;
-        addNode(restartingNode.line, restartingNode);
+        registerActiveNode(restartingNode);
         startLine = restartingNode.line;
         endLine = startLine + 1;
         totalWidth = restartingNode.totalWidth;
@@ -682,7 +756,7 @@ public abstract class BreakingAlgorithm {
                     if (log.isTraceEnabled()) {
                         log.trace("Removing " + node);
                     }
-                    removeNode(line, node);
+                    deactivateNode(node);
                     lastDeactivated = compareNodes(lastDeactivated, node);
                 }
     
@@ -709,33 +783,36 @@ public abstract class BreakingAlgorithm {
                 if (force && (r <= -1 || r > threshold)) {
                     int fitnessClass = computeFitness(r);
                     double demerits = computeDemerits(node, element, fitnessClass, r);
-                    int newWidth = totalWidth;
-                    int newStretch = totalStretch;
-                    int newShrink = totalShrink;
-
-                    // add the width, stretch and shrink of glue elements after 
-                    // the break
-                    // this does not affect the dimension of the line / page, only
-                    // the values stored in the node; these would be as if the break
-                    // was just before the next box element, thus ignoring glues and
-                    // penalties between the "real" break and the following box
-                    for (int i = elementIdx; i < par.size(); i++) {
-                        KnuthElement tempElement = getElement(i);
-                        if (tempElement.isBox()) {
-                            break;
-                        } else if (tempElement.isGlue()) {
-                            newWidth += tempElement.getW();
-                            newStretch += tempElement.getY();
-                            newShrink += tempElement.getZ();
-                        } else if (tempElement.isForcedBreak() && i != elementIdx) {
-                            break;
-                        }
-                    }
+                    sumsAfter.compute(elementIdx, totalWidth, totalStretch, totalShrink);
+//                    int newWidth = totalWidth;
+//                    int newStretch = totalStretch;
+//                    int newShrink = totalShrink;
+//
+//                    // add the width, stretch and shrink of glue elements after 
+//                    // the break
+//                    // this does not affect the dimension of the line / page, only
+//                    // the values stored in the node; these would be as if the break
+//                    // was just before the next box element, thus ignoring glues and
+//                    // penalties between the "real" break and the following box
+//                    for (int i = elementIdx; i < par.size(); i++) {
+//                        KnuthElement tempElement = getElement(i);
+//                        if (tempElement.isBox()) {
+//                            break;
+//                        } else if (tempElement.isGlue()) {
+//                            newWidth += tempElement.getW();
+//                            newStretch += tempElement.getY();
+//                            newShrink += tempElement.getZ();
+//                        } else if (tempElement.isForcedBreak() && i != elementIdx) {
+//                            break;
+//                        }
+//                    }
 
                     if (r <= -1) {
                         if (lastTooLong == null || demerits < lastTooLong.totalDemerits) {
                             lastTooLong = createNode(elementIdx, line + 1, fitnessClass,
-                                    newWidth, newStretch, newShrink,
+                                    sumsAfter.getWidthAfter(),
+                                    sumsAfter.getStretchAfter(),
+                                    sumsAfter.getShrinkAfter(),
                                     r, availableShrink, availableStretch,
                                     difference, demerits, node);
                             if (log.isTraceEnabled()) {
@@ -751,7 +828,9 @@ public abstract class BreakingAlgorithm {
                                         difference, fitnessClass);
                             }
                             lastTooShort = createNode(elementIdx, line + 1, fitnessClass,
-                                    newWidth, newStretch, newShrink,
+                                    sumsAfter.getWidthAfter(),
+                                    sumsAfter.getStretchAfter(),
+                                    sumsAfter.getShrinkAfter(),
                                     r, availableShrink, availableStretch,
                                     difference, demerits, node);
                             if (log.isTraceEnabled()) {
@@ -770,33 +849,34 @@ public abstract class BreakingAlgorithm {
      * @param line number of the previous line; this element will end line number (line+1)
      * @param elementIdx the element's index
      */
-    private void addBreaks(int line, int elementIdx) {
+    void addBreaks(int line, int elementIdx) {
         if (!best.hasRecords()) {
             return;
         }
 
-        int newWidth = totalWidth;
-        int newStretch = totalStretch;
-        int newShrink = totalShrink;
-
-        // add the width, stretch and shrink of glue elements after 
-        // the break
-        // this does not affect the dimension of the line / page, only
-        // the values stored in the node; these would be as if the break
-        // was just before the next box element, thus ignoring glues and
-        // penalties between the "real" break and the following box
-        for (int i = elementIdx; i < par.size(); i++) {
-            KnuthElement tempElement = getElement(i);
-            if (tempElement.isBox()) {
-                break;
-            } else if (tempElement.isGlue()) {
-                newWidth += tempElement.getW();
-                newStretch += tempElement.getY();
-                newShrink += tempElement.getZ();
-            } else if (tempElement.isForcedBreak() && i != elementIdx) {
-                break;
-            }
-        }
+        sumsAfter.compute(elementIdx, totalWidth, totalStretch, totalShrink);
+//        int newWidth = totalWidth;
+//        int newStretch = totalStretch;
+//        int newShrink = totalShrink;
+//
+//        // add the width, stretch and shrink of glue elements after 
+//        // the break
+//        // this does not affect the dimension of the line / page, only
+//        // the values stored in the node; these would be as if the break
+//        // was just before the next box element, thus ignoring glues and
+//        // penalties between the "real" break and the following box
+//        for (int i = elementIdx; i < par.size(); i++) {
+//            KnuthElement tempElement = getElement(i);
+//            if (tempElement.isBox()) {
+//                break;
+//            } else if (tempElement.isGlue()) {
+//                newWidth += tempElement.getW();
+//                newStretch += tempElement.getY();
+//                newShrink += tempElement.getZ();
+//            } else if (tempElement.isForcedBreak() && i != elementIdx) {
+//                break;
+//            }
+//        }
 
         // add nodes to the active nodes list
         double minimumDemerits = best.getMinDemerits() + incompatibleFitnessDemerit;
@@ -809,8 +889,10 @@ public abstract class BreakingAlgorithm {
                             + " from fitness class " + i);
                 }
                 KnuthNode newNode = createNode(elementIdx, line + 1, i,
-                                               newWidth, newStretch, newShrink);
-                addNode(line + 1, newNode);
+                        sumsAfter.getWidthAfter(),
+                        sumsAfter.getStretchAfter(),
+                        sumsAfter.getShrinkAfter());
+                registerActiveNode(newNode);
             }
         }
         best.reset();
@@ -878,7 +960,7 @@ public abstract class BreakingAlgorithm {
      * @param r
      * @return the fitness class
      */
-    private int computeFitness(double r) {
+    int computeFitness(double r) {
         if (r < -0.5) {
             return 0;
         } else if (r <= 0.5) {
@@ -987,13 +1069,12 @@ public abstract class BreakingAlgorithm {
     }
 
     /**
-     * Add a node at the end of the given line's existing active nodes.
-     * If this is the first node in the line, adjust endLine accordingly.
-     * @param line number of the line ending at the node's corresponding breakpoint
-     * @param node the active node to add
+     * Registers a new active node. If this is the first node in the corresponding line,
+     * adjust endLine accordingly.
+     * @param node the active node to register
      */
-    public void addNode(int line, KnuthNode node) {
-        int headIdx = line * 2;
+    public void registerActiveNode(KnuthNode node) {
+        int headIdx = node.line * 2;
         if (headIdx >= activeLines.length) {
             KnuthNode[] oldList = activeLines;
             activeLines = new KnuthNode[headIdx + headIdx];
@@ -1004,21 +1085,20 @@ public abstract class BreakingAlgorithm {
             activeLines[headIdx + 1].next = node;
         } else {
             activeLines[headIdx] = node;
-            endLine = line + 1;
+            endLine = node.line + 1;
         }
         activeLines[headIdx + 1] = node;
         activeNodeCount++;
     }
 
     /**
-     * Remove the given active node registered for the given line. If there are no more active nodes
-     * for this line, adjust the startLine accordingly.
-     * @param line number of the line ending at the node's corresponding breakpoint
+     * Deactivates the given node. If there are no more active nodes for the corresponding
+     * line, adjust the startLine accordingly.
      * @param node the node to deactivate
      */
-    public void removeNode(int line, KnuthNode node) {
-        int headIdx = line * 2;
-        KnuthNode n = getNode(line);
+    public void deactivateNode(KnuthNode node) {
+        int headIdx = node.line * 2;
+        KnuthNode n = getNode(node.line);
         if (n != node) {
             // nodes could be rightly deactivated in a different order
             KnuthNode prevNode = null;
index 30d30f4a941bb124a178c404d30ccc3c36a7e25f..1aa165b8ec7d31ee252807e69d4cf78c9fd81771 100644 (file)
 
 package org.apache.fop.layoutmgr;
 
+import java.text.NumberFormat;
+import java.util.Iterator;
 import java.util.LinkedList;
+import java.util.Locale;
+import java.util.Stack;
+import java.util.Vector;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -27,35 +32,97 @@ 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.layoutmgr.breaking.BeforeFloatsRecord;
+import org.apache.fop.layoutmgr.breaking.ElasticLength;
+import org.apache.fop.layoutmgr.breaking.FootnotesRecord;
+import org.apache.fop.layoutmgr.breaking.BeforeFloatsRecord.BeforeFloatsProgress;
+import org.apache.fop.layoutmgr.breaking.FootnotesRecord.FootnotesProgress;
 
 import org.apache.fop.traits.MinOptMax;
 
 public class PageBreakingAlgorithm extends BreakingAlgorithm {
 
+    /* TODO vh: all of the following parameters should be available through a config
+     * option
+     */
+    /**
+     * Minimum allowed fill ratio for pages. Underfull pages which are filled at
+     * least this ratio are considered to be feasible breaks.
+     */
+    /* Set to 1.0 for now, otherwise this breaks testcases. */
+    public static final double MIN_NORMAL_PAGE_FILL_RATIO = 1.0;
+
+    /**
+     * Minimum allowed fill ratio for float-only pages. Underfull pages which are filled
+     * at least this ratio are considered to be feasible breaks.
+     */
+    public static final double MIN_FLOAT_PAGE_FILL_RATIO = 1.0;
+
+    /**
+     * Minimum acceptable ratio of normal content on pages containing both normal text and
+     * out-of-lines.
+     */
+    public static final double TEXT_FRACTION = 0.05;
+
+    /**
+     * Are float-only pages allowed?
+     */
+    public static final boolean FLOAT_PAGES_ALLOWED = true;
+
+    /**
+     * Are footnotes allowed on float-only pages?
+     */
+    public static final boolean FOOTNOTES_ALLOWED_ON_FLOAT_PAGES = true;
+
+    /**
+     * Are footnotes-only pages allowed?
+     */
+    public static final boolean FOOTNOTES_ONLY_PAGES_ALLOWED = true;
+
+    /**
+     * Additional demerits for an underfull page, which however has an acceptable fill ratio.
+     */
+    private static final double UNDERFULL_PAGE_DEMERITS = 20000;
+
+    /**
+     * This mode is chosen when out-of-lines must be typeset on a page containing normal
+     * content.
+     */
+    public static final int NORMAL_MODE = 0;
+
+    /**
+     * This mode is chosen when out-of-lines must be typeset on a float-only page.
+     */
+    public static final int FLOAT_PAGE_MODE = 1;
+
+    /**
+     * This mode is chosen when out-of-lines must be typeset on a float-only page at the
+     * end of a page-sequence.
+     */
+    public static final int FLUSH_MODE = 2;
+
     /** the logger for the class */
     protected static Log classLog = LogFactory.getLog(PageBreakingAlgorithm.class);
 
     private LayoutManager topLevelLM;
     private PageSequenceLayoutManager.PageProvider pageProvider;
+    private PageBreakingLayoutListener layoutListener;
     /** List of PageBreakPosition elements. */
     private LinkedList pageBreaks = null;
 
-    private OutOfLineRecord footnotes;
-    private OutOfLineRecord floats;
+    private NormalContentProgressInfo normalContentProgress = new NormalContentProgressInfo();
+    private FootnotesRecord footnotesRecord;
+    private BeforeFloatsRecord beforeFloatsRecord;
+    private FootnotesRecord.FootnotesProgress footnotesProgress;
+    private BeforeFloatsRecord.BeforeFloatsProgress beforeFloatsProgress;
 
+    private ActiveNodeRecorder activeNodeRecorder = new ActiveNodeRecorder();
+    
     // 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 int deferredFloatDemerits = 10000;
-
-    // the method noBreakBetween(int, int) uses these variables 
-    // to store parameters and result of the last call, in order
-    // to reuse them and take less time
-    private int storedPrevBreakIndex = -1;
-    private int storedBreakIndex = -1;
-    private boolean storedValue = false;
+    private int deferredFloatDemerits = 2000;
 
     //Controls whether overflows should be warned about or not
     private boolean autoHeight = false;
@@ -65,6 +132,7 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm {
     
     public PageBreakingAlgorithm(LayoutManager topLevelLM,
                                  PageSequenceLayoutManager.PageProvider pageProvider,
+                                 PageBreakingLayoutListener layoutListener,
                                  int alignment, int alignmentLast,
                                  MinOptMax footnoteSeparatorLength, MinOptMax floatSeparatorLength,
                                  boolean partOverflowRecovery, boolean autoHeight,
@@ -73,9 +141,12 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm {
         this.log = classLog;
         this.topLevelLM = topLevelLM;
         this.pageProvider = pageProvider;
-        best = new BestPageRecords();
-        footnotes = new OutOfLineRecord((MinOptMax) footnoteSeparatorLength.clone());
-        floats = new OutOfLineRecord((MinOptMax) floatSeparatorLength.clone());
+        this.layoutListener = layoutListener;
+        best = new BestPageRecords(log);
+        footnotesRecord = new FootnotesRecord(footnoteSeparatorLength);
+        beforeFloatsRecord = new BeforeFloatsRecord(floatSeparatorLength);
+        footnotesProgress = footnotesRecord.new FootnotesProgress();
+        beforeFloatsProgress = beforeFloatsRecord.new BeforeFloatsProgress();
         // add some stretch, to avoid a restart for every page containing footnotes
         if (footnoteSeparatorLength.min == footnoteSeparatorLength.max) {
             footnoteSeparatorLength.max += 10000;
@@ -90,21 +161,21 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm {
      */
     public class KnuthPageNode extends KnuthNode {
 
-        public OutOfLineRecord.ProgressInfo footnotesProgress;
-        public OutOfLineRecord.ProgressInfo floatsProgress;
+        public FootnotesRecord.FootnotesProgress footnotesProgress;
+        public BeforeFloatsRecord.BeforeFloatsProgress beforeFloatsProgress;
 
         public KnuthPageNode(int position, int line, int fitness,
                              int totalWidth, int totalStretch, int totalShrink,
-                             OutOfLineRecord.ProgressInfo footnotesProgress,
-                             OutOfLineRecord.ProgressInfo floatsProgress,
+                             FootnotesRecord.FootnotesProgress footnotesProgress,
+                             BeforeFloatsRecord.BeforeFloatsProgress 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.footnotesProgress = footnotesProgress.copy();
-            this.floatsProgress = floatsProgress.copy();
+            this.footnotesProgress = footnotesRecord.new FootnotesProgress(footnotesProgress);
+            this.beforeFloatsProgress = beforeFloatsRecord.new BeforeFloatsProgress(floatsProgress);
         }
 
     }
@@ -115,46 +186,469 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm {
      */
     protected class BestPageRecords extends BestRecords {
 
-        private OutOfLineRecord.ProgressInfo[] bestFootnotesProgress
-                = new OutOfLineRecord.ProgressInfo[4];
-        private OutOfLineRecord.ProgressInfo[] bestFloatsProgress
-                = new OutOfLineRecord.ProgressInfo[4];
-        
+        private FootnotesRecord.FootnotesProgress[] bestFootnotesProgress
+                = new FootnotesRecord.FootnotesProgress[4];
+        private BeforeFloatsRecord.BeforeFloatsProgress[] bestFloatsProgress
+                = new BeforeFloatsRecord.BeforeFloatsProgress[4];
+
+        public BestPageRecords(Log log) {
+            super(log);
+        }
+
         public void addRecord(double demerits, KnuthNode node, double adjust,
                               int availableShrink, int availableStretch,
-                              int difference, int fitness) {
+                              int difference, int fitness,
+                              FootnotesRecord.FootnotesProgress footnotesProgress,
+                              BeforeFloatsRecord.BeforeFloatsProgress beforeFloatsProgress) {
             super.addRecord(demerits, node, adjust,
                             availableShrink, availableStretch,
                             difference, fitness);
-            bestFootnotesProgress[fitness] = footnotes.getProgress().copy();
-            bestFloatsProgress[fitness] = floats.getProgress().copy();
+            bestFootnotesProgress[fitness]
+                    = footnotesRecord.new FootnotesProgress(footnotesProgress);
+            bestFloatsProgress[fitness]
+                    = beforeFloatsRecord.new BeforeFloatsProgress(beforeFloatsProgress);
         }
 
-        public int getFootnotesLength(int fitness) {
-            return bestFootnotesProgress[fitness].getAlreadyInsertedLength();
+        public FootnotesRecord.FootnotesProgress getFootnoteProgress(int fitness) {
+            return bestFootnotesProgress[fitness];
         }
 
-        public int getFootnoteListIndex(int fitness) {
-            return bestFootnotesProgress[fitness].getLastInsertedIndex();
+        public BeforeFloatsRecord.BeforeFloatsProgress getFloatProgress(int fitness) {
+            return bestFloatsProgress[fitness];
+        }
+    }
+
+    /**
+     * This class records information about the amount of normal content that has been
+     * handled so far.
+     */
+    public class NormalContentProgressInfo {
+
+        /**
+         * Position in the Knuth sequence.
+         */
+        int position;
+
+        /**
+         * Cumulative lengths of normal content inserted so far. This corresponds to the
+         * totalWidth, totalStretch, totalShrink described in Knuth's algorithm.
+         */
+        ElasticLength insertedDims = new ElasticLength();
+
+        /**
+         * Initializes this record to handle the given Knuth sequence, such that no
+         * content has been inserted yet.
+         *
+         * @param par the sequence of normal content that will have to be typeset
+         */
+        void initialize(KnuthSequence par) {
+            insertedDims.reset();
         }
 
-        public int getFootnoteElementIndex(int fitness) {
-            return bestFootnotesProgress[fitness].getLastElementOfLastInsertedIndex();
+        public String toString() {
+            return "Position: " + position + "; inserted: " + insertedDims;
         }
+    }
 
-        public OutOfLineRecord.ProgressInfo getFootnoteProgress(int fitness) {
-            return bestFootnotesProgress[fitness];
+    /**
+     * Tests candidate nodes to determine whether they are feasible, and if so records
+     * them.
+     */
+    public class ActiveNodeRecorder {
+
+        /** Adjustment ratio for the currently tested page. */
+        private double adjustmentRatio;
+
+        /** Fill ratio of the currently tested page. */
+        private double fillRatio;
+
+        private int fitnessClass;
+
+        /**
+         * Difference between the physical page's BPD and the BPD of the page's content.
+         */
+        private int difference;
+
+        /** Used to record feasible breaks in flush mode. */
+        private LinkedList queue;
+
+        /**
+         * <code>true</code> if a layout must be found, even if there is no feasible
+         * break. This usually consists of selecting a too-short or too-long node.
+         */
+        private boolean force;
+
+        /**
+         * Sets the behavior of the algorithm when no feasible break is found.
+         *
+         * @param force if <code>true</code>, a too-short or too-long node must be chosen
+         * as a feasible break; otherwise no node is created.
+         */
+        void setForce(boolean force) {
+            this.force = force;
         }
 
-        public OutOfLineRecord.ProgressInfo getFloatProgress(int fitness) {
-            return bestFloatsProgress[fitness];
+        /**
+         * Computes the adjustment ratio for the current page.
+         *
+         * @param totalLength total amount of content on the page (including floats and
+         * footnotes)
+         * @param pageBPD available space on the page
+         * @param minFillRatio minimum acceptable fill ratio for the page. If the content
+         * must be too much stretched to fill the page, it is allowed to stretch less
+         * provided the resulting fill ratio is superior or equal to this ratio
+         */
+        void computeAdjustmentRatio(ElasticLength totalLength, int pageBPD, double minFillRatio) {
+            difference = pageBPD - totalLength.getLength();
+            fillRatio = 1.0;
+            if (difference > 0) {  // too short
+                double totalStretch = totalLength.getStretch();
+                if (totalStretch <= 0) {
+                    fillRatio = ((double) totalLength.getLength()) / pageBPD;
+                    if (fillRatio >= minFillRatio) {
+                        adjustmentRatio = 0;
+                    } else {
+                        adjustmentRatio = INFINITE_RATIO;
+                    }
+                } else {
+                    adjustmentRatio = difference / totalStretch;
+                    if (adjustmentRatio > threshold) {
+                        fillRatio = (totalLength.getLength() + totalStretch * threshold) / pageBPD;
+                        if (fillRatio >= minFillRatio) {
+                            adjustmentRatio = threshold;
+                        }
+                    }
+                }
+            } else if (difference < 0) {  // too long
+                double totalShrink = totalLength.getShrink();
+                if (totalShrink > 0) {
+                    adjustmentRatio = difference / totalShrink;
+                } else {
+                    adjustmentRatio = -INFINITE_RATIO;
+                }
+            } else {
+                adjustmentRatio = 0;
+            }
+        }
+
+        /**
+         * Computes the total demerits of the page layout up to the current page.
+         *
+         * @param footnotes information about footnotes put on the current page
+         * @param beforeFloats information about before-floats put on the current page
+         * @param lastNormalElementIdx index of the last Knuth element representing normal
+         * content put on the current page
+         * @param activeNode node representing the previous page break
+         * @param mode one of {@link PageBreakingAlgorithm#NORMAL_MODE}, {@link
+         * PageBreakingAlgorithm#FLOAT_PAGE_MODE} or {@link
+         * PageBreakingAlgorithm#FLUSH_MODE}
+         */
+        double computeDemerits(FootnotesRecord.FootnotesProgress footnotes,
+                               BeforeFloatsRecord.BeforeFloatsProgress beforeFloats,
+                               int lastNormalElementIdx,
+                               KnuthPageNode activeNode,
+                               int mode) {
+            // TODO penalty for footnotes of floats ending on penalty elements
+            KnuthElement lastNormalElement = (KnuthElement) par.get(lastNormalElementIdx);
+            double demerits = 0;
+            double f = Math.abs(adjustmentRatio);
+            /*
+             * 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 threshold it becomes possible to make the difference
+             * when there are deferred floats.
+             */
+            if (f > threshold) {
+                f = threshold;
+            }
+            f = 1 + 100 * f * f * f;
+            double minPageFillRatio;
+            if (mode == NORMAL_MODE) {
+                minPageFillRatio = MIN_NORMAL_PAGE_FILL_RATIO;
+                if (!lastNormalElement.isPenalty()) {
+                    demerits = f * f;
+                } else {
+                    double penalty = lastNormalElement.getP();
+                    if (penalty >= 0) {
+                        f += penalty;
+                        demerits = f * f;
+                    } else if (!lastNormalElement.isForcedBreak()) {
+                        demerits = f * f - penalty * penalty;
+                    } else {
+                        demerits = f * f;
+                    }
+                    if (((KnuthPenalty) lastNormalElement).isFlagged()
+                            && getElement(activeNode.position).isPenalty()
+                            && ((KnuthPenalty) getElement(activeNode.position)).isFlagged()) {
+                        // add demerit for consecutive breaks at flagged penalties
+                        demerits += repeatedFlaggedDemerit;
+                    }
+                }
+            } else {
+                minPageFillRatio = MIN_FLOAT_PAGE_FILL_RATIO;
+                demerits = f * f;
+            }
+            fitnessClass = computeFitness(adjustmentRatio);
+            if (Math.abs(fitnessClass - activeNode.fitness) > 1) {
+                // add demerit for consecutive breaks
+                // with very different fitness classes
+                demerits += incompatibleFitnessDemerit;
+            }
+
+            demerits += footnotes.getNbOfDeferred() * deferredFootnoteDemerits; 
+            if (footnotes.isLastSplit()) {
+                demerits += footnotes.getNbSplit() * splitFootnoteDemerits;
+            }
+            demerits += beforeFloats.getNbOfDeferred() * deferredFloatDemerits; 
+            if (beforeFloats.isLastSplit()) {
+                demerits += beforeFloats.getNbSplit() * splitFootnoteDemerits;
+            }
+            if (fillRatio < minPageFillRatio) {
+                /* To select too-short nodes among the least too underfull pages. This formula
+                 * will give smaller results than below but, anyway, too-short nodes are
+                 * handled separately
+                 */
+                demerits += (2.0 - fillRatio) * UNDERFULL_PAGE_DEMERITS;
+            } else if (minPageFillRatio < 1.0) {
+                /* demerits += x * UNDERFULL_PAGE_DEMERITS
+                 * The idea is that x tends to 1.0 when fillRatio tends to
+                 * MIN_PAGE_FILL_RATIO, to give the preference to full pages. Of course the
+                 * following formula works only if MIN_PAGE_FILL_RATIO != 1.0, hence the test
+                 */
+                demerits += (1.0 - fillRatio) / (1.0 - minPageFillRatio) * UNDERFULL_PAGE_DEMERITS;
+            }
+            demerits += activeNode.totalDemerits;
+            return demerits;
+        }
+
+        /**
+         * Tests if the node corresponding to the given parameters represents a feasible
+         * break, and if so computes its demerits and records it. This methods returns
+         * <code>true</code> if there is potential room for putting additional content on
+         * the page. Otherwise, this indicates that it is not even worth trying.
+         *
+         * @param mode one of {@link PageBreakingAlgorithm#NORMAL_MODE}, {@link
+         * PageBreakingAlgorithm#FLOAT_PAGE_MODE} or {@link
+         * PageBreakingAlgorithm#FLUSH_MODE}
+         * @param normalProgress progress information for the normal content
+         * @param footnotesProgress progress information for footnotes
+         * @param beforeFloatsProgress progress information for before-floats
+         * @param previousNode node representing the previous page break
+         * @return <code>true</code> if some additional content may potentially be added
+         * on the page (adjustment ratio &gt; -1); otherwise <code>false</code>
+         * (adjustment ratio &lt;= -1)
+         */
+        public boolean handleNode(int mode,
+                           NormalContentProgressInfo normalProgress,
+                           FootnotesRecord.FootnotesProgress footnotesProgress,
+                           BeforeFloatsRecord.BeforeFloatsProgress beforeFloatsProgress,
+                           KnuthPageNode previousNode) {
+            int pageBPD = getLineWidth(previousNode.line);
+            ElasticLength totalLength = new ElasticLength(footnotesProgress.getInserted());
+            totalLength.add(beforeFloatsProgress.getInserted());
+            if (mode == NORMAL_MODE) {
+                totalLength.add(normalProgress.insertedDims);
+                computeAdjustmentRatio(totalLength, pageBPD, MIN_NORMAL_PAGE_FILL_RATIO);
+            } else {
+                computeAdjustmentRatio(totalLength, pageBPD, MIN_FLOAT_PAGE_FILL_RATIO);
+            }
+            switch (mode) {
+            case NORMAL_MODE: {
+                int beforeFloatActualBPD = beforeFloatsProgress.getInserted().getLength();
+                if (adjustmentRatio < 0) {
+                    beforeFloatActualBPD += beforeFloatsProgress.getInserted().getShrink()
+                            * adjustmentRatio; 
+                } else if (adjustmentRatio > 0) {
+                    beforeFloatActualBPD += beforeFloatsProgress.getInserted().getStretch()
+                            * adjustmentRatio;
+                }
+                if (((double) beforeFloatActualBPD) / pageBPD >= 1.0 - TEXT_FRACTION) {
+                    // Not acceptable page, but if some further footnotes are
+                    // added, may become feasible thanks to shrinking
+                    double minBeforeFloatFraction
+                            = ((double) (beforeFloatsProgress.getInserted().getLength()
+                            - beforeFloatsProgress.getInserted().getShrink())) / pageBPD; 
+                    return adjustmentRatio > -1 && minBeforeFloatFraction < 1.0 - TEXT_FRACTION;
+                } else {
+                    if (-1 <= adjustmentRatio && adjustmentRatio <= threshold) {
+                        double demerits = computeDemerits(footnotesProgress,
+                                beforeFloatsProgress,
+                                normalProgress.position,
+                                previousNode,
+                                NORMAL_MODE);
+                        if (demerits < best.getDemerits(fitnessClass)) {
+                            ((BestPageRecords) best).addRecord(demerits,
+                                    previousNode,
+                                    adjustmentRatio,
+                                    totalLength.getShrink(),
+                                    totalLength.getStretch(),
+                                    difference, fitnessClass,
+                                    footnotesProgress, beforeFloatsProgress);
+                            lastTooShort = null;
+                        }
+                        return true;
+                    } else if (force) {
+                        double demerits = computeDemerits(footnotesProgress,
+                                beforeFloatsProgress,
+                                normalProgress.position,
+                                previousNode,
+                                NORMAL_MODE);
+                        sumsAfter.compute(normalProgress.position,
+                                totalWidth, totalStretch, totalShrink);
+                        if (adjustmentRatio < -1) {
+                            if (lastTooLong == null || demerits < lastTooLong.totalDemerits) {
+                                lastTooLong = createNode(normalProgress.position,
+                                        previousNode.line + 1,
+                                        fitnessClass,
+                                        sumsAfter.getWidthAfter(),
+                                        sumsAfter.getStretchAfter(),
+                                        sumsAfter.getShrinkAfter(),
+                                        adjustmentRatio,
+                                        totalLength.getShrink(),
+                                        totalLength.getStretch(),
+                                        difference, demerits, previousNode);
+                            }
+                            return false;
+                        } else {
+                            if (lastTooShort == null || demerits <= lastTooShort.totalDemerits) {
+                                if (considerTooShort) {
+                                    ((BestPageRecords) best).addRecord(demerits,
+                                            previousNode,
+                                            adjustmentRatio, 
+                                            totalLength.getShrink(),
+                                            totalLength.getStretch(),
+                                            difference, fitnessClass,
+                                            footnotesProgress, beforeFloatsProgress);
+                                }
+                                if (log.isDebugEnabled()) {
+                                    NumberFormat nf = NumberFormat.getInstance(Locale.ENGLISH);
+                                    nf.setMaximumFractionDigits(2);
+                                    nf.setGroupingUsed(false);
+                                    log.debug("Registering too-short node [demerits="
+                                            + nf.format(demerits)
+                                            + ", adjRatio=" + nf.format(adjustmentRatio)
+                                            + ", page " + (previousNode.line + 1)
+                                            + "]");
+                                }
+                                lastTooShort = createNode(normalProgress.position,
+                                        previousNode.line + 1,
+                                        fitnessClass,
+                                        sumsAfter.getWidthAfter(),
+                                        sumsAfter.getStretchAfter(),
+                                        sumsAfter.getShrinkAfter(),
+                                        adjustmentRatio,
+                                        totalLength.getShrink(),
+                                        totalLength.getStretch(),
+                                        difference, demerits, previousNode);
+                            }
+                            return true;
+                        }
+                    }
+                    return adjustmentRatio >= -1;
+                }
+            }
+            case FLOAT_PAGE_MODE:
+                if (-1 <= adjustmentRatio && adjustmentRatio <= threshold) {
+                    double demerits = computeDemerits(footnotesProgress,
+                            beforeFloatsProgress,
+                            normalProgress.position,
+                            previousNode,
+                            NORMAL_MODE);
+                    KnuthNode node = createNode(normalProgress.position,
+                            previousNode.line + 1,
+                            fitnessClass,
+                            previousNode.totalWidth,
+                            previousNode.totalStretch,
+                            previousNode.totalShrink,
+                            adjustmentRatio,
+                            totalLength.getShrink(),
+                            totalLength.getStretch(),
+                            difference,
+                            demerits,
+                            previousNode);
+                    registerActiveNode(node);
+                    lastTooShort = null;
+                }
+                return adjustmentRatio >= -1;
+            default:  // case FLUSH_MODE:
+                if (-1 <= adjustmentRatio && adjustmentRatio <= threshold) {
+                    double demerits = computeDemerits(footnotesProgress,
+                            beforeFloatsProgress,
+                            par.size() - 1,
+                            previousNode,
+                            NORMAL_MODE);
+                    KnuthNode node = createNode(par.size() - 1,
+                            previousNode.line + 1,
+                            fitnessClass,
+                            0, 0, 0,
+                            adjustmentRatio,
+                            totalLength.getShrink(), totalLength.getStretch(),
+                            difference, demerits, previousNode);
+                    queue.addLast(node);
+                    lastTooShort = null;
+                    return true;
+                } else if (force) {
+                    double demerits = computeDemerits(footnotesProgress,
+                            beforeFloatsProgress,
+                            par.size() - 1,
+                            previousNode,
+                            NORMAL_MODE);
+                    if (adjustmentRatio < -1) {
+                        if (lastTooLong == null || demerits < lastTooLong.totalDemerits) {
+                            lastTooLong = createNode(par.size() - 1,
+                                    previousNode.line + 1,
+                                    fitnessClass,
+                                    0, 0, 0,
+                                    adjustmentRatio,
+                                    totalLength.getShrink(), totalLength.getStretch(),
+                                    difference, demerits, previousNode);
+                        }
+                        return false;
+                    } else {
+                        if (lastTooShort == null || demerits <= lastTooShort.totalDemerits) {
+                            lastTooShort = createNode(par.size() - 1,
+                                    previousNode.line + 1,
+                                    fitnessClass,
+                                    0, 0, 0,
+                                    adjustmentRatio,
+                                    totalLength.getShrink(), totalLength.getStretch(),
+                                    difference, demerits, previousNode);
+                            if (considerTooShort) {
+                                queue.addLast(lastTooShort);
+                            }
+                        }
+                        return true;
+                    }
+                }
+            return adjustmentRatio >= -1;
+            }
+        }
+
+        /**
+         * When in flush mode, uses the given queue for registering new active nodes. TODO
+         * vh: highly temporary! As in flush mode the handling is a bit different,
+         * activeLines cannot be re-used. Will have to unify the handling of active nodes
+         * eventually.
+         *
+         * @param queue FIFO structure for registering active nodes in flush mode
+         */
+        void setQueue(LinkedList queue) {
+            this.queue = queue;
         }
     }
 
     protected void initialize() {
         super.initialize();
-        footnotes.initialize();
-        floats.initialize();
+        normalContentProgress.initialize(par);
+        footnotesRecord.initialize();
+        beforeFloatsRecord.initialize();
+        footnotesProgress.initialize();
+        beforeFloatsProgress.initialize();
+        activeNodeRecorder.setForce(force);
     }
 
     public KnuthNode createNode(int position, int line, int fitness,
@@ -163,7 +657,7 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm {
                                    int difference, double totalDemerits, KnuthNode previous) {
         return new KnuthPageNode(position, line, fitness,
                                  totalWidth, totalStretch, totalShrink,
-                                 footnotes.getProgress(), floats.getProgress(),
+                                 footnotesProgress, beforeFloatsProgress,
                                  adjustRatio, availableShrink, availableStretch,
                                  difference, totalDemerits, previous);
     }
@@ -187,31 +681,29 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm {
     protected void handleBox(KnuthBox box) {
         if (box instanceof KnuthBlockBox
                 && ((KnuthBlockBox) box).hasFootnoteAnchors()) {
-            footnotes.add(((KnuthBlockBox) box).getFootnoteElementLists());
+            footnotesRecord.add(((KnuthBlockBox) box).getFootnoteElementLists());
         }
         if (box instanceof KnuthBlockBox
                 && ((KnuthBlockBox) box).hasFloatAnchors()) {
-            floats.add(((KnuthBlockBox) box).getFloatElementLists());
+            beforeFloatsRecord.add(((KnuthBlockBox) box).getFloatElementLists());
         }
     }
 
 
     protected int restartFrom(KnuthNode restartingNode, int currentIndex) {
         int returnValue = super.restartFrom(restartingNode, currentIndex);
-        footnotes.resetNewSinceLastBreakpoint();
-        floats.resetNewSinceLastBreakpoint();
-        if (footnotes.existing() || floats.existing()) {
+        if (footnotesRecord.existing() || beforeFloatsRecord.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).hasFootnoteAnchors()) {
-                    footnotes.reset(((KnuthBlockBox) resettedElement).getFootnoteElementLists());
+                KnuthElement resetElement = getElement(j);
+                if (resetElement instanceof KnuthBlockBox
+                        && ((KnuthBlockBox) resetElement).hasFootnoteAnchors()) {
+                    footnotesRecord.reset(((KnuthBlockBox) resetElement).getFootnoteElementLists());
                 }
-                if (resettedElement instanceof KnuthBlockBox
-                        && ((KnuthBlockBox) resettedElement).hasFloatAnchors()) {
-                    floats.reset(((KnuthBlockBox) resettedElement).getFloatElementLists());//TODO
+                if (resetElement instanceof KnuthBlockBox
+                        && ((KnuthBlockBox) resetElement).hasFloatAnchors()) {
+                    beforeFloatsRecord.reset(((KnuthBlockBox) resetElement).getFloatElementLists());
                 }
             }
         }
@@ -219,291 +711,177 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm {
     }
 
     protected void considerLegalBreak(KnuthElement element, int elementIdx) {
-        super.considerLegalBreak(element, elementIdx);
-        footnotes.resetNewSinceLastBreakpoint();
-        floats.resetNewSinceLastBreakpoint();
-    }
 
-    protected int computeDifference(KnuthNode activeNode, KnuthElement element,
-                                    int elementIndex) {
-        KnuthPageNode pageNode = (KnuthPageNode) activeNode;
-        int actualWidth = totalWidth - pageNode.totalWidth;
-        if (element.isPenalty()) {
-            actualWidth += element.getW();
+        if (log.isTraceEnabled()) {
+            log.trace("considerLegalBreak() at " + elementIdx 
+                    + " (" + totalWidth + "+" + totalStretch + "-" + totalShrink 
+                    + "), parts/lines: " + startLine + "-" + endLine);
+            log.trace("\tCurrent active node list: " + activeNodeCount + " " + this.toString("\t"));
         }
-        if (footnotes.existing()) {
-            footnotes.setProgress(pageNode.footnotesProgress);
-            // compute the total length of the footnotes not yet inserted
-            int allFootnotes = footnotes.getTotalLength()
-                    - pageNode.footnotesProgress.getAlreadyInsertedLength();
-            if (allFootnotes > 0) {
-                // this page contains some footnote citations
-                // add the footnote separator width
-                actualWidth += footnotes.getSeparatorLength().opt;
-                if (actualWidth + allFootnotes <= getLineWidth()) {
-                    // there is enough space to insert all footnotes:
-                    // add the whole allFootnotes length
-                    actualWidth += allFootnotes;
-                    footnotes.insertAll();
-                } else {
-                    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();
-                    }
+
+        lastDeactivated = null;
+        lastTooLong = null;
+        for (int line = startLine; line < endLine; line++) {
+            for (KnuthPageNode node = (KnuthPageNode) getNode(line);
+                    node != null;
+                    node = (KnuthPageNode) node.next) {
+                if (node.position == elementIdx) {
+                    continue;
                 }
-            } // 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;
+                footnotesProgress.setPrevious(node.footnotesProgress);
+                beforeFloatsProgress.setPrevious(node.beforeFloatsProgress);
+                footnotesProgress.handleSplit();
+                beforeFloatsProgress.handleSplit();
+                normalContentProgress.insertedDims.set(totalShrink - node.totalShrink,
+                        totalWidth - node.totalWidth, totalStretch - node.totalStretch);
+                normalContentProgress.position = elementIdx;
+                if (element.isPenalty()) {
+                    normalContentProgress.insertedDims.add(0, element.getW(), 0);
                 }
-            }
-        }
-        /* 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 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 checkCanDeferOldOutOfLines(OutOfLineRecord outOfLine,
-                                               int activeNodePosition,
-                                               int contentElementIndex) {
-        return (noBreakBetween(activeNodePosition, contentElementIndex)
-                && outOfLine.deferred());
-    }
-
-    /**
-     * 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 is a legal breakpoint
-     */
-    private boolean noBreakBetween(int prevBreakIndex, int breakIndex) {
-        // this method stores the parameters and the return value from previous calls
-        // in order to avoid scanning the element list unnecessarily:
-        //  - if there is no break between element #i and element #j
-        //    there will not be a break between #(i+h) and #j too
-        //  - if there is a break between element #i and element #j
-        //    there will be a break between #(i-h) and #(j+k) too
-        if (storedPrevBreakIndex != -1
-            && ((prevBreakIndex >= storedPrevBreakIndex
-                 && breakIndex == storedBreakIndex
-                 && storedValue)
-                || (prevBreakIndex <= storedPrevBreakIndex
-                    && breakIndex >= storedBreakIndex
-                    && !storedValue))) {
-            // use the stored value, do nothing
-        } else {
-            // compute the new value
-            int index;
-            // ignore suppressed elements
-            for (index = prevBreakIndex + 1;
-                    !par.getElement(index).isBox();
-                    index++) {
-                //nop
-            }
-            // find the next break
-            for (;
-                 index < breakIndex;
-                 index++) {
-                if (par.getElement(index).isGlue() && par.getElement(index - 1).isBox()
-                    || par.getElement(index).isPenalty() 
-                       && ((KnuthElement) par.getElement(index)).getP() < KnuthElement.INFINITE) {
-                    // break found
-                    break;
+                boolean recorded = activeNodeRecorder.handleNode(NORMAL_MODE,
+                        normalContentProgress, footnotesProgress, beforeFloatsProgress, node); 
+                if (!recorded || element.isForcedBreak()) {
+                    deactivateNode(node);
+                    lastDeactivated = compareNodes(lastDeactivated, node);
+                }
+                if (recorded) {
+                    footnotesProgress.consider(NORMAL_MODE, activeNodeRecorder,
+                            normalContentProgress, beforeFloatsProgress, node);
                 }
-            }
-            // update stored parameters and value
-            storedPrevBreakIndex = prevBreakIndex;
-            storedBreakIndex = breakIndex;
-            storedValue = (index == breakIndex);
-        }
-        return storedValue;
-    }
 
-    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).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;
-            } else {
-                return INFINITE_RATIO;
-            }
-        } else if (difference < 0) {
-            int maxAdjustment = totalShrink - activeNode.totalShrink;
-            // add the footnote separator shrink if some footnote content will be added
-            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;
-            } else {
-                return -INFINITE_RATIO;
-            }
-        } else {
-            return 0;
+            addBreaks(line, elementIdx);
         }
     }
 
-    protected double computeDemerits(KnuthNode activeNode, KnuthElement element, 
-                                    int fitnessClass, double r) {
-        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();
-            demerits = f * f;
-        } else if (element.isPenalty() && !element.isForcedBreak()) {
-            double penalty = element.getP();
-            demerits = f * f - penalty * penalty;
-        } else {
-            demerits = f * f;
-        }
+    // TODO vh: refactor
+    // It may happen that several successive float-only pages are created; in such cases
+    // the progress informations must be saved as they'll still be used after this method
+    // call
+    private Stack footnotesStack = new Stack();
 
-        if (element.isPenalty() && ((KnuthPenalty) element).isFlagged()
-            && getElement(activeNode.position).isPenalty()
-            && ((KnuthPenalty) getElement(activeNode.position)).isFlagged()) {
-            // add demerit for consecutive breaks at flagged penalties
-            demerits += repeatedFlaggedDemerit;
-        }
-        if (Math.abs(fitnessClass - activeNode.fitness) > 1) {
-            // add demerit for consecutive breaks
-            // with very different fitness classes
-            demerits += incompatibleFitnessDemerit;
-        }
+    private Stack beforeFloatsStack = new Stack();
 
-        if (footnotes.existing()) {
-            demerits += footnotes.getNbOfDeferred() * deferredFootnoteDemerits; 
-            if (footnotes.isSplit()) {
-                demerits += splitFootnoteDemerits;
-            }
-        }
-        if (floats.existing()) {
-            demerits += floats.getNbOfDeferred() * deferredFloatDemerits; 
+    public void registerActiveNode(KnuthNode node) {
+        super.registerActiveNode(node);
+        KnuthPageNode pageNode = (KnuthPageNode) node;
+        if (pageNode.position < par.size() - 1
+                && (pageNode.footnotesProgress.remaining() || pageNode.beforeFloatsProgress.remaining())
+                && FLOAT_PAGES_ALLOWED) {
+            footnotesStack.push(footnotesProgress);
+            footnotesProgress = footnotesRecord.new FootnotesProgress();
+            beforeFloatsStack.push(beforeFloatsProgress);
+            beforeFloatsProgress = beforeFloatsRecord.new BeforeFloatsProgress();
+            
+            footnotesProgress.setPrevious(pageNode.footnotesProgress);
+            beforeFloatsProgress.setPrevious(pageNode.beforeFloatsProgress);
+            footnotesProgress.handleSplit(FLOAT_PAGE_MODE, activeNodeRecorder,
+                    normalContentProgress, beforeFloatsProgress, pageNode);
+            beforeFloatsProgress.handleSplit(FLOAT_PAGE_MODE, activeNodeRecorder,
+                    normalContentProgress, footnotesProgress, pageNode);
+            footnotesProgress.consider(FLOAT_PAGE_MODE, activeNodeRecorder,
+                    normalContentProgress, beforeFloatsProgress, pageNode);
+            
+            footnotesProgress = (FootnotesProgress) footnotesStack.pop();
+            beforeFloatsProgress = (BeforeFloatsProgress) beforeFloatsStack.pop();
         }
-        demerits += activeNode.totalDemerits;
-        return demerits;
     }
 
+    // TODO vh: refactor
     protected void finish() {
-        for (int i = startLine; i < endLine; i++) {
-            for (KnuthPageNode node = (KnuthPageNode) getNode(i);
-                 node != null;
-                 node = (KnuthPageNode) node.next) {
-                if (node.footnotesProgress.getAlreadyInsertedLength()
-                        < footnotes.getTotalLength()) {
-                    // layout remaining footnote bodies
-                    footnotes.createFootnotePages(node, this, getLineWidth());
+        lastTooShort = null;
+        lastTooLong = null;
+        Vector bestNodes = new Vector();
+        int startIndex = startLine;
+        int endIndex = endLine;
+        bestNodes.setSize(endIndex - startIndex);
+        // Declared as a LinkedList as we need access to the queue-like methods defined
+        // in LinkedList
+        LinkedList queue = new LinkedList();
+        activeNodeRecorder.setQueue(queue);
+        Object n = null;
+        do {
+            for (int i = startIndex; i < endIndex; i++) {
+                for (KnuthPageNode node = (KnuthPageNode) getNode(i);
+                     node != null;
+                     node = (KnuthPageNode) node.next) {
+                    deactivateNode(node);
+                    if (node.footnotesProgress.remaining()
+                            || node.beforeFloatsProgress.remaining()) {
+                        queue.addFirst(node);
+                        flush(queue, bestNodes, startIndex);
+                    } else {
+                        if (bestNodes.get(i - startIndex) == null
+                                || node.totalDemerits < ((KnuthNode) bestNodes.get(i - startIndex)).totalDemerits) {
+                            bestNodes.set(i - startIndex, node);
+                        }
+                    }
                 }
-                if (node.floatsProgress.getAlreadyInsertedLength() < floats.getTotalLength()) {
-                    // layout remaining float bodies
-                    floats.createFloatPages(node, this, getLineWidth());
+            }
+            Iterator iter = bestNodes.iterator();
+            while (iter.hasNext() && (n = iter.next()) == null);
+            if (n == null) {
+                log.debug("Recovering");
+                KnuthNode recovered = null;
+                if (lastTooShort == null) {
+                    if (lastTooLong == null) {
+                        log.debug("Both lastTooShort and lastTooLong null!");
+                    } else {
+                        recovered = lastTooLong;
+                    }
+                } else {
+                    recovered = lastTooShort;
+                }
+                recovered.totalDemerits = 0;
+                registerActiveNode(recovered);
+                startIndex = recovered.line;
+                endIndex = startIndex + 1;
+                lastTooLong = null;
+                lastTooShort = null;
+            }
+        } while (n == null);
+        Iterator nodeIter = bestNodes.iterator();
+        while (nodeIter.hasNext()) {
+            KnuthNode node = (KnuthNode) nodeIter.next();
+            if (node != null) { // TODO
+                int tmp = endLine;
+                registerActiveNode(node);
+                if (startLine > node.line) {
+                    startLine = node.line;
+                }
+                if (endLine < tmp) {
+                    endLine = tmp;
                 }
             }
         }
     }
 
+    // TODO vh: refactor
+    private void flush(LinkedList queue, Vector bestNodes, int startIndex) {
+        do {
+            KnuthPageNode node = (KnuthPageNode) queue.removeFirst();
+            if (node.footnotesProgress.remaining() || node.beforeFloatsProgress.remaining()) {
+                footnotesProgress.setPrevious(node.footnotesProgress);
+                beforeFloatsProgress.setPrevious(node.beforeFloatsProgress);
+                footnotesProgress.handleSplit(FLUSH_MODE, activeNodeRecorder,
+                        null, beforeFloatsProgress, node);
+                beforeFloatsProgress.handleSplit(FLUSH_MODE, activeNodeRecorder,
+                        null, footnotesProgress, node);
+                footnotesProgress.consider(FLUSH_MODE, activeNodeRecorder,
+                        null, beforeFloatsProgress, node);
+            } else {
+                int index = node.line - startIndex;
+                if (index >= bestNodes.size()) {
+                    bestNodes.setSize(index + 1);
+                }
+                if (bestNodes.get(index) == null
+                        || node.totalDemerits < ((KnuthNode) bestNodes.get(index)).totalDemerits) {
+                    bestNodes.set(index, node);
+                }                
+            }
+        } while (!queue.isEmpty());
+    }
+
     /**
      * @return a list of PageBreakPosition elements
      */
@@ -536,11 +914,15 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm {
         //      ? bestActiveNode.difference : bestActiveNode.difference + fillerMinWidth;
         int difference = bestActiveNode.difference;
         if (difference + bestActiveNode.availableShrink < 0) {
-            if (!autoHeight && log.isWarnEnabled()) {
-                log.warn(FONode.decorateWithContextInfo(
-                        "Part/page " + (getPartCount() + 1) 
-                        + " overflows the available area in block-progression dimension.", 
-                        getFObj()));
+            if (!autoHeight) {
+                if (layoutListener != null) {
+                    layoutListener.notifyOverflow(bestActiveNode.line - 1, getFObj());
+                } else if (log.isWarnEnabled()) {
+                    log.warn(FONode.decorateWithContextInfo(
+                            "Part/page " + (bestActiveNode.line - 1) 
+                            + " overflows the available area in block-progression dimension.", 
+                            getFObj()));
+                }
             }
         }
         boolean isNonLastPage = (bestActiveNode.line < total);
@@ -579,8 +961,8 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm {
         if (firstFootnoteListIndex == -1) {
             firstFootnoteListIndex++;
             firstFootnoteElementIndex = 0;
-        } else if (footnotes.getSequence(firstFootnoteListIndex) != null
-                && firstFootnoteElementIndex == ((LinkedList) footnotes.
+        } else if (footnotesRecord.getSequence(firstFootnoteListIndex) != null
+                && firstFootnoteElementIndex == ((LinkedList) footnotesRecord.
                         getSequence(firstFootnoteListIndex)).size() - 1) {
             // advance to the next list
             firstFootnoteListIndex++;
@@ -590,7 +972,7 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm {
         }
         // compute the indexes of the first float list
         int firstFloatListIndex = ((KnuthPageNode) bestActiveNode.previous).
-                floatsProgress.getLastInsertedIndex() + 1;
+                beforeFloatsProgress.getLastInsertedIndex() + 1;
 
         // add nodes at the beginning of the list, as they are found
         // backwards, from the last one to the first one
@@ -605,7 +987,7 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm {
                 ((KnuthPageNode) bestActiveNode).footnotesProgress.
                         getLastElementOfLastInsertedIndex(),
                 firstFloatListIndex,
-                ((KnuthPageNode) bestActiveNode).floatsProgress.getLastInsertedIndex(),
+                ((KnuthPageNode) bestActiveNode).beforeFloatsProgress.getLastInsertedIndex(),
                 ratio, difference));
     }
 
@@ -624,7 +1006,7 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm {
                     bestActiveNode = compareNodes(bestActiveNode, node);
                 }
                 if (node != bestActiveNode) {
-                    removeNode(i, node);
+                    deactivateNode(node);
                 }
             }
         }
@@ -632,11 +1014,11 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm {
     }
 
     public LinkedList getFootnoteList(int index) {
-        return (LinkedList) footnotes.getSequence(index);
+        return (LinkedList) footnotesRecord.getSequence(index);
     }
 
     public LinkedList getFloatList(int index) {
-        return (LinkedList) floats.getSequence(index);
+        return (LinkedList) beforeFloatsRecord.getSequence(index);
     }
 
     /** @return the associated top-level formatting object. */
@@ -658,4 +1040,18 @@ public class PageBreakingAlgorithm extends BreakingAlgorithm {
         return bpd;
     }
     
+    /**
+     * Interface to notify about layout events during page breaking.
+     */
+    public interface PageBreakingLayoutListener {
+
+        /**
+         * Issued when an overflow is detected
+         * @param part the number of the part (page) this happens on
+         * @param obj the root FO object where this happens
+         */
+        void notifyOverflow(int part, FObj obj);
+        
+    }
+    
 }
index e1e40744bad4512ef77811b178672cf1af307dda..92ce3d06760add4db946b0b9a970bb064943a7ce 100644 (file)
@@ -34,6 +34,8 @@ import org.apache.fop.area.LineArea;
 import org.apache.fop.area.Resolvable;
 
 import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.FObj;
 import org.apache.fop.fo.flow.Marker;
 import org.apache.fop.fo.flow.RetrieveMarker;
 
@@ -44,6 +46,7 @@ import org.apache.fop.fo.pagination.RegionBody;
 import org.apache.fop.fo.pagination.SideRegion;
 import org.apache.fop.fo.pagination.SimplePageMaster;
 import org.apache.fop.fo.pagination.StaticContent;
+import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener;
 import org.apache.fop.layoutmgr.inline.ContentLayoutManager;
 
 import org.apache.fop.traits.MinOptMax;
@@ -170,6 +173,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
                 (currentPageNum - startPageNum) + 1);
         areaTreeHandler.notifyPageSequenceFinished(pageSeq,
                 (currentPageNum - startPageNum) + 1);
+        pageSeq.releasePageSequence();
         log.debug("Ending layout");
     }
 
@@ -194,8 +198,9 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
             context.setRefIPD(flowIPD);
         }
         
+        /** @see org.apache.fop.layoutmgr.AbstractBreaker#getTopLevelLM() */
         protected LayoutManager getTopLevelLM() {
-            return null;  // unneeded for PSLM
+            return pslm;
         }
         
         /** @see org.apache.fop.layoutmgr.AbstractBreaker#getPageProvider() */
@@ -203,6 +208,32 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
             return pageProvider;
         }
         
+        /**
+         * @see org.apache.fop.layoutmgr.AbstractBreaker#getLayoutListener()
+         */
+        protected PageBreakingLayoutListener getLayoutListener() {
+            return new PageBreakingLayoutListener() {
+
+                public void notifyOverflow(int part, FObj obj) {
+                    Page p = pageProvider.getPage(
+                                false, part, PageProvider.RELTO_CURRENT_ELEMENT_LIST);
+                    RegionBody body = (RegionBody)p.getSimplePageMaster().getRegion(
+                            Region.FO_REGION_BODY);
+                    String err = FONode.decorateWithContextInfo(
+                            "Content of the region-body on page " 
+                            + p.getPageViewport().getPageNumberString() 
+                            + " overflows the available area in block-progression dimension.", 
+                            obj);
+                    if (body.getOverflow() == Constants.EN_ERROR_IF_OVERFLOW) {
+                        throw new RuntimeException(err);
+                    } else {
+                        PageSequenceLayoutManager.log.warn(err);
+                    }
+                }
+                
+            };
+        }
+
         /** @see org.apache.fop.layoutmgr.AbstractBreaker */
         protected int handleSpanChange(LayoutContext childLC, int nextSequenceStartsOn) {
             needColumnBalancing = false;
@@ -436,7 +467,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
             //Restart last page
             PageBreakingAlgorithm algRestart = new PageBreakingAlgorithm(
                     getTopLevelLM(),
-                    getPageProvider(),
+                    getPageProvider(), getLayoutListener(),
                     alg.getAlignment(), alg.getAlignmentLast(), 
                     footnoteSeparatorLength, floatSeparatorLength,
                     isPartOverflowRecoveryActivated(), false, false);
@@ -496,7 +527,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
             //Restart last page
             PageBreakingAlgorithm algRestart = new BalancingColumnBreakingAlgorithm(
                     getTopLevelLM(),
-                    getPageProvider(),
+                    getPageProvider(), getLayoutListener(),
                     alignment, Constants.EN_START, footnoteSeparatorLength, floatSeparatorLength,
                     isPartOverflowRecoveryActivated(),
                     getCurrentPV().getBodyRegion().getColumnCount());
index 222f20040452a296542ce735d0ee0cd6b8472938..91a0634f556081711fc1a61649ee925b02cb48f0 100644 (file)
@@ -609,7 +609,7 @@ public class SpaceResolver {
      * Resolves unresolved elements applying the space resolution rules defined in 4.3.1.
      * @param elems the element list
      */
-    public static void resolveElementList(LinkedList elems) {
+    public static void resolveElementList(List elems) {
         if (log.isTraceEnabled()) {
             log.trace(elems);
         }
diff --git a/src/java/org/apache/fop/layoutmgr/breaking/BeforeFloatsRecord.java b/src/java/org/apache/fop/layoutmgr/breaking/BeforeFloatsRecord.java
new file mode 100644 (file)
index 0000000..7881772
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * 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.Iterator;
+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;
+
+/**
+ * A class that handles the placement of before-floats. Stores informations about
+ * existing before-floats, already placed ones, split floats, etc.
+ */
+public class BeforeFloatsRecord extends OutOfLineRecord {
+
+    /**
+     * Dimensions of the recorded before-floats. As the whole dimensions of floats are
+     * often required to determine placements, they are computed only once and stored in a
+     * dedicated field.
+     */
+    private List elasticLengths = new ArrayList();
+
+    /**
+     * Progress informations for before-floats. Among other things, this class handles the
+     * splitting of floats over several pages.
+     */
+    public class BeforeFloatsProgress extends ProgressInfo {
+
+        /**
+         * Creates and initializes a new record.
+         */
+        public BeforeFloatsProgress() {
+            super();
+        }
+
+        /**
+         * Creates a copy of the given record.
+         *
+         * @param beforeFloatProgress original progress information
+         */
+        public BeforeFloatsProgress(BeforeFloatsProgress beforeFloatProgress) {
+            super(beforeFloatProgress);
+        }
+
+        /**
+         * Checks whether there is still an entire float which has not been handled.
+         *
+         * @return <code>true</code> if there is still at least one entire non-handled
+         * float
+         */
+        private boolean hasNextFull() {
+            return lastInsertedIndex < knuthSequences.size() - 1;
+        }
+
+        /**
+         * Places on the current page the next entire float, without considering to split
+         * it.
+         */
+        private void nextFull() {
+            lastInsertedIndex++;
+            lastElementOfLastInsertedIndex
+                    = ((List) knuthSequences.get(lastInsertedIndex)).size() - 1;
+            alreadyInserted.add((ElasticLength) elasticLengths.get(lastInsertedIndex)); 
+        }
+
+        /**
+         * If the float on the previous page was split, put the rest of it on the current
+         * page. This method is only called when typesetting a page which also has normal
+         * content. If the float on the previous page was split, this implies that this
+         * was a float-only page with only one float (no normal content, no footnote, no
+         * other float). This is the only case where a before-float is allowed to be
+         * split.
+         */
+        public void handleSplit() {
+            if (isLastSplit()) {
+                do {
+                    nextInsideBreak();
+                } while (!endOfOutOfLine());
+                addSeparator();
+            }
+        }
+
+        /**
+         * If the float on the previous page was split, put the rest of it on the current
+         * page. This method is called when building a float-only page, either inside a
+         * page sequence or at the end of it.
+         *
+         * @param mode one of {@link PageBreakingAlgorithm#FLOAT_PAGE_MODE} or {@link
+         * PageBreakingAlgorithm#FLUSH_MODE}
+         * @param activeNodeRecorder
+         * @param normalContentProgress information about normal content already typeset
+         * @param footnotesProgress information about footnotes already typeset
+         * @param previousNode node ending the previous page
+         */
+        public void handleSplit(int mode,
+                         PageBreakingAlgorithm.ActiveNodeRecorder activeNodeRecorder,
+                         PageBreakingAlgorithm.NormalContentProgressInfo normalContentProgress,
+                         FootnotesRecord.FootnotesProgress footnotesProgress,
+                         KnuthPageNode previousNode) {
+            if (isLastSplit()) {
+                do {
+                    nextInsideBreak();
+                    if (!activeNodeRecorder.handleNode(mode, normalContentProgress,
+                            footnotesProgress, this, previousNode)) {
+                        break;
+                    }
+                } while (!endOfOutOfLine());
+            }
+        }
+
+        /**
+         * Considers the placement of floats on the current page.
+         *
+         * @param mode one of {@link PageBreakingAlgorithm#NORMAL_MODE}, {@link
+         * PageBreakingAlgorithm#FLOAT_PAGE_MODE} or {@link
+         * PageBreakingAlgorithm#FLUSH_MODE}
+         * @param activeNodeRecorder
+         * @param normalContentProgress information about normal content already typeset
+         * @param footnotesProgress information about footnotes already typeset
+         * @param previousNode node ending the previous page
+         */
+        public void consider(int mode,
+                             PageBreakingAlgorithm.ActiveNodeRecorder activeNodeRecorder,
+                             PageBreakingAlgorithm.NormalContentProgressInfo normalContentProgress,
+                             FootnotesRecord.FootnotesProgress footnotesProgress,
+                             KnuthPageNode previousNode) {
+            setPrevious(previousNode.beforeFloatsProgress);
+            switch (mode) {
+            case PageBreakingAlgorithm.NORMAL_MODE:
+                if (alreadyInserted.getLength() == 0) {
+                    addSeparator();
+                }
+                while (hasNextFull()) {
+                    nextFull();
+                    if (!activeNodeRecorder.handleNode(mode, normalContentProgress,
+                            footnotesProgress, this, previousNode)) {
+                        break;
+                    }
+                }
+                break;
+            case PageBreakingAlgorithm.FLOAT_PAGE_MODE:
+            case PageBreakingAlgorithm.FLUSH_MODE:
+                if (footnotesProgress.alreadyInserted.getLength() == 0
+                        && alreadyInserted.getLength() == 0
+                        && hasNextFull()) {  // first split allowed
+                    lastInsertedIndex++;
+                    lastElementOfLastInsertedIndex = -1;
+                    do {
+                        nextInsideBreak();
+                        if (!activeNodeRecorder.handleNode(mode, normalContentProgress,
+                                footnotesProgress, this, previousNode)) {
+                            break;
+                        }
+                    } while (!endOfOutOfLine());
+                }
+                while (hasNextFull()) {
+                    nextFull();
+                    if (!activeNodeRecorder.handleNode(mode, normalContentProgress,
+                            footnotesProgress, this, previousNode)) {
+                        break;
+                    }
+                }
+                break;
+            }
+        }
+    }
+    
+    /**
+     * Creates a new record for handling before-floats.
+     *
+     * @param separator dimensions of the separator between the before-float area
+     * and the normal content
+     */
+    public BeforeFloatsRecord(MinOptMax separator) {
+        super(separator);
+    }
+
+    public void initialize() {
+        super.initialize();
+        elasticLengths = new ArrayList();
+    }
+
+    public void add(List elementLists) {
+        // compute the total length of the footnotes
+        ListIterator elementListsIterator = elementLists.listIterator();
+        while (elementListsIterator.hasNext()) {
+            List knuthSequence = (List) elementListsIterator.next();
+            
+            //Space resolution (Note: this does not respect possible stacking constraints 
+            //between footnotes!)
+            SpaceResolver.resolveElementList(knuthSequence);
+            
+            knuthSequences.add(knuthSequence);
+            Iterator elementIter = knuthSequence.iterator();
+            ElasticLength floatDims = new ElasticLength();
+            while (elementIter.hasNext()) {
+                KnuthElement element = (KnuthElement) elementIter.next();
+                if (element.isBox()) {
+                    floatDims.add(0, element.getW(), 0);
+                } else if (element.isGlue()) {
+                    floatDims.add(element.getZ(), element.getW(), element.getY());
+                }
+            }
+            elasticLengths.add(floatDims);
+        }
+    }
+
+    public void reset(List elementLists) {
+        super.reset(elementLists);
+        for (int i = 0; i < elementLists.size(); i++) {
+            elasticLengths.remove(elasticLengths.size() - 1);
+        }
+    }
+}
diff --git a/src/java/org/apache/fop/layoutmgr/breaking/ElasticLength.java b/src/java/org/apache/fop/layoutmgr/breaking/ElasticLength.java
new file mode 100644 (file)
index 0000000..374b9b3
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * 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;
+
+/**
+ * A length with three components: the natural value, the amount of authorized shrink, the
+ * amount of authorized stretch.
+ */
+public class ElasticLength {
+
+    private int shrink;
+
+    private int length;
+
+    private int stretch;
+
+    /**
+     * Creates a new length of 0, with 0 shrink and 0 stretch.
+     */
+    public ElasticLength() {
+        shrink = 0;
+        length = 0;
+        stretch = 0;
+    }
+
+    /**
+     * Creates a copy of the given length.
+     *
+     * @param el an elastic length
+     */
+    public ElasticLength(ElasticLength el) {
+        shrink = el.shrink;
+        length = el.length;
+        stretch = el.stretch;
+    }
+
+    /**
+     * Creates a new length with the given components.
+     *
+     * @param shrink amount of authorized shrink
+     * @param length natural length
+     * @param stretch amount of authorized stretch
+     */
+    public ElasticLength(int shrink, int length, int stretch) {
+        set(shrink, length, stretch);
+    }
+
+    /**
+     * Returns the amount by which this length may be shrinked.
+     *
+     * @return the amount by which this length may be shrinked.
+     */
+    public int getShrink() {
+        return shrink;
+    }
+
+    /**
+     * Returns the natural value of this length.
+     *
+     * @return the natural value of this length
+     */
+    public int getLength() {
+        return length;
+    }
+
+    /**
+     * Returns the amount by which this length may be stretched.
+     *
+     * @return the amount by which this length may be stretched.
+     */
+    public int getStretch() {
+        return stretch;
+    }
+
+    /**
+     * Resets the three components of this length to 0.
+     */
+    public void reset() {
+        set(0, 0, 0);
+    }
+
+    /**
+     * Sets the components of this length to the given values.
+     *
+     * @param shrink authorized shrink
+     * @param length natural length
+     * @param stretch authorized stretch
+     */
+    public void set(int shrink, int length, int stretch) {
+        this.shrink = shrink;
+        this.length = length;
+        this.stretch = stretch;
+    }
+
+    /**
+     * Sets the components of this length to those of the given length.
+     *
+     * @param elasticLength a length
+     */
+    public void set(ElasticLength elasticLength) {
+        set(elasticLength.shrink, elasticLength.length, elasticLength.stretch);
+    }
+
+    /**
+     * Adds the given values to the components of this length.
+     *
+     * @param shrink additional authorized shrink
+     * @param length additional natural length
+     * @param stretch additional authorized stretch
+     */
+    public void add(int shrink, int length, int stretch) {
+        this.shrink += shrink;
+        this.length += length;
+        this.stretch += stretch;
+    }
+
+    /**
+     * Adds to the components of this length those of the given length.
+     *
+     * @param elasticLength a length of which each component will be added to this ones'
+     */
+    public void add(ElasticLength elasticLength) {
+        add(elasticLength.shrink, elasticLength.length, elasticLength.stretch);
+    }
+
+    public String toString() {
+        return "[" + shrink + "," + length + "," + stretch + "]";
+    }
+}
diff --git a/src/java/org/apache/fop/layoutmgr/breaking/FootnotesRecord.java b/src/java/org/apache/fop/layoutmgr/breaking/FootnotesRecord.java
new file mode 100644 (file)
index 0000000..0ff1869
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * 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 org.apache.fop.layoutmgr.PageBreakingAlgorithm;
+import org.apache.fop.layoutmgr.PageBreakingAlgorithm.KnuthPageNode;
+import org.apache.fop.traits.MinOptMax;
+
+/**
+ * A class that handles the placement of footnotes. Stores informations about
+ * existing footnotes, already placed ones, split footnotes, etc.
+ */
+public class FootnotesRecord extends OutOfLineRecord {
+
+    /**
+     * Progress informations for footnotes. When building pages, footnotes content will
+     * be put one unbreakable part at a time.
+     */
+    public class FootnotesProgress extends ProgressInfo {
+
+        /**
+         * Creates and initializes a new record.
+         */
+        public FootnotesProgress() {
+            super();
+        }
+
+        /**
+         * Creates a copy of the given record.
+         *
+         * @param footnotesProgress original progress information
+         */
+        public FootnotesProgress(FootnotesProgress footnotesProgress) {
+            super(footnotesProgress);
+        }
+
+        /**
+         * Checks whether there is some not yet typeset footnote content.
+         *
+         * @return <code>true</code> if the end of the footnote element list has not been
+         * reached yet
+         */
+        private boolean hasNext() {
+            return knuthSequences.size() > 0
+                    && (lastInsertedIndex < knuthSequences.size() - 1 || !endOfOutOfLine());
+        }
+
+        /**
+         * Insert footnote content on the current page up to the next legal break. This
+         * may be within a footnote or at the end of one.
+         */
+        private void next() {
+            if (lastInsertedIndex < 0 || endOfOutOfLine()) {
+                // Go to the next footnote
+                lastInsertedIndex++;
+                lastElementOfLastInsertedIndex = -1;
+                nbSplit = 1;
+            } else {  // We are still inside a footnote
+                nextInsideBreak();
+            }
+        }
+
+        /**
+         * If the last footnote of the previous page was split, places at least one more
+         * chunk of it on the current page. This would look very weird if the rest of a
+         * footnote split on one page would appear only two pages further. So this is
+         * necessary to put at least one chunk on the current page. If this leads to an
+         * unfeasible page, then the previous page will never appear in the optimal page
+         * layout anyway.
+         */
+        public void handleSplit() {
+            if (isLastSplit()) {
+                next();
+                addSeparator();
+            }
+        }
+
+        /**
+         * If the current page is a float-only page, handles the splitting of the last
+         * footnote of the previous page. Usually by adding at least a chunk of it on the
+         * current page, unless footnotes are not allowed on float-only pages (TODO this
+         * may lead to weird results (footnote continued only two pages further)).
+         *
+         * @param mode one of {@link PageBreakingAlgorithm#FLOAT_PAGE_MODE} or {@link
+         * PageBreakingAlgorithm#FLUSH_MODE}
+         * @param activeNodeRecorder
+         * @param normalContentProgress information about normal content already typeset
+         * @param beforeFloatsProgress information about before-floats already typeset
+         * @param previousNode node ending the previous page
+         */
+        public void handleSplit(int mode,
+                         PageBreakingAlgorithm.ActiveNodeRecorder activeNodeRecorder,
+                         PageBreakingAlgorithm.NormalContentProgressInfo normalContentProgress,
+                         BeforeFloatsRecord.BeforeFloatsProgress beforeFloatsProgress,
+                         KnuthPageNode previousNode) {
+            if (isLastSplit()) {
+                if (mode == PageBreakingAlgorithm.FLOAT_PAGE_MODE) {
+                    if (PageBreakingAlgorithm.FOOTNOTES_ALLOWED_ON_FLOAT_PAGES) {
+                        next();
+                        addSeparator();
+                        if (PageBreakingAlgorithm.FOOTNOTES_ONLY_PAGES_ALLOWED) {
+                            activeNodeRecorder.handleNode(mode, normalContentProgress,
+                                    this, beforeFloatsProgress, previousNode);
+                        }
+                    }
+                } else {  // mode == PageBreakingAlgorithm.FLUSH_MODE
+                    next();
+                    addSeparator();
+                    activeNodeRecorder.handleNode(mode, normalContentProgress,
+                            this, beforeFloatsProgress, previousNode);
+                }
+            }
+        }
+
+        /**
+         * Considers the placement of footnotes on the current page.
+         *
+         * @param mode one of {@link PageBreakingAlgorithm#NORMAL_MODE}, {@link
+         * PageBreakingAlgorithm#FLOAT_PAGE_MODE} or {@link
+         * PageBreakingAlgorithm#FLUSH_MODE}
+         * @param activeNodeRecorder
+         * @param normalContentProgress information about normal content already typeset
+         * @param beforeFloatsProgress information about before-floats already typeset
+         * @param previousNode node ending the previous page
+         */
+        public void consider(int mode,
+                             PageBreakingAlgorithm.ActiveNodeRecorder activeNodeRecorder,
+                             PageBreakingAlgorithm.NormalContentProgressInfo normalContentProgress,
+                             BeforeFloatsRecord.BeforeFloatsProgress beforeFloatsProgress,
+                             KnuthPageNode previousNode) {
+            beforeFloatsProgress.consider(mode, activeNodeRecorder,
+                    normalContentProgress, this, previousNode);
+            if (alreadyInserted.getLength() == 0) {
+                addSeparator();
+            }
+            switch (mode) {
+            case PageBreakingAlgorithm.NORMAL_MODE:
+                while (hasNext()) {
+                    next();
+                    if (!activeNodeRecorder.handleNode(mode, normalContentProgress,
+                            this, beforeFloatsProgress, previousNode)) {
+                        break;
+                    }
+                    beforeFloatsProgress.consider(mode, activeNodeRecorder,
+                            normalContentProgress, this, previousNode);
+                }
+                break;
+            case PageBreakingAlgorithm.FLOAT_PAGE_MODE:
+                if (PageBreakingAlgorithm.FOOTNOTES_ALLOWED_ON_FLOAT_PAGES) {
+                    while (hasNext()) {
+                        next();
+                        if (PageBreakingAlgorithm.FOOTNOTES_ONLY_PAGES_ALLOWED) {
+                            if (!activeNodeRecorder.handleNode(mode, normalContentProgress,
+                                    this, beforeFloatsProgress, previousNode)) {
+                                break;
+                            }
+                        }
+                        beforeFloatsProgress.consider(mode, activeNodeRecorder,
+                                normalContentProgress, this, previousNode);
+                    }
+                }
+                break;
+            case PageBreakingAlgorithm.FLUSH_MODE:
+                while (hasNext()) {
+                    next();
+                    if (!activeNodeRecorder.handleNode(mode, normalContentProgress,
+                            this, beforeFloatsProgress, previousNode)) {
+                        break;
+                    }
+                    beforeFloatsProgress.consider(mode, activeNodeRecorder,
+                            normalContentProgress, this, previousNode);
+                }
+                break;
+            }
+        }
+    }
+
+    /**
+     * Creates a new record for handling footnotes.
+     *
+     * @param footnoteSeparator dimensions of the separator between the normal content and
+     * the footnote area
+     */
+    public FootnotesRecord(MinOptMax footnoteSeparator) {
+        super(footnoteSeparator);
+    }
+}
diff --git a/src/java/org/apache/fop/layoutmgr/breaking/LineBreakPosition.java b/src/java/org/apache/fop/layoutmgr/breaking/LineBreakPosition.java
new file mode 100644 (file)
index 0000000..387d5ea
--- /dev/null
@@ -0,0 +1,51 @@
+package org.apache.fop.layoutmgr.breaking;
+
+import org.apache.fop.layoutmgr.LayoutManager;
+import org.apache.fop.layoutmgr.LeafPosition;
+
+/**
+ * Private class to store information about inline breaks.
+ * Each value holds the start and end indexes into a List of
+ * inline break positions.
+ */
+public class LineBreakPosition extends LeafPosition {
+    /*
+     * TODO vh: fields temporarily made public to ease the moving of
+     * LineBreakPosition from a LineLayoutManager inner class to a top-level
+     * class.
+     */
+    public int iParIndex; // index of the Paragraph this Position refers to
+    public int iStartIndex; //index of the first element this Position refers to
+    public int availableShrink;
+    public int availableStretch;
+    public int difference;
+    public double dAdjust; // Percentage to adjust (stretch or shrink)
+    public double ipdAdjust; // Percentage to adjust (stretch or shrink)
+    public int startIndent;
+    public int lineHeight;
+    public int lineWidth;
+    public int spaceBefore;
+    public int spaceAfter;
+    public int baseline;
+
+    LineBreakPosition(LayoutManager lm, int index, int iStartIndex, int iBreakIndex,
+                      int shrink, int stretch, int diff,
+                      double ipdA, double adjust, int ind,
+                      int lh, int lw, int sb, int sa, int bl) {
+        super(lm, iBreakIndex);
+        availableShrink = shrink;
+        availableStretch = stretch;
+        difference = diff;
+        iParIndex = index;
+        this.iStartIndex = iStartIndex;
+        ipdAdjust = ipdA;
+        dAdjust = adjust;
+        startIndent = ind;
+        lineHeight = lh;
+        lineWidth = lw;
+        spaceBefore = sb;
+        spaceAfter = sa;
+        baseline = bl;
+    }
+    
+}
\ No newline at end of file
diff --git a/src/java/org/apache/fop/layoutmgr/breaking/LineBreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/breaking/LineBreakingAlgorithm.java
new file mode 100644 (file)
index 0000000..e7e48a1
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * 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.ListIterator;
+
+import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.flow.Block;
+import org.apache.fop.layoutmgr.BreakingAlgorithm;
+import org.apache.fop.layoutmgr.KnuthElement;
+import org.apache.fop.layoutmgr.KnuthSequence;
+import org.apache.fop.layoutmgr.inline.AlignmentContext;
+import org.apache.fop.layoutmgr.inline.KnuthInlineBox;
+import org.apache.fop.layoutmgr.inline.LineLayoutManager;
+import org.apache.fop.layoutmgr.inline.LineLayoutManager.Paragraph;
+
+public class LineBreakingAlgorithm extends BreakingAlgorithm {
+    private LineLayoutManager thisLLM;
+    private int pageAlignment;
+    private int activePossibility;
+    private int addedPositions;
+    private int textIndent;
+    private int fillerMinWidth;
+    private int lineHeight;
+    private int lead;
+    private int follow;
+//    private int maxDiff;
+    private static final double MAX_DEMERITS = 10e6;
+
+    public LineBreakingAlgorithm (int pageAlign,
+                                  int textAlign, int textAlignLast,
+                                  int indent, int fillerWidth,
+                                  int lh, int ld, int fl, boolean first,
+                                  int maxFlagCount, LineLayoutManager llm) {
+        super(textAlign, textAlignLast, first, false, maxFlagCount);
+        pageAlignment = pageAlign;
+        textIndent = indent;
+        fillerMinWidth = fillerWidth;
+        lineHeight = lh;
+        lead = ld;
+        follow = fl;
+        thisLLM = llm;
+        activePossibility = -1;
+//        maxDiff = fobj.getWidows() >= fobj.getOrphans() 
+//                ? fobj.getWidows()
+//                : fobj.getOrphans();
+    }
+
+    public void updateData1(int lineCount, double demerits) {
+        thisLLM.getLineLayouts().addPossibility(lineCount, demerits);
+        log.trace("Layout possibility in " + lineCount + " lines; break at position:");
+    }
+
+    public void updateData2(KnuthNode bestActiveNode,
+                            KnuthSequence par,
+                            int total) {
+        // compute indent and adjustment ratio, according to
+        // the value of text-align and text-align-last
+        int indent = 0;
+        int difference = bestActiveNode.difference;
+        int textAlign = (bestActiveNode.line < total) ? alignment : alignmentLast;
+        indent += (textAlign == Constants.EN_CENTER)
+                  ? difference / 2 : (textAlign == Constants.EN_END) ? difference : 0;
+        indent += (bestActiveNode.line == 1 && bFirst && thisLLM.isFirstInBlock()) ? textIndent : 0;
+        double ratio = (textAlign == Constants.EN_JUSTIFY
+            || difference < 0 && -difference <= bestActiveNode.availableShrink)
+                    ? bestActiveNode.adjustRatio : 0;
+
+        // add nodes at the beginning of the list, as they are found
+        // backwards, from the last one to the first one
+
+        // the first time this method is called, initialize activePossibility
+        if (activePossibility == -1) {
+            activePossibility = 0;
+            addedPositions = 0;
+        }
+
+        if (addedPositions == thisLLM.getLineLayouts().getLineCount(activePossibility)) {
+            activePossibility++;
+            addedPositions = 0;
+        }
+
+        if (difference + bestActiveNode.availableShrink < 0) {
+            if (log.isWarnEnabled()) {
+                log.warn(FONode.decorateWithContextInfo(
+                        "Line " + (addedPositions + 1) 
+                        + " of a paragraph overflows the available area.", thisLLM.getFObj()));
+            }
+        }
+        
+        //log.debug("LLM> (" + (thisLLM.getLineLayouts().getLineNumber(activePossibility) - addedPositions) 
+        //    + ") difference = " + difference + " ratio = " + ratio);
+        thisLLM.getLineLayouts().addBreakPosition(makeLineBreakPosition(par,
+               (bestActiveNode.line > 1 ? bestActiveNode.previous.position + 1 : 0),
+               bestActiveNode.position,
+               bestActiveNode.availableShrink - (addedPositions > 0 
+                   ? 0 : ((Paragraph)par).getLineFiller().opt - ((Paragraph)par).getLineFiller().min), 
+               bestActiveNode.availableStretch, 
+               difference, ratio, indent), activePossibility);
+        addedPositions++;
+    }
+
+    /* reset activePossibility, as if breakpoints have not yet been computed
+     */
+    public void resetAlgorithm() {
+        activePossibility = -1;
+    }
+
+    private LineBreakPosition makeLineBreakPosition(KnuthSequence par,
+                                                    int firstElementIndex,
+                                                    int lastElementIndex,
+                                                    int availableShrink, 
+                                                    int availableStretch, 
+                                                    int difference,
+                                                    double ratio,
+                                                    int indent) {
+        // line height calculation - spaceBefore may differ from spaceAfter
+        // by 1mpt due to rounding
+        int spaceBefore = (lineHeight - lead - follow) / 2;
+        int spaceAfter = lineHeight - lead - follow - spaceBefore;
+        // height before the main baseline
+        int lineLead = lead;
+        // maximum follow 
+        int lineFollow = follow;
+        // true if this line contains only zero-height, auxiliary boxes
+        // and the actual line width is 0; in this case, the line "collapses"
+        // i.e. the line area will have bpd = 0
+        boolean bZeroHeightLine = (difference == thisLLM.getLineWidth());
+
+        // if line-stacking-strategy is "font-height", the line height
+        // is not affected by its content
+        if (((Block) thisLLM.getFObj()).getLineStackingStrategy() != Constants.EN_FONT_HEIGHT) {
+            ListIterator inlineIterator
+                = par.listIterator(firstElementIndex);
+            AlignmentContext lastAC = null;
+            int maxIgnoredHeight = 0; // See spec 7.13
+            for (int j = firstElementIndex;
+                 j <= lastElementIndex;
+                 j++) {
+                KnuthElement element = (KnuthElement) inlineIterator.next();
+                if (element instanceof KnuthInlineBox ) {
+                    AlignmentContext ac = ((KnuthInlineBox) element).getAlignmentContext();
+                    if (ac != null && lastAC != ac) {
+                        if (!ac.usesInitialBaselineTable()
+                            || ac.getAlignmentBaselineIdentifier() != Constants.EN_BEFORE_EDGE
+                               && ac.getAlignmentBaselineIdentifier() != Constants.EN_AFTER_EDGE) {
+                            int alignmentOffset = ac.getTotalAlignmentBaselineOffset();
+                            if (alignmentOffset + ac.getAltitude() > lineLead) {
+                                lineLead = alignmentOffset + ac.getAltitude();
+                            }
+                            if (ac.getDepth() - alignmentOffset > lineFollow)  {
+                                lineFollow = ac.getDepth() - alignmentOffset;
+                            }
+                        } else {
+                            if (ac.getHeight() > maxIgnoredHeight) {
+                                maxIgnoredHeight = ac.getHeight();
+                            }
+                        }
+                        lastAC = ac;
+                    }
+                    if (bZeroHeightLine
+                        && (!element.isAuxiliary() || ac != null && ac.getHeight() > 0)) {
+                        bZeroHeightLine = false;
+                    }
+                }
+            }
+
+            if (lineFollow < maxIgnoredHeight - lineLead) {
+                lineFollow = maxIgnoredHeight - lineLead;
+            }
+        }
+
+        thisLLM.setConstantLineHeight(lineLead + lineFollow);
+
+        if (bZeroHeightLine) {
+            return new LineBreakPosition(thisLLM,
+                                         thisLLM.getKnuthParagraphs().indexOf(par),
+                                         firstElementIndex, lastElementIndex,
+                                         availableShrink, availableStretch,
+                                         difference, ratio, 0, indent,
+                                         0, thisLLM.getLineWidth(), 0, 0, 0);
+        } else {
+            return new LineBreakPosition(thisLLM,
+                                         thisLLM.getKnuthParagraphs().indexOf(par),
+                                         firstElementIndex, lastElementIndex,
+                                         availableShrink, availableStretch,
+                                         difference, ratio, 0, indent,
+                                         lineLead + lineFollow, 
+                                         thisLLM.getLineWidth(), spaceBefore, spaceAfter,
+                                         lineLead);
+        }
+    }
+
+    public int findBreakingPoints(Paragraph par, /*int lineWidth,*/
+                                  double threshold, boolean force,
+                                  int allowedBreaks) {
+        return super.findBreakingPoints(par, /*lineWidth,*/ 
+                threshold, force, allowedBreaks);
+    }
+
+    protected int filterActiveNodes() {
+        KnuthNode bestActiveNode = null;
+
+        if (pageAlignment == Constants.EN_JUSTIFY) {
+            // leave all active nodes and find the optimum line number
+            //log.debug("LBA.filterActiveNodes> " + activeNodeCount + " layouts");
+            for (int i = startLine; i < endLine; i++) {
+                for (KnuthNode node = getNode(i); node != null; node = node.next) {
+                    //log.debug("                       + lines = " + node.line + " demerits = " + node.totalDemerits);
+                    bestActiveNode = compareNodes(bestActiveNode, node);
+                }
+            }
+
+            // scan the node set once again and remove some nodes
+            //log.debug("LBA.filterActiveList> layout selection");
+            for (int i = startLine; i < endLine; i++) {
+                for (KnuthNode node = getNode(i); node != null; node = node.next) {
+                    //if (Math.abs(node.line - bestActiveNode.line) > maxDiff) {
+                    //if (false) {
+                    if (node.line != bestActiveNode.line
+                        && node.totalDemerits > MAX_DEMERITS) {
+                        //log.debug("                     XXX lines = " + node.line + " demerits = " + node.totalDemerits);
+                        deactivateNode(node);
+                    } else {
+                        //log.debug("                      ok lines = " + node.line + " demerits = " + node.totalDemerits);
+                    }
+                }
+            }
+        } else {
+            // leave only the active node with fewest total demerits
+            for (int i = startLine; i < endLine; i++) {
+                for (KnuthNode node = getNode(i); node != null; node = node.next) {
+                    bestActiveNode = compareNodes(bestActiveNode, node);
+                    if (node != bestActiveNode) {
+                        deactivateNode(node);
+                    }
+                }
+            }
+        }
+        return bestActiveNode.line;
+    }
+}
\ No newline at end of file
index 53e8805527be2502feef0ef8a477115a32ffbe41..7df760e0b7c2f66641054a08072ff2d31211968e 100644 (file)
 package org.apache.fop.layoutmgr.breaking;
 
 import java.util.ArrayList;
-import java.util.LinkedList;
+import java.util.Iterator;
 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.
+     * During page breaking, records which out-of-line objects have already been handled,
+     * and how much of them are placed on the current page.
      */
-    public static class ProgressInfo {
-
-        /** Cumulated BPD length of all out-of-line objects inserted so far. */
-        private int alreadyInsertedLength = 0;
+    class ProgressInfo {
 
         /** Index of the last inserted out-of-line object. */
-        private int lastInsertedIndex = -1;
+        int lastInsertedIndex;
 
         /**
          * 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.
+         * object.
+         */
+        int lastElementOfLastInsertedIndex;
+
+        /**
+         * Amount of out-of-lines put on the current page.
          */
-        private int lastElementOfLastInsertedIndex = -1;
-        
+        ElasticLength alreadyInserted = new ElasticLength();
+
         /**
-         * Initializes this record, as if no out-of-line object were handled yet.
+         * Number of times the last out-of-line is split. The initial value is 1, and is
+         * reset each time the end of an out-of-line is reached. The purpose is to compute
+         * additional demerits for split out-of-lines.
          */
-        private void initialize() {
-            alreadyInsertedLength = 0;
-            lastInsertedIndex = -1;
-            lastElementOfLastInsertedIndex = -1;
+        int nbSplit;
+
+        /**
+         * Creates and initializes a new record.
+         *
+         * @see OutOfLineRecord#initialize()
+         */
+        ProgressInfo() {
+            initialize();
         }
 
         /**
-         * @return a copy of this record
+         * Creates a copy of the given record.
+         *
+         * @param progressInfo original record
          */
-        public ProgressInfo copy() {
-            ProgressInfo info = new ProgressInfo();
-            info.alreadyInsertedLength = alreadyInsertedLength;
-            info.lastInsertedIndex = lastInsertedIndex;
-            info.lastElementOfLastInsertedIndex = lastElementOfLastInsertedIndex;
-            return info;
+        ProgressInfo(ProgressInfo progressInfo) {
+            this.lastInsertedIndex = progressInfo.lastInsertedIndex;
+            this.lastElementOfLastInsertedIndex = progressInfo.lastElementOfLastInsertedIndex;
+            this.alreadyInserted.set(progressInfo.alreadyInserted);
+            this.nbSplit = progressInfo.nbSplit;
         }
 
         /**
-         * Returns the cumulated length of all already typeset out-of-line objects.
-         * @return the total length in the block-progression-direction
+         * Returns the amount of out-of-lines inserted on the current page.
+         *
+         * @return the amount of out-of-lines inserted on the current page
          */
-        public int getAlreadyInsertedLength() {
-            return alreadyInsertedLength;
+        public ElasticLength getInserted() {
+            return alreadyInserted;
         }
 
         /**
-         * Returns the index of the last element of the last already typeset out-of-line
-         * object.
-         * @return the index of the last placed KnuthElement
+         * Returns the number of times the last out-of-line is split.
+         *
+         * @return the number of times the last out-of-line is split
+         */
+        public int getNbSplit() {
+            return nbSplit;
+        }
+
+        /**
+         * Returns the index of the last element of the last inserted out-of-line.
+         *
+         * @return the index of the last element of the last inserted out-of-line
          */
         public int getLastElementOfLastInsertedIndex() {
             return lastElementOfLastInsertedIndex;
         }
 
         /**
-         * @return the index of the last already typeset out-of-line object.
+         * Returns the index of the last inserted out-of-line.
+         *
+         * @return the index of the last inserted out-of-line
          */
         public int getLastInsertedIndex() {
             return lastInsertedIndex;
         }
 
-        public String toString() {
-            return "length=" + alreadyInsertedLength
-                    + ", index=" + lastInsertedIndex
-                    + ", elt=" + lastElementOfLastInsertedIndex;
+        /**
+         * Initializes this record such that no out-of-line has been inserted yet.
+         */
+        public void initialize() {
+            lastInsertedIndex = -1;
+            lastElementOfLastInsertedIndex = -1;
+            alreadyInserted.reset();
+            nbSplit = 1;
         }
-    }
-
-    /**
-     * 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;
+        /**
+         * Records progress status for out-of-lines up to the previous page.
+         *
+         * @param info progress informations for the previous page
+         */
+        public void setPrevious(ProgressInfo info) {
+            lastInsertedIndex = info.lastInsertedIndex;
+            lastElementOfLastInsertedIndex = info.lastElementOfLastInsertedIndex;
+            if (lastInsertedIndex >= 0) {
+                List lastOutOfLine = ((List) knuthSequences.get(lastInsertedIndex));
+                // If the last out-of-line was split, go just before the first next box
+                while (lastElementOfLastInsertedIndex < lastOutOfLine.size() - 2
+                        && !((KnuthElement) lastOutOfLine.get(lastElementOfLastInsertedIndex + 1)).isBox()) {
+                    lastElementOfLastInsertedIndex++;
+                }
+                if (lastElementOfLastInsertedIndex < lastOutOfLine.size() - 1) {
+                    // We haven't reached the end of the out-of-line yet
+                    nbSplit = info.nbSplit + 1;
+                } else {
+                    nbSplit = 1;
+                }
+            }
+            alreadyInserted.reset();
+        }
 
-    /**
-     * 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;
+        /**
+         * Checks whether there are still out-of-line objects to be placed.
+         *
+         * @return <code>true</code> if not all out-of-lines have been placed yet,
+         * otherwise <code>false</code>
+         */
+        public boolean remaining() {
+            return (lastInsertedIndex < knuthSequences.size() - 1) || isLastSplit();
+        }
 
-    /**
-     * Index of the first newly encountered out-of-line object since the last legal
-     * breakpoint.
-     * 
-     * @see OutOfLineRecord#knuthSequences
-     */
-    private int firstNewIndex = 0;
+        /**
+         * Returns the number of still to-be-placed out-of-lines.
+         * @return the number of not yet typeset out-of-line objects.
+         */
+        public int getNbOfDeferred() {
+            return knuthSequences.size() - 1 - lastInsertedIndex;
+        }
 
-    /**
-     * Dimension in the BPD of the separator between the out-of-line area and the main
-     * area.
-     */
-    private MinOptMax separatorLength = null;
+        /**
+         * Checks whether the end of the current out-of-line has been reached.
+         *
+         * @return <code>true</code> if the whole out-of-line has been placed, otherwise
+         * <code>false</code>
+         */
+        boolean endOfOutOfLine() {
+            return lastElementOfLastInsertedIndex
+                    >= ((List) knuthSequences.get(lastInsertedIndex)).size() - 1;
+        }
 
-    /**
-     * Record of already handled out-of-line objects.
-     * 
-     * @see ProgressInfo
-     */
-    private ProgressInfo progressInfo;
+        /**
+         * Checks whether the last out-of-line placed on the current page must be split or
+         * not.
+         *
+         * @return <code>true</code> if the last typeset out-of-line object must be split on
+         * several pages.
+         */
+        public boolean isLastSplit() {
+            return lastInsertedIndex >= 0 && !endOfOutOfLine();
+        }
 
-    public OutOfLineRecord(MinOptMax separatorLength) {
-        this.separatorLength = separatorLength;
-        this.progressInfo = new ProgressInfo();
-    }
+        /**
+         * Adds the dimensions of the separator between the out-of-line area and the main
+         * content to the amount of out-of-lines already placed on the current page.
+         */
+        public void addSeparator() {
+            alreadyInserted.add(separator);
+        }
 
-    /**
-     * 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();
-    }
+        /**
+         * Places on the current page out-of-line content up to the next legal break in the current
+         * out-of-line. This method is meant to be called by subclasses of this class, not
+         * by external classes even in the same package.
+         * <p><strong>Pre-condition:</strong> it is supposed that we are <em>inside</em>
+         * the out-of-line, and that we haven't reached its end yet.
+         */
+        void nextInsideBreak() {
+            List knuthSequence = (List) knuthSequences.get(lastInsertedIndex);
+            Iterator elementIter = knuthSequence.listIterator(lastElementOfLastInsertedIndex + 1);
+            boolean prevIsBox = false;
+            do {
+                lastElementOfLastInsertedIndex++;
+                KnuthElement element = (KnuthElement) elementIter.next();
+                if (element.isBox()) {
+                    alreadyInserted.add(0, element.getW(), 0);
+                    prevIsBox = true;
+                } else if (element.isGlue()) {
+                    if (prevIsBox) {
+                        break;
+                    }
+                    alreadyInserted.add(element.getZ(), element.getW(), element.getY());
+                    prevIsBox = false;
+                } else {
+                    if (element.getP() < KnuthElement.INFINITE) {
+                        alreadyInserted.add(0, element.getW(), 0);
+                        break;
+                    }
+                    prevIsBox = false;
+                }
+            } while (lastElementOfLastInsertedIndex < knuthSequence.size() - 1);
+            if (lastElementOfLastInsertedIndex == knuthSequence.size() - 1) {
+                nbSplit = 0;
+            }
+        }
 
-    /**
-     * @return the informations about already handled out-of-line objects
-     */
-    public ProgressInfo getProgress() {
-        return this.progressInfo;
+        public String toString() {
+            return "index=" + lastInsertedIndex
+                    + ", elt=" + lastElementOfLastInsertedIndex
+                    + ", inserted=" + alreadyInserted
+                    + ", splits=" + nbSplit;
+        }
     }
 
     /**
-     * @return the length in the BPD of the separator between the out-of-line area and the
-     * main area.
+     * Sequences of KnuthElement corresponding to already encountered out-of-line objects.
+     * This is a List of List of KnuthElement.
      */
-    public MinOptMax getSeparatorLength() {
-        return separatorLength;
-    }
+    List knuthSequences = new ArrayList();
 
     /**
-     * @return the total length of already encountered out-of-line objects
+     * Dimension in the BPD of the separator between the out-of-line area and the main
+     * area.
      */
-    public int getTotalLength() {
-        if (cumulativeLengths == null || cumulativeLengths.size() == 0) {
-            return 0;
-        } else {
-            return ((Integer) cumulativeLengths.get(cumulativeLengths.size() - 1)).intValue(); 
-        }
-    }
+    private ElasticLength separator = null;
 
     /**
-     * @return true if out-of-line objects have already been encountered (but not
-     * necessarily typeset yet)
+     * Creates a new record for a given type of out-of-lines.
+     *
+     * @param separator dimensions of the separator between the out-of-line area and the
+     * main area
      */
-    public boolean existing() {
-        return (knuthSequences != null && knuthSequences.size() > 0);
-    }
-
-    public void resetNewSinceLastBreakpoint() {
-        newSinceLastBreakpoint = false;
+    public OutOfLineRecord(MinOptMax separator) {
+        this.separator = new ElasticLength(separator.opt - separator.min,
+                                           separator.opt,
+                                           separator.max - separator.opt);
     }
 
     /**
-     * @return true if new out-of-line objects are cited in the sequence of Knuth
-     * elements since the last encountered legal breakpoint.
+     * Initializes this record, as if no out-of-line object were handled yet.
      */
-    public boolean newSinceLastBreakpoint() {
-        return newSinceLastBreakpoint;
+    public void initialize() {
+        knuthSequences = new ArrayList();
     }
 
     /**
      * Records one or more newly encountered out-of-line objects.
-     * @param elementLists the list of corresponding Knuth sequences
+     *
+     * @param elementLists the list of corresponding Knuth sequences. This is a
+     * List&lt;List&lt;KnuthElement&gt;&gt;.
      */
     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;
-    }
+            List knuthSequence = (List) elementListsIterator.next();
 
-    /* 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;
-//  }
+            // Space resolution (Note: this does not respect possible stacking constraints
+            // /between/ out-of-lines!)
+            SpaceResolver.resolveElementList(knuthSequence);
 
-    /**
-     * @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;
+            knuthSequences.add(knuthSequence);
+        }
     }
 
     /**
-     * @return <code>true</code> if the last typeset out-of-line object must be split on
-     * several pages.
+     * Returns <code>true</code> if out-of-lines have already been encountered.
+     *
+     * @return <code>true</code> if out-of-lines are recorded, possibly not yet typeset
      */
-    public boolean isSplit() {
-        return (progressInfo.lastElementOfLastInsertedIndex 
-                < ((LinkedList) knuthSequences.get(progressInfo.lastInsertedIndex)).size() - 1);
+    public boolean existing() {
+        return knuthSequences.size() > 0;
     }
 
     /**
@@ -318,284 +315,19 @@ public class OutOfLineRecord {
      * it does not exist
      */
     public List getSequence(int index) {
-        /*TODO vh: bof */
-        if (knuthSequences == null) {
-            return null;
-        } else {
-            return (List) knuthSequences.get(index);
-        }
+        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.
+     * and thus must be removed from the list of already encountered 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);
     }
 }
index aada86f8f0a01c0fe3f1f29dde7e848f98ef0e25..4f57e5bb61474f0bb6d98148490878fd29419556 100644 (file)
@@ -47,6 +47,8 @@ import org.apache.fop.layoutmgr.NonLeafPosition;
 import org.apache.fop.layoutmgr.Position;
 import org.apache.fop.layoutmgr.PositionIterator;
 import org.apache.fop.layoutmgr.SpaceSpecifier;
+import org.apache.fop.layoutmgr.breaking.LineBreakPosition;
+import org.apache.fop.layoutmgr.breaking.LineBreakingAlgorithm;
 import org.apache.fop.area.Area;
 import org.apache.fop.area.LineArea;
 import org.apache.fop.area.inline.InlineArea;
@@ -100,49 +102,6 @@ public class LineLayoutManager extends InlineStackingLayoutManager
         }
     }
     
-    /**
-     * Private class to store information about inline breaks.
-     * Each value holds the start and end indexes into a List of
-     * inline break positions.
-     */
-    private static class LineBreakPosition extends LeafPosition {
-        private int iParIndex; // index of the Paragraph this Position refers to
-        private int iStartIndex; //index of the first element this Position refers to
-        private int availableShrink;
-        private int availableStretch;
-        private int difference;
-        private double dAdjust; // Percentage to adjust (stretch or shrink)
-        private double ipdAdjust; // Percentage to adjust (stretch or shrink)
-        private int startIndent;
-        private int lineHeight;
-        private int lineWidth;
-        private int spaceBefore;
-        private int spaceAfter;
-        private int baseline;
-
-        LineBreakPosition(LayoutManager lm, int index, int iStartIndex, int iBreakIndex,
-                          int shrink, int stretch, int diff,
-                          double ipdA, double adjust, int ind,
-                          int lh, int lw, int sb, int sa, int bl) {
-            super(lm, iBreakIndex);
-            availableShrink = shrink;
-            availableStretch = stretch;
-            difference = diff;
-            iParIndex = index;
-            this.iStartIndex = iStartIndex;
-            ipdAdjust = ipdA;
-            dAdjust = adjust;
-            startIndent = ind;
-            lineHeight = lh;
-            lineWidth = lw;
-            spaceBefore = sb;
-            spaceAfter = sa;
-            baseline = bl;
-        }
-        
-    }
-
-
     private int textAlignment = EN_JUSTIFY;
     private int textAlignmentLast;
     private int effectiveAlignment;
@@ -195,7 +154,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager
     }
 
     // this class represents a paragraph
-    private class Paragraph extends InlineKnuthSequence {
+    public class Paragraph extends InlineKnuthSequence {
         /** Number of elements to ignore at the beginning of the list. */ 
         private int ignoreAtStart = 0;
         /** Number of elements to ignore at the end of the list. */
@@ -221,6 +180,10 @@ public class LineLayoutManager extends InlineStackingLayoutManager
             lastLineEndIndent = endIndent;
         }
 
+        public MinOptMax getLineFiller() {
+            return lineFiller;
+        }
+
         public void startParagraph(int lw) {
             lineWidth = lw;
             startSequence();
@@ -307,233 +270,6 @@ public class LineLayoutManager extends InlineStackingLayoutManager
         }
     }
 
-    private class LineBreakingAlgorithm extends BreakingAlgorithm {
-        private LineLayoutManager thisLLM;
-        private int pageAlignment;
-        private int activePossibility;
-        private int addedPositions;
-        private int textIndent;
-        private int fillerMinWidth;
-        private int lineHeight;
-        private int lead;
-        private int follow;
-        private int maxDiff;
-        private static final double MAX_DEMERITS = 10e6;
-
-        public LineBreakingAlgorithm (int pageAlign,
-                                      int textAlign, int textAlignLast,
-                                      int indent, int fillerWidth,
-                                      int lh, int ld, int fl, boolean first,
-                                      int maxFlagCount, LineLayoutManager llm) {
-            super(textAlign, textAlignLast, first, false, maxFlagCount);
-            pageAlignment = pageAlign;
-            textIndent = indent;
-            fillerMinWidth = fillerWidth;
-            lineHeight = lh;
-            lead = ld;
-            follow = fl;
-            thisLLM = llm;
-            activePossibility = -1;
-            maxDiff = fobj.getWidows() >= fobj.getOrphans() 
-                    ? fobj.getWidows()
-                    : fobj.getOrphans();
-        }
-
-        public void updateData1(int lineCount, double demerits) {
-            lineLayouts.addPossibility(lineCount, demerits);
-            log.trace("Layout possibility in " + lineCount + " lines; break at position:");
-        }
-
-        public void updateData2(KnuthNode bestActiveNode,
-                                KnuthSequence par,
-                                int total) {
-            // compute indent and adjustment ratio, according to
-            // the value of text-align and text-align-last
-            int indent = 0;
-            int difference = bestActiveNode.difference;
-            int textAlign = (bestActiveNode.line < total) ? alignment : alignmentLast;
-            indent += (textAlign == Constants.EN_CENTER)
-                      ? difference / 2 : (textAlign == Constants.EN_END) ? difference : 0;
-            indent += (bestActiveNode.line == 1 && bFirst && isFirstInBlock) ? textIndent : 0;
-            double ratio = (textAlign == Constants.EN_JUSTIFY
-                || difference < 0 && -difference <= bestActiveNode.availableShrink)
-                        ? bestActiveNode.adjustRatio : 0;
-
-            // add nodes at the beginning of the list, as they are found
-            // backwards, from the last one to the first one
-
-            // the first time this method is called, initialize activePossibility
-            if (activePossibility == -1) {
-                activePossibility = 0;
-                addedPositions = 0;
-            }
-
-            if (addedPositions == lineLayouts.getLineCount(activePossibility)) {
-                activePossibility++;
-                addedPositions = 0;
-            }
-
-            if (difference + bestActiveNode.availableShrink < 0) {
-                if (log.isWarnEnabled()) {
-                    log.warn(FONode.decorateWithContextInfo(
-                            "Line " + (addedPositions + 1) 
-                            + " of a paragraph overflows the available area.", getFObj()));
-                }
-            }
-            
-            //log.debug("LLM> (" + (lineLayouts.getLineNumber(activePossibility) - addedPositions) 
-            //    + ") difference = " + difference + " ratio = " + ratio);
-            lineLayouts.addBreakPosition(makeLineBreakPosition(par,
-                   (bestActiveNode.line > 1 ? bestActiveNode.previous.position + 1 : 0),
-                   bestActiveNode.position,
-                   bestActiveNode.availableShrink - (addedPositions > 0 
-                       ? 0 : ((Paragraph)par).lineFiller.opt - ((Paragraph)par).lineFiller.min), 
-                   bestActiveNode.availableStretch, 
-                   difference, ratio, indent), activePossibility);
-            addedPositions++;
-        }
-
-        /* reset activePossibility, as if breakpoints have not yet been computed
-         */
-        public void resetAlgorithm() {
-            activePossibility = -1;
-        }
-
-        private LineBreakPosition makeLineBreakPosition(KnuthSequence par,
-                                                        int firstElementIndex,
-                                                        int lastElementIndex,
-                                                        int availableShrink, 
-                                                        int availableStretch, 
-                                                        int difference,
-                                                        double ratio,
-                                                        int indent) {
-            // line height calculation - spaceBefore may differ from spaceAfter
-            // by 1mpt due to rounding
-            int spaceBefore = (lineHeight - lead - follow) / 2;
-            int spaceAfter = lineHeight - lead - follow - spaceBefore;
-            // height before the main baseline
-            int lineLead = lead;
-            // maximum follow 
-            int lineFollow = follow;
-            // true if this line contains only zero-height, auxiliary boxes
-            // and the actual line width is 0; in this case, the line "collapses"
-            // i.e. the line area will have bpd = 0
-            boolean bZeroHeightLine = (difference == iLineWidth);
-
-            // if line-stacking-strategy is "font-height", the line height
-            // is not affected by its content
-            if (fobj.getLineStackingStrategy() != EN_FONT_HEIGHT) {
-                ListIterator inlineIterator
-                    = par.listIterator(firstElementIndex);
-                AlignmentContext lastAC = null;
-                int maxIgnoredHeight = 0; // See spec 7.13
-                for (int j = firstElementIndex;
-                     j <= lastElementIndex;
-                     j++) {
-                    KnuthElement element = (KnuthElement) inlineIterator.next();
-                    if (element instanceof KnuthInlineBox ) {
-                        AlignmentContext ac = ((KnuthInlineBox) element).getAlignmentContext();
-                        if (ac != null && lastAC != ac) {
-                            if (!ac.usesInitialBaselineTable()
-                                || ac.getAlignmentBaselineIdentifier() != EN_BEFORE_EDGE
-                                   && ac.getAlignmentBaselineIdentifier() != EN_AFTER_EDGE) {
-                                int alignmentOffset = ac.getTotalAlignmentBaselineOffset();
-                                if (alignmentOffset + ac.getAltitude() > lineLead) {
-                                    lineLead = alignmentOffset + ac.getAltitude();
-                                }
-                                if (ac.getDepth() - alignmentOffset > lineFollow)  {
-                                    lineFollow = ac.getDepth() - alignmentOffset;
-                                }
-                            } else {
-                                if (ac.getHeight() > maxIgnoredHeight) {
-                                    maxIgnoredHeight = ac.getHeight();
-                                }
-                            }
-                            lastAC = ac;
-                        }
-                        if (bZeroHeightLine
-                            && (!element.isAuxiliary() || ac != null && ac.getHeight() > 0)) {
-                            bZeroHeightLine = false;
-                        }
-                    }
-                }
-
-                if (lineFollow < maxIgnoredHeight - lineLead) {
-                    lineFollow = maxIgnoredHeight - lineLead;
-                }
-            }
-
-            constantLineHeight = lineLead + lineFollow;
-
-            if (bZeroHeightLine) {
-                return new LineBreakPosition(thisLLM,
-                                             knuthParagraphs.indexOf(par),
-                                             firstElementIndex, lastElementIndex,
-                                             availableShrink, availableStretch,
-                                             difference, ratio, 0, indent,
-                                             0, iLineWidth, 0, 0, 0);
-            } else {
-                return new LineBreakPosition(thisLLM,
-                                             knuthParagraphs.indexOf(par),
-                                             firstElementIndex, lastElementIndex,
-                                             availableShrink, availableStretch,
-                                             difference, ratio, 0, indent,
-                                             lineLead + lineFollow, 
-                                             iLineWidth, spaceBefore, spaceAfter,
-                                             lineLead);
-            }
-        }
-
-        public int findBreakingPoints(Paragraph par, /*int lineWidth,*/
-                                      double threshold, boolean force,
-                                      int allowedBreaks) {
-            return super.findBreakingPoints(par, /*lineWidth,*/ 
-                    threshold, force, allowedBreaks);
-        }
-
-        protected int filterActiveNodes() {
-            KnuthNode bestActiveNode = null;
-
-            if (pageAlignment == EN_JUSTIFY) {
-                // leave all active nodes and find the optimum line number
-                //log.debug("LBA.filterActiveNodes> " + activeNodeCount + " layouts");
-                for (int i = startLine; i < endLine; i++) {
-                    for (KnuthNode node = getNode(i); node != null; node = node.next) {
-                        //log.debug("                       + lines = " + node.line + " demerits = " + node.totalDemerits);
-                        bestActiveNode = compareNodes(bestActiveNode, node);
-                    }
-                }
-
-                // scan the node set once again and remove some nodes
-                //log.debug("LBA.filterActiveList> layout selection");
-                for (int i = startLine; i < endLine; i++) {
-                    for (KnuthNode node = getNode(i); node != null; node = node.next) {
-                        //if (Math.abs(node.line - bestActiveNode.line) > maxDiff) {
-                        //if (false) {
-                        if (node.line != bestActiveNode.line
-                            && node.totalDemerits > MAX_DEMERITS) {
-                            //log.debug("                     XXX lines = " + node.line + " demerits = " + node.totalDemerits);
-                            removeNode(i, node);
-                        } else {
-                            //log.debug("                      ok lines = " + node.line + " demerits = " + node.totalDemerits);
-                        }
-                    }
-                }
-            } else {
-                // leave only the active node with fewest total demerits
-                for (int i = startLine; i < endLine; i++) {
-                    for (KnuthNode node = getNode(i); node != null; node = node.next) {
-                        bestActiveNode = compareNodes(bestActiveNode, node);
-                        if (node != bestActiveNode) {
-                            removeNode(i, node);
-                        }
-                    }
-                }
-            }
-            return bestActiveNode.line;
-        }
-    }
-
       
     private int constantLineHeight = 12000;
       
@@ -558,6 +294,26 @@ public class LineLayoutManager extends InlineStackingLayoutManager
         follow = f;
     }
 
+    public LineLayoutPossibilities getLineLayouts() {
+        return lineLayouts;
+    }
+
+    public boolean isFirstInBlock() {
+        return isFirstInBlock;
+    }
+
+    public int getLineWidth() {
+        return iLineWidth;
+    }
+
+    public void setConstantLineHeight(int constantLineHeight) {
+        this.constantLineHeight = constantLineHeight;
+    }
+
+    public List getKnuthParagraphs() {
+        return knuthParagraphs;
+    }
+
     /** @see org.apache.fop.layoutmgr.LayoutManager */
     public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
         Font fs = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo(), this);
index 6e647353e7d37e8fc451066afc8253d75a23bcfa..41ab67dcd9d682d190cf5eae000becafaa5f60ce 100644 (file)
@@ -30,6 +30,7 @@ import org.apache.fop.layoutmgr.PositionIterator;
 import org.apache.fop.layoutmgr.Position;
 import org.apache.fop.layoutmgr.NonLeafPosition;
 import org.apache.fop.layoutmgr.TraitSetter;
+import org.apache.fop.layoutmgr.SpaceResolver.SpaceHandlingBreakPosition;
 import org.apache.fop.area.Area;
 import org.apache.fop.area.Block;
 
@@ -145,6 +146,8 @@ public class ListItemContentLayoutManager extends BlockStackingLayoutManager {
                 if (firstLM == null) {
                     firstLM = lastLM;
                 }
+            } else if (pos instanceof SpaceHandlingBreakPosition) {
+                positionList.add(pos);
             } else {
                 // pos was created by this ListBlockLM, so it must be ignored
             }
index 6bcbf4de08d01d20727245ff0a5dc5894f1f36e7..336d34f30b04ec553293679f9c992b3ff6f2d43b 100644 (file)
@@ -288,7 +288,21 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager
             int penaltyHeight = step 
                 + getMaxRemainingHeight(fullHeights, partialHeights) 
                 - totalHeight;
+            
+            //Additional penalty height from penalties in the source lists
+            int additionalPenaltyHeight = 0;
+            KnuthElement endEl = (KnuthElement)elementLists[0].get(end[0]);
+            if (endEl instanceof KnuthPenalty) {
+                additionalPenaltyHeight = ((KnuthPenalty)endEl).getW();
+            }
+            endEl = (KnuthElement)elementLists[1].get(end[1]);
+            if (endEl instanceof KnuthPenalty) {
+                additionalPenaltyHeight = Math.max(
+                        additionalPenaltyHeight, ((KnuthPenalty)endEl).getW());
+            }
+            
             int boxHeight = step - addedBoxHeight - penaltyHeight;
+            penaltyHeight += additionalPenaltyHeight; //Add AFTER calculating boxHeight!
 
             // add the new elements
             addedBoxHeight += boxHeight;
@@ -300,7 +314,6 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager
                 if (keepWithNextActive || mustKeepTogether()) {
                     p = KnuthPenalty.INFINITE;
                 }
-                //returnList.add(new KnuthPenalty(penaltyHeight, p, false, stepPosition, false));
                 returnList.add(new BreakElement(stepPosition, penaltyHeight, p, -1, context));
             }
         }
index 27f3da0c94bd7f44b572930717985e025a310480..1c3045e52946be71f9041b7cb67805161a5beb15 100644 (file)
@@ -77,6 +77,8 @@ public class TableContentLayoutManager implements PercentBaseContext {
     private int startXOffset;
     private int usedBPD;
     
+    private TableStepper stepper = new TableStepper(this);
+        
     /**
      * Main constructor
      * @param parent Parent layout manager
@@ -144,15 +146,18 @@ public class TableContentLayoutManager implements PercentBaseContext {
             this.headerList = getKnuthElementsForRowIterator(
                     headerIter, context, alignment, TableRowIterator.HEADER);
             ElementListUtils.removeLegalBreaks(this.headerList);
-            this.headerNetHeight = ElementListUtils.calcContentLength(this.headerList);
+            this.headerNetHeight = 
+                ElementListUtils.calcContentLength(this.headerList);
             if (log.isDebugEnabled()) {
-                log.debug("==> Header: " + headerNetHeight + " - " + this.headerList);
+                log.debug("==> Header: " 
+                        + headerNetHeight + " - " + this.headerList);
             }
             TableHeaderFooterPosition pos = new TableHeaderFooterPosition(
                     getTableLM(), true, this.headerList);
             KnuthBox box = new KnuthBox(headerNetHeight, pos, false);
             if (getTableLM().getTable().omitHeaderAtBreak()) {
-                //We can simply add the table header at the beginning of the whole list
+                //We can simply add the table header at the start 
+                //of the whole list
                 headerAsFirst = box;
             } else {
                 headerAsSecondToLast = box;
@@ -162,17 +167,17 @@ public class TableContentLayoutManager implements PercentBaseContext {
             this.footerList = getKnuthElementsForRowIterator(
                     footerIter, context, alignment, TableRowIterator.FOOTER);
             ElementListUtils.removeLegalBreaks(this.footerList);
-            this.footerNetHeight = ElementListUtils.calcContentLength(this.footerList);
+            this.footerNetHeight = 
+                    ElementListUtils.calcContentLength(this.footerList);
             if (log.isDebugEnabled()) {
-                log.debug("==> Footer: " + footerNetHeight + " - " + this.footerList);
-            }
-            if (true /*getTableLM().getTable().omitFooterAtBreak()*/) {
-                //We can simply add the table header at the end of the whole list
-                TableHeaderFooterPosition pos = new TableHeaderFooterPosition(
-                        getTableLM(), false, this.footerList);
-                KnuthBox box = new KnuthBox(footerNetHeight, pos, false);
-                footerAsLast = box;
+                log.debug("==> Footer: " 
+                        + footerNetHeight + " - " + this.footerList);
             }
+            //We can simply add the table footer at the end of the whole list
+            TableHeaderFooterPosition pos = new TableHeaderFooterPosition(
+                    getTableLM(), false, this.footerList);
+            KnuthBox box = new KnuthBox(footerNetHeight, pos, false);
+            footerAsLast = box;
         }
         LinkedList returnList = getKnuthElementsForRowIterator(
                 trIter, context, alignment, TableRowIterator.BODY);
@@ -192,7 +197,8 @@ public class TableContentLayoutManager implements PercentBaseContext {
      * @param iter TableRowIterator instance to fetch rows from
      * @param context Active LayoutContext
      * @param alignment alignment indicator
-     * @param bodyType Indicates what kind of body is being processed (BODY, HEADER or FOOTER)
+     * @param bodyType Indicates what kind of body is being processed 
+     *                  (BODY, HEADER or FOOTER)
      * @return An element list
      */
     private LinkedList getKnuthElementsForRowIterator(TableRowIterator iter, 
@@ -211,7 +217,7 @@ public class TableContentLayoutManager implements PercentBaseContext {
                         pen.setP(-KnuthPenalty.INFINITE);
                         pen.setBreakClass(rowFO.getBreakBefore());
                     } else if (last instanceof BreakElement) {
-                        BreakElement breakPoss = (BreakElement)last;
+                        BreakElement breakPoss = (BreakElement) last;
                         breakPoss.setPenaltyValue(-KnuthPenalty.INFINITE);
                         breakPoss.setBreakClass(rowFO.getBreakBefore());
                     }
@@ -573,9 +579,7 @@ public class TableContentLayoutManager implements PercentBaseContext {
                 log.debug("  height=" + rowHeights[i] + " explicit=" + explicitRowHeights[i]);
             }
         }
-        //TODO It may make sense to reuse the stepper since it allocates quite some space
-        TableStepper stepper = new TableStepper(this);
-        LinkedList returnedList = stepper.getCombinedKnuthElementsForRowGroup(
+        LinkedList returnedList = this.stepper.getCombinedKnuthElementsForRowGroup(
                 context, rowGroup, maxColumnCount, bodyType);
         if (returnedList != null) {
             returnList.addAll(returnedList);
index e4191732b9c688898545ce71797f91b5f66fa6e1..9d77293ed708a2dd547dafdf429147b7ba9f421b 100644 (file)
@@ -72,10 +72,10 @@ public class TableStepper {
      */
     public TableStepper(TableContentLayoutManager tclm) {
         this.tclm = tclm;
-        this.activeRow = 0;
     }
     
     private void setup(int columnCount) {
+        this.activeRow = 0;
         elementLists = new List[columnCount];
         startRow = new int[columnCount];
         start = new int[columnCount];
index bf90191b2b1ab377c5230081bdfea5fee445d97a..c8f5f71a6edf84a6a7046c152ec08f376e9e128f 100644 (file)
@@ -26,6 +26,7 @@ import java.awt.Color;
 import java.awt.geom.AffineTransform;
 
 import org.w3c.dom.Document;
+import org.w3c.dom.svg.SVGAElement;
 import org.w3c.dom.svg.SVGDocument;
 import org.w3c.dom.svg.SVGSVGElement;
 
@@ -39,6 +40,7 @@ import org.apache.fop.pdf.PDFPage;
 import org.apache.fop.pdf.PDFState;
 import org.apache.fop.pdf.PDFStream;
 import org.apache.fop.pdf.PDFResourceContext;
+import org.apache.fop.svg.PDFAElementBridge;
 import org.apache.fop.svg.PDFBridgeContext;
 import org.apache.fop.svg.PDFGraphics2D;
 import org.apache.fop.svg.SVGUserAgent;
@@ -55,7 +57,9 @@ import org.apache.avalon.framework.configuration.Configuration;
 import org.apache.batik.bridge.GVTBuilder;
 import org.apache.batik.bridge.BridgeContext;
 import org.apache.batik.bridge.ViewBox;
+import org.apache.batik.dom.svg.SVGDOMImplementation;
 import org.apache.batik.gvt.GraphicsNode;
+import org.apache.batik.util.SVGConstants;
 
 /**
  * PDF XML handler for SVG (uses Apache Batik).
@@ -142,6 +146,7 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler
      */
     protected void renderSVGDocument(RendererContext context,
             Document doc) {
+        PDFRenderer renderer = (PDFRenderer)context.getRenderer();
         PDFInfo pdfInfo = getPDFInfo(context);
         if (pdfInfo.paintAsBitmap) {
             try {
@@ -163,12 +168,18 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler
         final float uaResolution = context.getUserAgent().getSourceResolution();
         SVGUserAgent ua = new SVGUserAgent(25.4f / uaResolution, new AffineTransform());
 
-        GVTBuilder builder = new GVTBuilder();
+        //Scale for higher resolution on-the-fly images from Batik
+        double s = uaResolution / deviceResolution;
+        AffineTransform resolutionScaling = new AffineTransform();
+        resolutionScaling.scale(s, s);
         
-        //TODO This AffineTransform here has to be fixed!!! 
-        AffineTransform linkTransform = pdfInfo.pdfState.getTransform();
-        linkTransform.translate(xOffset / 1000f, yOffset / 1000f);
+        //Transformation matrix that establishes the local coordinate system for the SVG graphic
+        //in relation to PDF's initial coordinate system.
+        AffineTransform baseTransform = (AffineTransform)renderer.currentBasicTransform.clone();
+        baseTransform.concatenate(pdfInfo.pdfState.getTransform());
 
+        GVTBuilder builder = new GVTBuilder();
+        
         //Controls whether text painted by Batik is generated using text or path operations
         boolean strokeText = false;
         Configuration cfg = pdfInfo.cfg;
@@ -178,11 +189,12 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler
         
         BridgeContext ctx = new PDFBridgeContext(ua, 
                 (strokeText ? null : pdfInfo.fi),
-                linkTransform);
+                new AffineTransform());
         
         GraphicsNode root;
         try {
             root = builder.build(ctx, doc);
+            builder = null;
         } catch (Exception e) {
             log.error("svg graphic could not be built: "
                                    + e.getMessage(), e);
@@ -195,35 +207,36 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler
         float sx = pdfInfo.width / (float)w;
         float sy = pdfInfo.height / (float)h;
 
-        ctx = null;
-        builder = null;
+        //Scaling and translation for the bounding box of the image
+        AffineTransform scaling = new AffineTransform(
+                sx, 0, 0, sy, xOffset / 1000f, yOffset / 1000f);
+
+        //Finish the baseTransform, now that we know everything
+        baseTransform.concatenate(scaling);
+        baseTransform.concatenate(resolutionScaling);
+        
+        //Now that we have the full baseTransform, we can update the transformation matrix for
+        //the AElementBridge.
+        PDFAElementBridge aBridge = (PDFAElementBridge)ctx.getBridge(
+                SVGDOMImplementation.SVG_NAMESPACE_URI, SVGConstants.SVG_A_TAG);
+        aBridge.getCurrentTransform().setTransform(baseTransform);
 
         /*
          * Clip to the svg area.
          * Note: To have the svg overlay (under) a text area then use
          * an fo:block-container
          */
-        PDFRenderer renderer = (PDFRenderer)context.getRenderer();
+        pdfInfo.currentStream.add("%SVG setup\n");
         renderer.saveGraphicsState();
         renderer.setColor(Color.black, false, null);
         renderer.setColor(Color.black, true, null);
-        // transform so that the coordinates (0,0) is from the top left
-        // and positive is down and to the right. (0,0) is where the
-        // viewBox puts it.
-        pdfInfo.currentStream.add(sx + " 0 0 " + sy + " " + xOffset / 1000f + " "
-                          + yOffset / 1000f + " cm\n");
+
+        if (!scaling.isIdentity()) {
+            pdfInfo.currentStream.add("%viewbox\n");
+            pdfInfo.currentStream.add(CTMHelper.toPDFString(scaling, false) + " cm\n");
+        }
 
         SVGSVGElement svg = ((SVGDocument)doc).getRootElement();
-        //AffineTransform at = ViewBox.getPreserveAspectRatioTransform(
-        //                          svg, w / 1000f, h / 1000f);
-        AffineTransform at = ViewBox.getPreserveAspectRatioTransform(svg,
-                pdfInfo.width / 1000f, pdfInfo.height / 1000f);
-        /*
-        if (!at.isIdentity()) {
-            double[] vals = new double[6];
-            at.getMatrix(vals);
-            pdfInfo.currentStream.add(CTMHelper.toPDFString(at, false) + " cm\n");
-        }*/
 
         if (pdfInfo.pdfContext == null) {
             pdfInfo.pdfContext = pdfInfo.pdfPage;
@@ -233,21 +246,18 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler
                 pdfInfo.pdfContext, pdfInfo.pdfPage.referencePDF(),
                 pdfInfo.currentFontName, pdfInfo.currentFontSize);
         graphics.setGraphicContext(new org.apache.xmlgraphics.java2d.GraphicContext());
-        pdfInfo.pdfState.push();
-        AffineTransform transform = new AffineTransform();
-        // scale to viewbox
-        transform.translate(xOffset / 1000f, yOffset / 1000f);
 
-        if (deviceResolution != uaResolution) {
-            //Scale for higher resolution on-the-fly images from Batik
-            double s = uaResolution / deviceResolution;
-            at.scale(s, s);
-            pdfInfo.currentStream.add("" + PDFNumber.doubleOut(s) + " 0 0 "
-                                + PDFNumber.doubleOut(s) + " 0 0 cm\n");
+        if (!resolutionScaling.isIdentity()) {
+            pdfInfo.currentStream.add("%resolution scaling for " + uaResolution + " -> " + deviceResolution + "\n");
+            pdfInfo.currentStream.add(
+                    CTMHelper.toPDFString(resolutionScaling, false) + " cm\n");
             graphics.scale(1 / s, 1 / s);
         }
+        
+        pdfInfo.currentStream.add("%SVG start\n");
 
-        pdfInfo.pdfState.setTransform(transform);
+        pdfInfo.pdfState.push();
+        pdfInfo.pdfState.setTransform(baseTransform);
         graphics.setPDFState(pdfInfo.pdfState);
         graphics.setOutputStream(pdfInfo.outputStream);
         try {
@@ -257,9 +267,9 @@ public class PDFSVGHandler extends AbstractGenericSVGHandler
             log.error("svg graphic could not be rendered: "
                                    + e.getMessage(), e);
         }
-
-        renderer.restoreGraphicsState();
         pdfInfo.pdfState.pop();
+        renderer.restoreGraphicsState();
+        pdfInfo.currentStream.add("%SVG end\n");
     }
     
     /** @see org.apache.fop.render.XMLHandler#supportsRenderer(org.apache.fop.render.Renderer) */
index 56576afa57b1d458b08fa7f976a0c97c9af59e8c..e9bb8a955a501b0b61575c5ac519eb6fd560ef7d 100644 (file)
@@ -51,6 +51,11 @@ public class PDFAElementBridge extends AbstractGraphicsNodeBridge {
         transform = tf;
     }
 
+    /** @return the transformation matrix for links */
+    public AffineTransform getCurrentTransform() {
+        return this.transform;
+    }
+    
     /**
      * Returns 'a'.
      * @return the name of this node
index 4d7ac826cff11eba25885b9fc3037f408a44ad04..74dac01febddcc4c8ed33ad0ca53b22e3fa8127b 100644 (file)
@@ -165,7 +165,7 @@ public class PDFDocumentGraphics2D extends PDFGraphics2D
      * @see org.apache.avalon.framework.activity.Initializable#initialize()
      */
     public void initialize() throws Exception {
-        if (this.fontInfo == null) {
+        if (this.fontInfo == null || this.cfg != null) {
             fontInfo = new FontInfo();
             FontSetup.setup(fontInfo, this.pdfContext.getFontList(), null);
             //FontState fontState = new FontState("Helvetica", "normal",
index af0a0cfaae82f10619d28637a9fdd40f186f4e5d..0e821975d306a50d45bb32a1b6dd2a59593e3df8 100644 (file)
@@ -342,6 +342,16 @@ public class PDFGraphics2D extends AbstractGraphics2D {
                 + PDFNumber.doubleOut(matrix[4], DEC) + " "
                 + PDFNumber.doubleOut(matrix[5], DEC) + " cm\n");
     }
+
+    /**
+     * This is mainly used for shading patterns which use the document-global coordinate system
+     * instead of the local one.
+     * @return the transformation matrix that established the basic user space for this document
+     */
+    protected AffineTransform getBaseTransform() {
+        AffineTransform at = new AffineTransform(graphicsState.getTransform());
+        return at;
+    }
     
     /**
      * This is a pdf specific method used to add a link to the
@@ -897,7 +907,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
             // Build proper transform from gradient space to page space
             // ('Patterns' don't get userspace transform).
             AffineTransform transform;
-            transform = new AffineTransform(graphicsState.getTransform());
+            transform = new AffineTransform(getBaseTransform());
             transform.concatenate(getTransform());
             transform.concatenate(gp.getTransform());
 
@@ -973,7 +983,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
             }
 
             AffineTransform transform;
-            transform = new AffineTransform(graphicsState.getTransform());
+            transform = new AffineTransform(getBaseTransform());
             transform.concatenate(getTransform());
             transform.concatenate(rgp.getTransform());
 
@@ -1103,7 +1113,7 @@ public class PDFGraphics2D extends AbstractGraphics2D {
         bbox.add(new Double(rect.getY()));
 
         AffineTransform transform;
-        transform = new AffineTransform(graphicsState.getTransform());
+        transform = new AffineTransform(getBaseTransform());
         transform.concatenate(getTransform());
         transform.concatenate(pp.getPatternTransform());
 
index 91afbd8c3b90c93db2ea98cf802b2e0b02bd65c0..c6cef39648964fbfab12d87fa9b1c7e429551918 100644 (file)
 
   <changes>
     <release version="FOP Trunk">
+      <action context="Code" dev="AD" type="update" fixes-bug="40270" due-to="Gary Reed">
+        Added relaxed validation for empty list-item-*, as suggested by Gary Reed.
+      </action>
+      <action context="Code" dev="AD" type="update">
+        Modified proportional-column-width() function to log an error if used 
+        with table-layout=auto
+      </action>
+      <action context="Code" dev="AD" type="fix">
+        Deferred property resolution for markers until they are actually retrieved,
+        which leads to percentages and relative font-sizes now getting the correct
+        values. Also deferred white-space-handling for markers.
+      </action>
+      <action context="Code" dev="JM" type="update">
+        Changed the way overflowing pages are handled. The overflow property on region-body
+        is now used to define the behaviour.
+      </action>
+      <action context="Code" dev="JM" type="fix">
+        Fixed a memory-leak: The FO tree part of a page-sequence was not released when a
+        page-sequence was finished.
+      </action>
+      <action context="Code" dev="JM" type="fix">
+        Bugfix: Table headers and footers were swallowed when a table was nested in a list-block.
+      </action>
       <action context="Code" dev="JM" type="fix">
         Fixed a bug with indent handling when margins are used on a surrounding block and 
         not start/end-indent.
         Initial support for page-number-citation-last (XSL 1.1). Works without problems 
         only for page-sequence so far.
       </action>
+      <action context="Code" dev="VH" type="update" fixes-bug="39777">
+        Improved support of before-floats: allow float-only pages, take into
+        account floats' shrinkability/stretchability, handle the splitting of
+        big floats.
+      </action>
     </release>
     <release version="0.92beta" date="18 Apr 2006">
       <action context="Code" dev="JM" type="fix">
diff --git a/test/fotree/testcases/table-cell_column-number_rowspan_bug38397.fo b/test/fotree/testcases/table-cell_column-number_rowspan_bug38397.fo
new file mode 100644 (file)
index 0000000..0cf2752
--- /dev/null
@@ -0,0 +1,98 @@
+<?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$ -->
+<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg"
+         xmlns:test="http://xmlgraphics.apache.org/fop/test">
+  <fo:layout-master-set>
+    <fo:simple-page-master master-name="normal" page-width="5in" page-height="5in">
+      <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:table table-layout="fixed" width="100%" border-collapse="separate">
+        <fo:table-column column-width="proportional-column-width(1)"/>
+        <fo:table-column column-width="proportional-column-width(1)"/>
+        <fo:table-column column-width="proportional-column-width(2)"/>
+        <fo:table-column column-width="proportional-column-width(2)"/>
+        <fo:table-body>
+          <fo:table-row>
+            <fo:table-cell number-rows-spanned="3" number-columns-spanned="2" display-align="center" border="solid 0.5pt">
+              <test:assert property="column-number" expected="1" />
+              <fo:block>cell1</fo:block>
+            </fo:table-cell>
+            <fo:table-cell number-rows-spanned="2" display-align="center" border="solid 0.5pt">
+              <test:assert property="column-number" expected="3" />
+              <fo:block>cell2</fo:block>
+            </fo:table-cell>
+            <fo:table-cell border="solid 0.5pt">
+              <test:assert property="column-number" expected="4" />
+              <fo:block>cell3</fo:block>
+            </fo:table-cell>
+          </fo:table-row>
+          <fo:table-row background-color="yellow">
+            <fo:table-cell border="solid 0.5pt">
+              <test:assert property="column-number" expected="4" />
+              <fo:block>cell4</fo:block>
+            </fo:table-cell>
+          </fo:table-row>
+          <fo:table-row>
+            <fo:table-cell number-rows-spanned="2" display-align="center" border="solid 0.5pt">
+              <test:assert property="column-number" expected="3" />
+              <fo:block>cell5</fo:block>
+            </fo:table-cell>
+            <fo:table-cell border="solid 0.5pt">
+              <test:assert property="column-number" expected="4" />
+              <fo:block>cell6</fo:block>
+            </fo:table-cell>
+          </fo:table-row>
+          <fo:table-row background-color="yellow">
+            <fo:table-cell number-rows-spanned="3" display-align="center" border="solid 0.5pt">
+              <test:assert property="column-number" expected="1" />
+              <fo:block>cell7</fo:block>
+            </fo:table-cell>
+            <fo:table-cell number-rows-spanned="3" display-align="center" border="solid 0.5pt">
+              <test:assert property="column-number" expected="2" />
+              <fo:block>cell8</fo:block>
+            </fo:table-cell>
+            <fo:table-cell border="solid 0.5pt">
+              <test:assert property="column-number" expected="4" />
+              <fo:block>cell9</fo:block>
+            </fo:table-cell>
+          </fo:table-row>
+          <fo:table-row>
+            <fo:table-cell number-rows-spanned="2" display-align="center" border="solid 0.5pt">
+              <test:assert property="column-number" expected="3" />
+              <fo:block>cell10</fo:block>
+            </fo:table-cell>
+            <fo:table-cell border="solid 0.5pt">
+              <test:assert property="column-number" expected="4" />
+              <fo:block>cell11</fo:block>
+            </fo:table-cell>
+          </fo:table-row>
+          <fo:table-row background-color="yellow">
+            <fo:table-cell border="solid 0.5pt">
+              <test:assert property="column-number" expected="4" />
+              <fo:block>cell12</fo:block>
+            </fo:table-cell>
+          </fo:table-row>
+        </fo:table-body>
+      </fo:table>
+    </fo:flow>
+  </fo:page-sequence>
+</fo:root>
diff --git a/test/java/org/apache/fop/memory/MemoryEater.java b/test/java/org/apache/fop/memory/MemoryEater.java
new file mode 100644 (file)
index 0000000..ed8f464
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * 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.memory;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.commons.io.output.NullOutputStream;
+import org.apache.fop.apps.Fop;
+import org.apache.fop.apps.FopFactory;
+import org.apache.fop.apps.MimeConstants;
+
+/**
+ * Debug tool to create and process large FO files by replicating them a specified number of times.
+ */
+public class MemoryEater {
+
+    private static void eatMemory(File foFile, int replicatorRepeats) throws Exception {
+
+        SAXTransformerFactory tFactory = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
+        FopFactory fopFactory = FopFactory.newInstance();
+        
+        File xsltFile = new File("test/xsl/fo-replicator.xsl");
+        Source xslt = new StreamSource(xsltFile);
+        
+        Source src = new StreamSource(foFile);
+        
+        Transformer transformer = tFactory.newTransformer(xslt);
+        transformer.setParameter("repeats", new Integer(replicatorRepeats));
+        
+        OutputStream out = new NullOutputStream(); //write to /dev/nul
+        Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);
+        Result res = new SAXResult(fop.getDefaultHandler());
+        
+        transformer.transform(src, res);
+        
+        System.out.println("Generated " + fop.getResults().getPageCount() + " pages.");
+        
+    }
+
+    private static void prompt() throws IOException {
+        BufferedReader in = new BufferedReader(new java.io.InputStreamReader(System.in));
+        System.out.print("Press return to continue...");
+        in.readLine();
+    }
+    
+    /**
+     * Main method.
+     * @param args the command-line arguments
+     */
+    public static void main(String[] args) {
+        boolean doPrompt = true; //true if you want a chance to start the monitoring console
+        try {
+            int replicatorRepeats = 2;
+            if (args.length > 0) {
+                replicatorRepeats = Integer.parseInt(args[0]);
+            }
+            File testFile = new File("examples/fo/basic/readme.fo");
+            
+            System.out.println("MemoryEater! About to replicate the test file " 
+                    + replicatorRepeats + " times...");
+            if (doPrompt) {
+                prompt();
+            }
+            
+            System.out.println("Processing..."); 
+            long start = System.currentTimeMillis();
+            
+            eatMemory(testFile, replicatorRepeats);
+            
+            long duration = System.currentTimeMillis() - start;
+            System.out.println("Success! Job took " + duration + " ms");
+            
+            if (doPrompt) {
+                prompt();
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+}
index f3d61eb2b1be1e3f3dc85564a139ebde1b8fbf4b..15c7629610c8a378eb949c00349ecb2915f49fc2 100644 (file)
     <description>Keep-with-previous doesn't work inside tables and
     lists, yet.</description>
   </testcase>
-  <testcase>
-    <name>Whitespace around markers is not handled correctly</name>
-    <file>marker_white-space-collapse.xml</file>
-    <description>Whitespace within markers is handled according to the value of
-    the white-space-collapse property in the context of the fo:marker and not according to
-    the value of the property in the fo:retrieve-marker context.</description>
-  </testcase>
-  <testcase>
-    <name>Relative font sizes within markers are not handled correctly</name>
-    <file>marker_font-size.xml</file>
-    <description>Relative font sizes within markers are evaluated according to the
-    font size in the fo:marker context and not the font size in the fo:retrieve-marker
-    context.</description>
-  </testcase>
   <testcase>
     <name>Page breaking doesn't deal with IPD changes</name>
     <file>page-breaking_4.xml</file>
   <testcase>
     <name>table-cell empty area with marker.xml</name>
     <file>table-cell_empty_area_with_marker.xml</file>
-    <description>A table-cell producing an empty area does currently not add any markers to a page. See TODO entry in AreaAdditionUtil.</description>
+    <description>A table-cell producing an empty area does currently not add any markers to a page. 
+      See TODO entry in AreaAdditionUtil.</description>
   </testcase>
   <testcase>
     <name>Border conditionality on table</name>
     underfull page is created so that the citation be on the same page as the
     float.</description>
   </testcase>
+  <testcase>
+    <name>Before-floats plus footnotes on last page</name>
+    <file>before-float_footnote_last-page.xml</file>
+    <description>When an additional page must be created at the end
+      of a flow for placing both a remaining before-float and a footnote, an
+      IndexOutOfBoundException occurs.</description>
+  </testcase>
+  <testcase>
+    <name>Before-float too large to fit on a page alone</name>
+    <file>before-float_large.xml</file>
+    <description>When a before-float is too large to even fit on a page alone,
+      it should be split on several pages. Currently, there is an OutOfMemory
+      error.</description>
+  </testcase>
 </disabled-testcases>
diff --git a/test/layoutengine/standard-testcases/before-float_footnote_last-page.xml b/test/layoutengine/standard-testcases/before-float_footnote_last-page.xml
new file mode 100644 (file)
index 0000000..ddc0061
--- /dev/null
@@ -0,0 +1,109 @@
+<?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 when an additional page must be created at the end
+      of a flow for remaining out-of-line objects, then both the remaining
+      before-floats and footnotes are placed on this page.
+    </p>
+    <p>
+      Currently disabled as this is a non-working feature (footnote is deferred
+      instead of being split).
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+      <fo:layout-master-set>
+       <fo:simple-page-master master-name="normal"
+         page-width="12cm" page-height="5.25cm">
+         <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"
+                space-after.minimum="2pt"
+                space-after.optimum="6pt"
+                space-after.maximum="14pt"
+                widows="1" orphans="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.
+           </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.
+           </fo:block>
+           <fo:block space-after="inherit">
+             This is a block with a float and a footnote.
+             This is a block with a float and a footnote.
+             This is a block with a float and a footnote.
+             The footnote citation is here<fo:footnote>
+               <fo:inline color="blue">1</fo:inline>
+               <fo:footnote-body color="blue">
+                 <fo:block>
+                   Footnote body. Footnote body. Footnote body. Footnote body.
+                   Footnote body. Footnote body. Footnote body. Footnote body.
+                   Footnote body. Footnote body. Footnote body. Footnote body.
+                   Footnote body. Footnote body. Footnote body. Footnote body.
+                 </fo:block>
+               </fo:footnote-body>
+             </fo:footnote>. The float anchor is <fo:inline color="red">here</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>.
+           </fo:block>
+       </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="2" xpath="count(//pageViewport)"/>
+    <!-- first page -->
+    <!-- no before-float -->
+    <eval expected="0"
+         xpath="count(//pageViewport[1]/page/regionViewport/regionBody/beforeFloat/block)"/>
+    <!-- two lines of footnotes -->
+    <eval expected="1"
+         xpath="count(//pageViewport[1]/page/regionViewport/regionBody/footnote/block)"/>
+    <eval expected="2"
+         xpath="count(//pageViewport[1]/page/regionViewport/regionBody/footnote/block/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"
+          xpath="//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block/lineArea/text[1]"/>
+    <eval expected="57600"
+          xpath="//pageViewport[2]/page/regionViewport/regionBody/beforeFloat/block/@bpd"/>
+    <!-- two remaining lines of footnotes -->
+    <eval expected="1"
+         xpath="count(//pageViewport[2]/page/regionViewport/regionBody/footnote/block)"/>
+    <eval expected="2"
+         xpath="count(//pageViewport[2]/page/regionViewport/regionBody/footnote/block/lineArea)"/>
+    <!-- nothing in the main area -->
+    <eval expected="0"
+         xpath="count(//pageViewport[2]/page/regionViewport/regionBody/mainReference/span/flow/block)"/>
+  </checks>
+</testcase>
diff --git a/test/layoutengine/standard-testcases/before-float_large.xml b/test/layoutengine/standard-testcases/before-float_large.xml
new file mode 100644 (file)
index 0000000..15d03c9
--- /dev/null
@@ -0,0 +1,103 @@
+<?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 too large to fit on a page alone is
+      split on several pages.
+    </p>
+    <p>
+      Currently disabled as this feature still needs some debugging.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+      <fo:layout-master-set>
+       <fo:simple-page-master master-name="normal"
+         page-width="12cm" page-height="5.25cm">
+         <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"
+                space-after.minimum="2pt"
+                space-after.optimum="6pt"
+                space-after.maximum="14pt"
+                widows="1" orphans="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.
+           </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.
+           </fo:block>
+           <fo:block space-after="inherit">
+             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.
+             The float anchor is <fo:inline color="red">here</fo:inline><fo:float
+               float="before" color="red"
+               space-after.minimum="3pt"
+               space-after.optimum="6pt"
+               space-after.maximum="9">
+               <fo:block space-after="inherit">
+                 This is the float content. This is the float content.
+                 This is the float content. This is the float content.
+               </fo:block>
+               <fo:block space-after="inherit">
+                 This is the float content. This is the float content.
+                 This is the float content. This is the float content.
+               </fo:block>
+               <fo:block space-after="inherit">
+                 This is the float content. This is the float content.
+                 This is the float content. This is the float content.
+               </fo:block>
+               <fo:block space-after="inherit">
+                 This is the float content. This is the float content.
+                 This is the float content. This is the float content.
+               </fo:block>
+               <fo:block space-after="inherit">
+                 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.
+           </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.
+           </fo:block>
+       </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <!--
+      Well, the result is highly dependent on the implemented algorithm. Tests
+      will have to be added when it is implemented. Currently the float
+      overflows the page instead of being split.
+    -->
+  </checks>
+</testcase>
index 1f7bb756ae1f20920a22603aed726b44ed287e6b..6e35b1146bba53da618841e4e99b0df8108edb99 100644 (file)
@@ -91,7 +91,7 @@
     <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"
+    <eval expected="3"
           xpath="count(//pageViewport[1]/page/regionViewport/regionBody/mainReference/span/flow/block/block[3]/lineArea)"/>
     <!-- second page -->
     <eval expected="0"
diff --git a/test/layoutengine/standard-testcases/block_keep-together_overflow_1.xml b/test/layoutengine/standard-testcases/block_keep-together_overflow_1.xml
new file mode 100644 (file)
index 0000000..a4a2de6
--- /dev/null
@@ -0,0 +1,85 @@
+<?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 keep-together with overflow conditions.
+    </p>
+    <p>
+      Widows and Orphans are disabled in this test to avoid side-effects.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" widows="0" orphans="0">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="normal" page-width="5in" page-height="4.5 * 14.4pt">
+          <fo:region-body overflow="hidden"/>
+        </fo:simple-page-master>
+        <fo:simple-page-master master-name="longer" page-width="5in" page-height="5.5 * 14.4pt">
+          <fo:region-body overflow="hidden"/>
+        </fo:simple-page-master>
+        <fo:page-sequence-master master-name="mix">
+          <fo:repeatable-page-master-reference master-reference="normal" maximum-repeats="2"/>
+          <fo:repeatable-page-master-reference master-reference="longer" maximum-repeats="1"/>
+          <fo:repeatable-page-master-reference master-reference="normal"/>
+        </fo:page-sequence-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="mix">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block>block1</fo:block>
+          <fo:block>block2</fo:block>
+          <fo:block keep-together.within-page="10" color="blue">
+            <fo:block>block3</fo:block>
+            <fo:block>block4</fo:block>
+            <fo:block>block5</fo:block>
+            <fo:block>block6</fo:block>
+            <fo:block>block7</fo:block>
+          </fo:block>
+          <fo:block>block8</fo:block>
+          <fo:block>block9</fo:block>
+          <fo:block keep-together.within-page="10" color="green">
+            <fo:block>block10</fo:block>
+            <fo:block>block11</fo:block>
+            <fo:block>block12</fo:block>
+            <fo:block>block13</fo:block>
+            <fo:block>block14</fo:block>
+          </fo:block>
+          <fo:block>block15</fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="1" xpath="//lineArea[starts-with(., 'block1')]/ancestor::pageViewport/@nr"/>
+    <eval expected="1" xpath="//lineArea[starts-with(., 'block2')]/ancestor::pageViewport/@nr"/>
+    <eval expected="3" xpath="//lineArea[starts-with(., 'block3')]/ancestor::pageViewport/@nr"/>
+    <eval expected="3" xpath="//lineArea[starts-with(., 'block4')]/ancestor::pageViewport/@nr"/>
+    <eval expected="3" xpath="//lineArea[starts-with(., 'block5')]/ancestor::pageViewport/@nr"/>
+    <eval expected="3" xpath="//lineArea[starts-with(., 'block6')]/ancestor::pageViewport/@nr"/>
+    <eval expected="3" xpath="//lineArea[starts-with(., 'block7')]/ancestor::pageViewport/@nr"/>
+    <eval expected="4" xpath="//lineArea[starts-with(., 'block8')]/ancestor::pageViewport/@nr"/>
+    <eval expected="4" xpath="//lineArea[starts-with(., 'block9')]/ancestor::pageViewport/@nr"/>
+    <eval expected="5" xpath="//lineArea[starts-with(., 'block10')]/ancestor::pageViewport/@nr"/>
+    <eval expected="5" xpath="//lineArea[starts-with(., 'block11')]/ancestor::pageViewport/@nr"/>
+    <eval expected="5" xpath="//lineArea[starts-with(., 'block12')]/ancestor::pageViewport/@nr"/>
+    <eval expected="5" xpath="//lineArea[starts-with(., 'block13')]/ancestor::pageViewport/@nr"/>
+    <eval expected="5" xpath="//lineArea[starts-with(., 'block14')]/ancestor::pageViewport/@nr"/>
+    <eval expected="6" xpath="//lineArea[starts-with(., 'block15')]/ancestor::pageViewport/@nr"/>
+  </checks>
+</testcase>
index 26a0a172ccf3ccc1c8aebb1b77a1de9c3cd54ff1..c4a7baba15afa17bf8bd5f9e367da58d1c640dc7 100644 (file)
@@ -37,7 +37,9 @@
       <fo:page-sequence master-reference="normal">
         <fo:static-content flow-name="xsl-region-before">
           <fo:block background-color="yellow" font-size="16pt">
-            1. Marker <fo:retrieve-marker retrieve-class-name="m1" />
+            1. Marker <fo:retrieve-marker retrieve-class-name="m1" 
+                          retrieve-boundary="page"
+                          retrieve-position="first-starting-within-page" />
           </fo:block>
         </fo:static-content>
         <fo:static-content flow-name="xsl-region-after">
@@ -46,7 +48,7 @@
           </fo:block>
         </fo:static-content>
         <fo:flow flow-name="xsl-region-body">
-          <fo:block background-color="red" white-space-collapse="false">
+          <fo:block background-color="red">
             <fo:marker marker-class-name="m1">
               <fo:block font-size=".5em">
                 First marker with small font 
@@ -65,8 +67,9 @@
     </fo:root>
   </fo>
   <checks>
+    <!-- font-size relative to the retrieve-marker context? -->
     <eval expected="8000" xpath="//regionBefore/block[1]/block/lineArea/text/@font-size"/>
-
-    <eval expected="20000" xpath="//regionBefore/block[1]/block/lineArea/text/@font-size"/>
+    <eval expected="20000" xpath="//regionAfter/block[1]/block/lineArea/text/@font-size"/>
+    
   </checks>
 </testcase>
diff --git a/test/layoutengine/standard-testcases/marker_percentage-resolution.xml b/test/layoutengine/standard-testcases/marker_percentage-resolution.xml
new file mode 100644 (file)
index 0000000..6bb9343
--- /dev/null
@@ -0,0 +1,106 @@
+<?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 markers and percentage resolution:
+      Percentages should be evaluated in the context in which the marker is
+      retrieved.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="normal" page-width="5in" page-height="2in">
+          <fo:region-body margin="0.5in 0"/>
+          <fo:region-after extent="0.5in"/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="normal">
+        <fo:static-content flow-name="xsl-region-after">
+          <fo:block text-align="end" background-color="yellow">
+            <fo:retrieve-marker retrieve-class-name="test" 
+                retrieve-boundary="page" 
+                retrieve-position="last-ending-within-page"/>
+          </fo:block>
+        </fo:static-content>
+        <fo:flow flow-name="xsl-region-body">
+          <fo:table table-layout="fixed" width="100%">
+            <fo:table-column number-columns-repeated="2"/>
+            <fo:table-body>
+              <fo:table-row>
+                <fo:table-cell>
+                  <fo:marker marker-class-name="test">
+                    <fo:table table-layout="fixed" width="100%">
+                      <fo:table-column number-columns-repeated="2"/>
+                      <fo:table-body>
+                        <fo:table-row>
+                          <fo:table-cell>
+                            <fo:block>Subtotal</fo:block>
+                          </fo:table-cell>
+                          <fo:table-cell text-align="end">
+                            <fo:block>29.95</fo:block>
+                          </fo:table-cell>
+                        </fo:table-row>
+                      </fo:table-body>
+                    </fo:table>
+                  </fo:marker>
+                  <fo:block>MemoryStick 32MB</fo:block>
+                </fo:table-cell>
+                <fo:table-cell text-align="end">
+                  <fo:block>29.95</fo:block>
+                </fo:table-cell>
+              </fo:table-row>
+              <fo:table-row>
+                <fo:table-cell>
+                  <fo:marker marker-class-name="test">
+                    <fo:block-container inline-progression-dimension="75%">
+                      <fo:block>Test</fo:block>
+                    </fo:block-container>
+                    <fo:table table-layout="fixed" width="100%">
+                      <fo:table-column number-columns-repeated="2"/>
+                      <fo:table-body>
+                        <fo:table-row>
+                          <fo:table-cell>
+                            <fo:block>Subtotal</fo:block>
+                          </fo:table-cell>
+                          <fo:table-cell text-align="end">
+                            <fo:block>49.95</fo:block>
+                          </fo:table-cell>
+                        </fo:table-row>
+                      </fo:table-body>
+                    </fo:table>
+                  </fo:marker>
+                  <fo:block>Geek-Tool</fo:block>
+                </fo:table-cell>
+                <fo:table-cell text-align="end">
+                  <fo:block>20.00</fo:block>
+                </fo:table-cell>
+              </fo:table-row>
+            </fo:table-body>
+          </fo:table>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="270000" xpath="//regionAfter/block[1]/block[1]/@ipd"/>
+    <eval expected="360000" xpath="//regionAfter/block[1]/block[2]/@ipd"/>
+  </checks>
+</testcase>
index 645d34f4e50977bec2e9345d24bb2e50d709e2c6..ddb9d23a297f1292f645078b3cafc274269affce 100644 (file)
@@ -52,7 +52,7 @@
           </fo:block>
         </fo:static-content>
         <fo:flow flow-name="xsl-region-body">
-          <fo:block background-color="red" white-space-collapse="false">
+          <fo:block background-color="red">
             <fo:marker marker-class-name="m1">
               <fo:block>
                 First   marker  with  whitespace  around
     </fo:root>
   </fo>
   <checks>
-    <!-- These checks do not have the correct values -->
-    <eval expected="1" xpath="count(//regionBefore/block[1]/block/lineArea/text/space)"/>
-    <eval expected="4" xpath="count(//regionBefore/block[2]/block/lineArea/text/space)"/>
+    <!-- preserve inter-word whitespace in the first block, 
+         collapse them in the second -->
+    <eval expected="9" xpath="count(//regionBefore/block[1]/block[1]/lineArea[1]/text/space)"/>
+    <eval expected="4" xpath="count(//regionBefore/block[2]/block[1]/lineArea[1]/text/space)"/>
 
-    <eval expected="1" xpath="count(//regionAfter/block[1]/block/lineArea/text/space)"/>
-    <eval expected="4" xpath="count(//regionAfter/block[2]/block/lineArea/text/space)"/>
+    <eval expected="8" xpath="count(//regionAfter/block[1]/block[1]/lineArea[1]/text/space)"/>
+    <eval expected="4" xpath="count(//regionAfter/block[2]/block[1]/lineArea[1]/text/space)"/>
   </checks>
 </testcase>
index 3bb7af3796a6b56b75a09fe3bc0b7674fd2ee070..ef018f2960bb2ecd0b3a1d312475b278bfbf9442 100644 (file)
@@ -96,7 +96,7 @@
           </fo:block>
         </fo:static-content>
         <fo:flow flow-name="xsl-region-body">
-          <fo:table>
+          <fo:table table-layout="fixed">
             <fo:marker marker-class-name="test">fo:table</fo:marker>
             <fo:table-body>
               <fo:table-cell>
           </fo:block>
         </fo:static-content>
         <fo:flow flow-name="xsl-region-body">
-          <fo:table>
+          <fo:table table-layout="fixed">
             <fo:table-body>
               <fo:marker marker-class-name="test">fo:table-body</fo:marker>
               <fo:table-cell>
           </fo:block>
         </fo:static-content>
         <fo:flow flow-name="xsl-region-body">
-          <fo:table>
+          <fo:table table-layout="fixed">
             <fo:table-header>
               <fo:marker marker-class-name="test">fo:table-header</fo:marker>
               <fo:table-cell>
           </fo:block>
         </fo:static-content>
         <fo:flow flow-name="xsl-region-body">
-          <fo:table>
+          <fo:table table-layout="fixed">
             <fo:table-footer>
               <fo:marker marker-class-name="test">fo:table-footer</fo:marker>
               <fo:table-cell>
           </fo:block>
         </fo:static-content>
         <fo:flow flow-name="xsl-region-body">
-          <fo:table>
+          <fo:table table-layout="fixed">
             <fo:table-body>
               <fo:table-cell>
                 <fo:marker marker-class-name="test">fo:table-cell</fo:marker>
index f9de1bf02af74f50e6c4db3d97bec83e7e9eda38..ce2cac1d8c5234f2371d6ec12d634a5bdefc398d 100644 (file)
@@ -49,7 +49,7 @@
           </fo:block>
         </fo:static-content>
         <fo:flow flow-name="xsl-region-body">
-          <fo:table color="black">
+          <fo:table color="black" table-layout="fixed">
             <fo:marker marker-class-name="test">table1</fo:marker>
             <fo:table-column number-columns-repeated="2"/>
             <fo:table-body>
@@ -77,7 +77,7 @@
               </fo:table-row>
             </fo:table-body>
           </fo:table>
-          <fo:table color="red">
+          <fo:table color="red" table-layout="fixed">
             <fo:marker marker-class-name="test">table2</fo:marker>
             <fo:table-column number-columns-repeated="2"/>
             <fo:table-body>
@@ -99,7 +99,7 @@
               </fo:table-row>
             </fo:table-body>
           </fo:table>
-          <fo:table color="blue">
+          <fo:table color="blue" table-layout="fixed">
             <fo:marker marker-class-name="test">table3</fo:marker>
             <fo:table-column number-columns-repeated="2"/>
             <fo:table-body>
index fd66dfb5e062833c19e2ebeee8de73ad8f642680..5197665e52052e0e18917a0eaedbe09003787809 100644 (file)
@@ -49,7 +49,7 @@
           </fo:block>
         </fo:static-content>
         <fo:flow flow-name="xsl-region-body">
-          <fo:table color="black">
+          <fo:table color="black" table-layout="fixed">
             <fo:table-column number-columns-repeated="2"/>
             <fo:table-body>
               <fo:marker marker-class-name="test">body1</fo:marker>
@@ -77,7 +77,7 @@
               </fo:table-row>
             </fo:table-body>
           </fo:table>
-          <fo:table color="red">
+          <fo:table color="red" table-layout="fixed">
             <fo:table-column number-columns-repeated="2"/>
             <fo:table-body>
               <fo:marker marker-class-name="test">body2</fo:marker>
@@ -99,7 +99,7 @@
               </fo:table-row>
             </fo:table-body>
           </fo:table>
-          <fo:table color="blue">
+          <fo:table color="blue" table-layout="fixed">
             <fo:table-column number-columns-repeated="2"/>
             <fo:table-body>
               <fo:marker marker-class-name="test">body3</fo:marker>
               </fo:table-row>
             </fo:table-body>
           </fo:table>
-          <fo:table>
+          <fo:table table-layout="fixed">
             <fo:table-column number-columns-repeated="2"/>
             <fo:table-body color="black">
               <fo:marker marker-class-name="test">body5</fo:marker>
index 5f70119794ad89400c7c0d2ffec1f5ac3f5c6aa9..3c2c61a1d029e9efbe7f35c3c48e5621bfc4a20c 100644 (file)
@@ -31,7 +31,7 @@
       </fo:layout-master-set>
       <fo:page-sequence master-reference="normal" white-space-collapse="true">
         <fo:flow flow-name="xsl-region-body">
-          <fo:table color="black">
+          <fo:table color="black" table-layout="fixed">
             <fo:table-column number-columns-repeated="2"/>
             <fo:table-body>
               <fo:table-row>
index 7bb180571560af78bb8b598b8ae586f4e21c72fb..0477282439f5d98470f5c755f9ced7bfcda71d04 100644 (file)
@@ -34,7 +34,7 @@
       </fo:layout-master-set>\r
       <fo:page-sequence master-reference="normal" white-space-collapse="true">\r
         <fo:flow flow-name="xsl-region-body">\r
-          <fo:table border-collapse="separate">\r
+          <fo:table border-collapse="separate" table-layout="fixed">\r
             <fo:table-column />\r
             <fo:table-column />\r
             <fo:table-column />\r
index acb795a6225bd254e943fa955af3d41bbd70ff45..2cc55afadfa5c14240b00769939be733c7373fc0 100644 (file)
@@ -31,7 +31,7 @@
       </fo:layout-master-set>\r
       <fo:page-sequence master-reference="normal" white-space-collapse="true">\r
         <fo:flow flow-name="xsl-region-body">\r
-          <fo:table>\r
+          <fo:table table-layout="fixed">\r
             <fo:table-column column-number="1" />\r
             <fo:table-column column-number="2" />\r
             <fo:table-column column-number="3" />\r
diff --git a/test/layoutengine/standard-testcases/table-header_in_list_bug.xml b/test/layoutengine/standard-testcases/table-header_in_list_bug.xml
new file mode 100644 (file)
index 0000000..f235786
--- /dev/null
@@ -0,0 +1,115 @@
+<?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 lists with nested fo:tables. Table headers used to be
+      swallowed.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="normal" page-width="3in" page-height="1in">
+          <fo:region-body/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="normal">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:list-block>
+            <fo:list-item>
+              <fo:list-item-label end-indent="label-end()">
+                <fo:block>1.</fo:block>
+              </fo:list-item-label>
+              <fo:list-item-body start-indent="body-start()">
+                <fo:table table-layout="fixed" width="100%" border-collapse="separate" id="table">
+                  <fo:table-column column-width="100pt" text-align="right"/>
+                  <fo:table-header>
+                    <fo:table-row>
+                      <fo:table-cell padding-left="3pt" padding-top="1pt" border-style="solid" border-width="thin" padding-right="0.25cm" id="header-cell">
+                        <fo:block text-align="center">Header</fo:block>
+                      </fo:table-cell>
+                    </fo:table-row>
+                  </fo:table-header>
+                  <fo:table-body>
+                    <fo:table-row>
+                      <fo:table-cell padding-left="3pt" padding-top="1pt" border-style="solid" border-width="thin" padding-right="0.25cm">
+                        <fo:block text-align="right">1</fo:block>
+                      </fo:table-cell>
+                    </fo:table-row>
+                    <fo:table-row>
+                      <fo:table-cell padding-left="3pt" padding-top="1pt" border-style="solid" border-width="thin" padding-right="0.25cm">
+                        <fo:block text-align="right">2</fo:block>
+                      </fo:table-cell>
+                    </fo:table-row>
+                    <fo:table-row>
+                      <fo:table-cell padding-left="3pt" padding-top="1pt" border-style="solid" border-width="thin" padding-right="0.25cm">
+                        <fo:block text-align="right">3</fo:block>
+                      </fo:table-cell>
+                    </fo:table-row>
+                    <fo:table-row>
+                      <fo:table-cell padding-left="3pt" padding-top="1pt" border-style="solid" border-width="thin" padding-right="0.25cm">
+                        <fo:block text-align="right">4</fo:block>
+                      </fo:table-cell>
+                    </fo:table-row>
+                    <fo:table-row>
+                      <fo:table-cell padding-left="3pt" padding-top="1pt" border-style="solid" border-width="thin" padding-right="0.25cm">
+                        <fo:block text-align="right">5</fo:block>
+                      </fo:table-cell>
+                    </fo:table-row>
+                    <fo:table-row>
+                      <fo:table-cell padding-left="3pt" padding-top="1pt" border-style="solid" border-width="thin" padding-right="0.25cm">
+                        <fo:block text-align="right">6</fo:block>
+                      </fo:table-cell>
+                    </fo:table-row>
+                    <fo:table-row>
+                      <fo:table-cell padding-left="3pt" padding-top="1pt" border-style="solid" border-width="thin" padding-right="0.25cm">
+                        <fo:block text-align="right">7</fo:block>
+                      </fo:table-cell>
+                    </fo:table-row>
+                    <fo:table-row>
+                      <fo:table-cell padding-left="3pt" padding-top="1pt" border-style="solid" border-width="thin" padding-right="0.25cm">
+                        <fo:block text-align="right">8</fo:block>
+                      </fo:table-cell>
+                    </fo:table-row>
+                  </fo:table-body>
+                </fo:table>
+              </fo:list-item-body>
+            </fo:list-item>
+          </fo:list-block>
+          <fo:block font-size="8pt">
+            <fo:block>trailing text...</fo:block>
+            <fo:block>trailing text...</fo:block>
+          </fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <true xpath="boolean(//pageViewport[@nr='1']//block[@prod-id='header-cell'])"/>
+    <eval expected="4" xpath="count(//pageViewport[@nr='1']//block[@prod-id='table']/block)"/>
+    
+    <true xpath="boolean(//pageViewport[@nr='2']//block[@prod-id='header-cell'])"/>
+    <eval expected="4" xpath="count(//pageViewport[@nr='2']//block[@prod-id='table']/block)"/>
+    
+    <true xpath="boolean(//pageViewport[@nr='3']//block[@prod-id='header-cell'])"/>
+    <eval expected="3" xpath="count(//pageViewport[@nr='3']//block[@prod-id='table']/block)"/>
+    
+  </checks>
+</testcase>
index 2145b43e8ab2875a93ec1b808ca420afb37cf6ad..eec4b6e1757a92fe60971a68a9ec3dafc40d142e 100644 (file)
@@ -34,7 +34,7 @@
       </fo:layout-master-set>\r
       <fo:page-sequence master-reference="normal" white-space-collapse="true">\r
         <fo:flow flow-name="xsl-region-body">\r
-          <fo:table>\r
+          <fo:table table-layout="fixed">\r
             <fo:table-column column-number="1" />\r
             <fo:table-column column-number="2" />\r
             <fo:table-column column-number="3" />\r
index 78968dd032b8f4b9b1c44ab3966456ef09b95db7..0b808638eedc7988a3c00c0c8a4180e312f7b299 100644 (file)
       </fo:layout-master-set>\r
       <fo:page-sequence master-reference="normal" white-space-collapse="true">\r
         <fo:flow flow-name="xsl-region-body">\r
-          <fo:table background-color="yellow" background-repeat="no-repeat" background-position-horizontal="center" background-position-vertical="center" background-image="##img">\r
+          <fo:table table-layout="fixed" background-color="yellow" \r
+                    background-repeat="no-repeat" \r
+                    background-position-horizontal="center" \r
+                    background-position-vertical="center" \r
+                    background-image="##img">\r
             <fo:table-column column-number="1" />\r
             <fo:table-column column-number="2" />\r
             <fo:table-column column-number="3" />\r
index 091ed49da55624102be026731182b7cb37079eb2..69c8f19b4773aa22fc1aa8b79104d99440bb07d4 100644 (file)
@@ -31,7 +31,8 @@
       </fo:layout-master-set>\r
       <fo:page-sequence master-reference="normal" white-space-collapse="true">\r
         <fo:flow flow-name="xsl-region-body">\r
-          <fo:table margin="0pt" padding="5%" background-color="yellow" border-collapse="separate">\r
+          <fo:table table-layout="fixed" margin="0pt" padding="5%" \r
+                    background-color="yellow" border-collapse="separate">\r
             <fo:table-column column-number="1" />\r
             <fo:table-column column-number="2" />\r
             <fo:table-column column-number="3" />\r
diff --git a/test/xsl/fo-page-sequence-splitter.xsl b/test/xsl/fo-page-sequence-splitter.xsl
new file mode 100644 (file)
index 0000000..d493ad2
--- /dev/null
@@ -0,0 +1,45 @@
+<?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$ -->
+<!-- Description: Splits an FO file into multiple FO files at page-sequence boundaries. -->
+<!-- Note: Requires Xalan-J! -->
+<xsl:stylesheet version="1.0"
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+    xmlns:fo="http://www.w3.org/1999/XSL/Format" 
+    xmlns:redirect="http://xml.apache.org/xalan/redirect" 
+    extension-element-prefixes="redirect">
+
+  <xsl:template match="fo:page-sequence">
+    <redirect:write select="concat('page-sequence-', position(), '.fo')">
+      <fo:root>
+        <xsl:apply-templates select="../fo:layout-master-set"/>
+        <xsl:apply-templates select="../fo:declarations"/>
+        <xsl:copy>
+          <xsl:apply-templates select="@*|node()"/>
+        </xsl:copy>
+      </fo:root>
+    </redirect:write>
+  </xsl:template>
+
+  <xsl:template match="@*|node()">
+    <xsl:copy>
+      <xsl:apply-templates select="@*|node()"/>
+    </xsl:copy>
+  </xsl:template>
+
+</xsl:stylesheet>
diff --git a/test/xsl/fo-replicator.xsl b/test/xsl/fo-replicator.xsl
new file mode 100644 (file)
index 0000000..0f2736c
--- /dev/null
@@ -0,0 +1,72 @@
+<?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$ -->
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
+  <xsl:param name="repeats" select="2"/>
+  <xsl:template match="@*|node()">
+    <xsl:param name="run"/>
+    <xsl:copy>
+      <xsl:apply-templates select="@*|node()">
+        <xsl:with-param name="run" select="$run"/>
+      </xsl:apply-templates>
+    </xsl:copy>
+  </xsl:template>
+  <xsl:template match="fo:root">
+    <fo:root>
+      <xsl:apply-templates select="@*|fo:layout-master-set|fo:declarations">
+        <xsl:with-param name="run" select="0"/>
+      </xsl:apply-templates>
+      <xsl:call-template name="repeat">
+        <xsl:with-param name="n" select="$repeats"/>
+        <xsl:with-param name="what" select="fo:page-sequence"/>
+      </xsl:call-template>
+    </fo:root>
+  </xsl:template>
+  <xsl:template name="repeat">
+    <xsl:param name="n"/>
+    <xsl:param name="what"/>
+    <xsl:if test="number($n) > 0">
+      <xsl:apply-templates select="$what">
+        <xsl:with-param name="run" select="$n"/>
+      </xsl:apply-templates>
+      <xsl:call-template name="repeat">
+        <xsl:with-param name="n" select="number($n) - 1"/>
+        <xsl:with-param name="what" select="$what"/>
+      </xsl:call-template>
+    </xsl:if>
+  </xsl:template>
+  <xsl:template match="@id">
+    <xsl:param name="run"/>
+    <xsl:attribute name="id"><xsl:value-of select="."/>-<xsl:value-of select="$run"/></xsl:attribute>
+  </xsl:template>
+  <xsl:template match="@ref-id">
+    <xsl:param name="run"/>
+    <xsl:attribute name="ref-id"><xsl:value-of select="."/>-<xsl:value-of select="$run"/></xsl:attribute>
+  </xsl:template>
+  <xsl:template match="@internal-destination">
+    <xsl:param name="run"/>
+    <xsl:attribute name="internal-destination"><xsl:value-of select="."/>-<xsl:value-of select="$run"/></xsl:attribute>
+  </xsl:template>
+  <xsl:template match="fo:page-number-citation">
+    <xsl:param name="run"/>
+    <fo:inline><xsl:value-of select="@ref-id"/></fo:inline>
+  </xsl:template>
+  <xsl:template match="fo:retrieve-marker|fo:marker">
+    <xsl:param name="run"/>
+  </xsl:template>
+</xsl:stylesheet>