]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Merge of branch Temp_KnuthStylePageBreaking back into HEAD.
authorJeremias Maerki <jeremias@apache.org>
Fri, 13 May 2005 19:16:54 +0000 (19:16 +0000)
committerJeremias Maerki <jeremias@apache.org>
Fri, 13 May 2005 19:16:54 +0000 (19:16 +0000)
Temp_KnuthStylePageBreaking branch and HEAD have been tagged prior to the merge, so merging uncommitted work from the branch should be easier.

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

106 files changed:
src/java/org/apache/fop/apps/CommandLineOptions.java
src/java/org/apache/fop/apps/FOUserAgent.java
src/java/org/apache/fop/area/AreaTreeHandler.java
src/java/org/apache/fop/area/BodyRegion.java
src/java/org/apache/fop/area/MainReference.java
src/java/org/apache/fop/area/NormalFlow.java
src/java/org/apache/fop/area/Page.java
src/java/org/apache/fop/area/PageViewport.java
src/java/org/apache/fop/area/RegionReference.java
src/java/org/apache/fop/area/RegionViewport.java
src/java/org/apache/fop/area/Span.java
src/java/org/apache/fop/fo/Constants.java
src/java/org/apache/fop/fo/FOPropertyMapping.java
src/java/org/apache/fop/fo/FObj.java
src/java/org/apache/fop/fo/PropertySets.java
src/java/org/apache/fop/fo/flow/Inline.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/Wrapper.java
src/java/org/apache/fop/fo/pagination/RegionBA.java
src/java/org/apache/fop/fo/pagination/RegionBody.java
src/java/org/apache/fop/fo/pagination/RegionSE.java
src/java/org/apache/fop/fo/pagination/SideRegion.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/AbstractBreaker.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java
src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/BasicLinkLayoutManager.java
src/java/org/apache/fop/layoutmgr/BidiLayoutManager.java
src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java
src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java
src/java/org/apache/fop/layoutmgr/BlockLevelLayoutManager.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java
src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/CharacterLayoutManager.java
src/java/org/apache/fop/layoutmgr/ContentLayoutManager.java
src/java/org/apache/fop/layoutmgr/ElementListUtils.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/ExternalGraphicLayoutManager.java
src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java
src/java/org/apache/fop/layoutmgr/ICLayoutManager.java
src/java/org/apache/fop/layoutmgr/InlineLayoutManager.java
src/java/org/apache/fop/layoutmgr/InlineLevelLayoutManager.java
src/java/org/apache/fop/layoutmgr/InlineStackingLayoutManager.java
src/java/org/apache/fop/layoutmgr/InstreamForeignObjectLM.java
src/java/org/apache/fop/layoutmgr/KnuthBlockBox.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/KnuthBox.java
src/java/org/apache/fop/layoutmgr/KnuthGlue.java
src/java/org/apache/fop/layoutmgr/KnuthInlineBox.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/KnuthParagraph.java
src/java/org/apache/fop/layoutmgr/KnuthPenalty.java
src/java/org/apache/fop/layoutmgr/KnuthPossPosIter.java
src/java/org/apache/fop/layoutmgr/KnuthSequence.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/LayoutContext.java
src/java/org/apache/fop/layoutmgr/LayoutManager.java
src/java/org/apache/fop/layoutmgr/LayoutManagerMaker.java
src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java
src/java/org/apache/fop/layoutmgr/LeaderLayoutManager.java
src/java/org/apache/fop/layoutmgr/LeafNodeLayoutManager.java
src/java/org/apache/fop/layoutmgr/LineLayoutManager.java
src/java/org/apache/fop/layoutmgr/LineLayoutPossibilities.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/MinOptMaxUtil.java
src/java/org/apache/fop/layoutmgr/NonLeafPosition.java
src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/PageNumberCitationLayoutManager.java
src/java/org/apache/fop/layoutmgr/PageNumberLayoutManager.java
src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java
src/java/org/apache/fop/layoutmgr/Position.java
src/java/org/apache/fop/layoutmgr/PositionIterator.java
src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java
src/java/org/apache/fop/layoutmgr/TextLayoutManager.java
src/java/org/apache/fop/layoutmgr/list/Item.java
src/java/org/apache/fop/layoutmgr/list/ListBlockLayoutManager.java
src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java
src/java/org/apache/fop/layoutmgr/table/Body.java [deleted file]
src/java/org/apache/fop/layoutmgr/table/Caption.java
src/java/org/apache/fop/layoutmgr/table/Cell.java
src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModel.java
src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModelEyeCatching.java
src/java/org/apache/fop/layoutmgr/table/Column.java [deleted file]
src/java/org/apache/fop/layoutmgr/table/ColumnSetup.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/table/EffRow.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/table/EmptyGridUnit.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/table/GridUnit.java
src/java/org/apache/fop/layoutmgr/table/PrimaryGridUnit.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/table/Row.java [deleted file]
src/java/org/apache/fop/layoutmgr/table/TableAndCaptionLayoutManager.java
src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java
src/java/org/apache/fop/layoutmgr/table/TableRowIterator.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/table/TableStepper.java [new file with mode: 0644]
src/java/org/apache/fop/render/AbstractRenderer.java
src/java/org/apache/fop/render/pdf/PDFRenderer.java
src/java/org/apache/fop/render/xml/XMLRenderer.java
src/java/org/apache/fop/traits/LayoutProps.java
test/layoutengine/testcases/breaks1.xml
test/layoutengine/testcases/breaks2.xml
test/layoutengine/testcases/keep-together1.xml [new file with mode: 0644]
test/layoutengine/testcases/keep-with-next1.xml [new file with mode: 0644]
test/layoutengine/testcases/keep-with-previous1.xml [new file with mode: 0644]
test/layoutengine/testcases/multi-column1.xml [new file with mode: 0644]
test/layoutengine/testcases/multi-column2.xml [new file with mode: 0644]
test/layoutengine/testcases/page-master3.xml
test/layoutengine/testcases/page-master4.xml [new file with mode: 0644]

index 42609311a724ea5198a79c6a6c2f636efeb1385c..edd0d8e5e5a7ebf1e4e7e3a0cab68fd404c2cdb4 100644 (file)
@@ -149,6 +149,8 @@ public class CommandLineOptions implements Constants {
                 suppressLowLevelAreas = Boolean.TRUE;
             } else if (args[i].equals("-d")) {
                 setLogLevel("debug");
+            } else if (args[i].equals("-r")) {
+                foUserAgent.setStrictValidation(false);
             } else if (args[i].equals("-q") || args[i].equals("--quiet")) {
                 setLogLevel("error");
             } else if (args[i].equals("-fo")) {
@@ -612,6 +614,7 @@ public class CommandLineOptions implements Constants {
             + "  -q          quiet mode  \n"
             + "  -c cfg.xml  use additional configuration file cfg.xml\n"
             + "  -l lang     the language to use for user information \n"
+            + "  -r          relaxed/less strict validation (where available)\n"
             + "  -s          for area tree XML, down to block areas only\n"
             + "  -v          to show FOP version being used\n\n"
             + " [INPUT]  \n"
index df263149c8c6fc6921b0f8924ba399b98ac947e9..d15b5d735b2551554b358735e08860cbf702a371 100644 (file)
@@ -85,7 +85,7 @@ public class FOUserAgent {
      * behavior for FOP.  However, this flag, if set, provides the user the
      * ability for FOP to halt on all content model violations if desired.   
      */ 
-    private boolean strictValidation = false;
+    private boolean strictValidation = true;
 
     /* Additional fo.ElementMapping subclasses set by user */
     private ArrayList additionalElementMappings = null;
index 2d6328f132af654683007be9c173e21a096eb5cc..ca0f87c7d73a37530827e1f1275e1b27407d6380 100644 (file)
@@ -230,16 +230,9 @@ public class AreaTreeHandler extends FOEventHandler {
         // If no main flow, nothing to layout!
         if (pageSequence.getMainFlow() != null) {
             PageSequenceLayoutManager pageSLM;
-            try {
-                pageSLM = (PageSequenceLayoutManager)
-                getLayoutManagerMaker().makeLayoutManager(pageSequence);
-            } catch (FOPException e) {
-                log.error("Failed to create a PageSequenceLayoutManager; "
-                        + "no pages will be laid out:");
-                log.error(e.getMessage());
-                return;
-            }
-            pageSLM.setAreaTreeHandler(this);
+            pageSLM =
+                getLayoutManagerMaker().makePageSequenceLayoutManager(this,
+                    pageSequence);
             pageSLM.activateLayout();
         }
     }
index c4223db8b66dffd8ea3fba2497f1a13967e5cafe..6283305409acb88a6bd8c8e77344f4f4ba24593f 100644 (file)
 package org.apache.fop.area;
 
 import org.apache.fop.fo.Constants;
-import org.apache.fop.fo.pagination.RegionBody;
 
 /**
- * This class is a container for all areas that may be generated by
+ * This class is a container for the areas that may be generated by
  * an fo:region-body.  It extends the RegionReference that is used
  * directly by the other region classes.
  * See fo:region-body definition in the XSL Rec for more information.
@@ -34,31 +33,15 @@ public class BodyRegion extends RegionReference {
     private int columnGap;
     private int columnCount;
 
-    /**
-     * Create a new body region area.
-     * This sets the region reference area class to BODY.
-     */
-    public BodyRegion() {
-        super(Constants.FO_REGION_BODY);
-        addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
-        mainReference = new MainReference();
-    }
-
     /**
      * Constructor which can read traits directly
      * from an fo:region-body formatting object.
      */
-    public BodyRegion(RegionBody rb) {
-        this();
-        columnCount = rb.getColumnCount();
-        columnGap = rb.getColumnGap();
-        if ((columnCount > 1) && (rb.getOverflow() == Constants.EN_SCROLL)) {
-            // recover by setting 'column-count' to 1. This is allowed but
-            // not required by the spec.
-            log.error("Setting 'column-count' to 1 because "
-                    + "'overflow' is set to 'scroll'");
-            columnCount = 1;
-        }
+    public BodyRegion(int columnCount, int columnGap, RegionViewport parent) {
+        super(Constants.FO_REGION_BODY, parent);
+        this.columnCount = columnCount;
+        this.columnGap = columnGap;
+        mainReference = new MainReference(this);
     }
 
     /**
@@ -89,42 +72,11 @@ public class BodyRegion extends RegionReference {
         this.columnGap = colGap;
     }
 
-    /**
-     * Set the before float area.
-     *
-     * @param bf the before float area
-     */
-    public void setBeforeFloat(BeforeFloat bf) {
-        beforeFloat = bf;
-    }
-
-    /**
-     * Set the main reference area.
-     *
-     * @param mr the main reference area
-     */
-    public void setMainReference(MainReference mr) {
-        mainReference = mr;
-    }
-
-    /**
-     * Set the footnote area.
-     *
-     * @param foot the footnote area
-     */
-    public void setFootnote(Footnote foot) {
-        footnote = foot;
+    /** @return the column-gap value */
+    public int getColumnGap() {
+        return this.columnGap;
     }
-
-    /**
-     * Get the before float area.
-     *
-     * @return the before float area
-     */
-    public BeforeFloat getBeforeFloat() {
-        return beforeFloat;
-    }
-
+   
     /**
      * Get the main reference area.
      *
@@ -144,12 +96,27 @@ public class BodyRegion extends RegionReference {
     }
 
 
+    /**
+     * Get the before float area.
+     *
+     * @return the before float area
+     */
+    public BeforeFloat getBeforeFloat() {
+        if (beforeFloat == null) {
+            beforeFloat = new BeforeFloat();
+        }
+        return beforeFloat;
+    }
+
     /**
      * Get the footnote area.
      *
      * @return the footnote area
      */
     public Footnote getFootnote() {
+        if (footnote == null) {
+            footnote = new Footnote();
+        }
         return footnote;
     }
 
@@ -159,11 +126,9 @@ public class BodyRegion extends RegionReference {
      * @return a shallow copy of this object
      */
     public Object clone() {
-        BodyRegion br = new BodyRegion();
+        BodyRegion br = new BodyRegion(columnCount, columnGap, regionViewport);
         br.setCTM(getCTM());
         br.setIPD(getIPD());
-        br.columnGap = columnGap;
-        br.columnCount = columnCount;
         br.beforeFloat = beforeFloat;
         br.mainReference = mainReference;
         br.footnote = footnote;
index 5274d49558ba28d3cdbbbcdfca652313250953f0..85a5d7ec17f5b74dbc713ae30cd5f5b1622b2962 100644 (file)
@@ -28,15 +28,17 @@ import java.util.Iterator;
  * See fo:region-body definition in the XSL Rec for more information.
  */
 public class MainReference extends Area {
+
+    private BodyRegion parent;
     private List spanAreas = new java.util.ArrayList();
-    private int columnGap;
     private int width;
     private boolean isEmpty = true;
 
     /**
      * Constructor
      */
-    public MainReference() {
+    public MainReference(BodyRegion parent) {
+        this.parent = parent;
         addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
     }
       
@@ -45,8 +47,15 @@ public class MainReference extends Area {
      *
      * @param span the span area to add
      */
-    public void addSpan(Span span) {
-        spanAreas.add(span);
+    public Span createSpan(boolean spanAll) {
+        RegionViewport rv = parent.getRegionViewport();
+        int ipdWidth = (int) parent.getIPD() -
+            rv.getBorderAndPaddingWidthStart() - rv.getBorderAndPaddingWidthEnd();
+        
+        Span newSpan = new Span(((spanAll) ? 1 : getColumnCount()), 
+                getColumnGap(), ipdWidth);
+        spanAreas.add(newSpan);
+        return getCurrentSpan(); 
     }
 
     /**
@@ -58,6 +67,15 @@ public class MainReference extends Area {
         return spanAreas;
     }
 
+    /**
+     * Get the span area currently being filled (i.e., the last span created)
+     *
+     * @return the active span 
+     */
+    public Span getCurrentSpan() {
+        return (Span) spanAreas.get(spanAreas.size()-1);
+    }
+
     /**
      * indicates whether any child areas have been added to this reference area
      * this is achieved by looping through each span
@@ -86,13 +104,14 @@ public class MainReference extends Area {
         return isEmpty;
     }
 
-    /**
-     * Get the column gap in millipoints.
-     *
-     * @return the column gap in millipoints
-     */
+    /** @return the number of columns */
+    public int getColumnCount() {
+        return parent.getColumnCount();
+    }
+
+    /** @return the column gap in millipoints */
     public int getColumnGap() {
-        return columnGap;
+        return parent.getColumnGap();
     }
 
     /**
index 32dfbc7d86ad32cfdb4b3874b96bbd82102f98cd..ce39eb1f775ad8b0d9a2a5dcc90a060641ff4b31 100644 (file)
@@ -26,9 +26,11 @@ package org.apache.fop.area;
 public class NormalFlow extends BlockParent {
     /**
      * Constructor.
+     * @param ipd of Normal flow object
      */
-    public NormalFlow() {
+    public NormalFlow(int ipd) {
         addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
+        setIPD(ipd);
     }
 }
 
index 9c1a1aafd3c98ae68edfd50c606066847928a588..858c0722d7e87d6d0d1a415b4c2899ddf85fbdb7 100644 (file)
 
 package org.apache.fop.area;
 
+import java.awt.Rectangle;
+import java.awt.geom.Rectangle2D;
 import java.io.Serializable;
 import java.util.HashMap;
+import java.util.Iterator;
 
+import org.apache.fop.datatypes.FODimension;
+import org.apache.fop.datatypes.PercentBase;
 import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.pagination.Region;
+import org.apache.fop.fo.pagination.RegionBody;
+import org.apache.fop.fo.pagination.SimplePageMaster;
+import org.apache.fop.fo.properties.CommonMarginBlock;
+import org.apache.fop.layoutmgr.TraitSetter;
 
 /**
  * The page.
@@ -45,13 +55,109 @@ public class Page implements Serializable, Cloneable {
     // temporary map of unresolved objects used when serializing the page
     private HashMap unresolved = null;
 
+    /**
+     *  Empty constructor, for cloning 
+     */
+    public Page() {
+    }
+
+    /**
+     * Constructor
+     * @param spm SimplePageMaster containing the dimensions for this
+     *            page-reference-area
+     */
+    public Page(SimplePageMaster spm) {
+        int pageWidth = spm.getPageWidth().getValue();
+        int pageHeight = spm.getPageHeight().getValue();
+
+        // Get absolute margin properties (top, left, bottom, right)
+        CommonMarginBlock mProps = spm.getCommonMarginBlock();
+
+        /*
+         * Create the page reference area rectangle (0,0 is at top left
+         * of the "page media" and y increases
+         * when moving towards the bottom of the page.
+         * The media rectangle itself is (0,0,pageWidth,pageHeight).
+         */
+        Rectangle pageRefRect =
+            new Rectangle(mProps.marginLeft.getValue(), mProps.marginTop.getValue(),
+            pageWidth - mProps.marginLeft.getValue() - mProps.marginRight.getValue(),
+            pageHeight - mProps.marginTop.getValue() - mProps.marginBottom.getValue());
+
+        // Set up the CTM on the page reference area based on writing-mode
+        // and reference-orientation
+        FODimension reldims = new FODimension(0, 0);
+        CTM pageCTM = CTM.getCTMandRelDims(spm.getReferenceOrientation(),
+            spm.getWritingMode(), pageRefRect, reldims);
+
+        // Create a RegionViewport/ reference area pair for each page region
+        RegionReference rr = null;
+        for (Iterator regenum = spm.getRegions().values().iterator();
+            regenum.hasNext();) {
+            Region r = (Region)regenum.next();
+            RegionViewport rvp = makeRegionViewport(r, reldims, pageCTM);
+            r.setLayoutDimension(PercentBase.BLOCK_IPD, rvp.getIPD());
+            r.setLayoutDimension(PercentBase.BLOCK_BPD, rvp.getBPD());
+            if (r.getNameId() == Constants.FO_REGION_BODY) {
+                RegionBody rb = (RegionBody) r;
+                rr = new BodyRegion(rb.getColumnCount(), rb.getColumnGap(),
+                       rvp);
+            } else {
+                rr = new RegionReference(r.getNameId(), rvp);
+            }
+            setRegionReferencePosition(rr, r, rvp.getViewArea());
+            rvp.setRegionReference(rr);
+            setRegionViewport(r.getNameId(), rvp);
+       }
+    }
+
+    /**
+     * Creates a RegionViewport Area object for this pagination Region.
+     * @param reldims relative dimensions
+     * @param pageCTM page coordinate transformation matrix
+     * @return the new region viewport
+     */
+    private RegionViewport makeRegionViewport(Region r, FODimension reldims, CTM pageCTM) {
+        Rectangle2D relRegionRect = r.getViewportRectangle(reldims);
+        Rectangle2D absRegionRect = pageCTM.transform(relRegionRect);
+        // Get the region viewport rectangle in absolute coords by
+        // transforming it using the page CTM
+        RegionViewport rv = new RegionViewport(absRegionRect);
+        rv.setBPD((int)relRegionRect.getHeight());
+        rv.setIPD((int)relRegionRect.getWidth());
+        TraitSetter.addBackground(rv, r.getCommonBorderPaddingBackground());
+        rv.setClip(r.getOverflow() == Constants.EN_HIDDEN 
+                || r.getOverflow() == Constants.EN_ERROR_IF_OVERFLOW);
+        return rv;
+    }
+   
+    /**
+     * Set the region reference position within the region viewport.
+     * This sets the transform that is used to place the contents of
+     * the region reference.
+     *
+     * @param rr the region reference area
+     * @param r the region-xxx formatting object
+     * @param absRegVPRect The region viewport rectangle in "absolute" coordinates
+     * where x=distance from left, y=distance from bottom, width=right-left
+     * height=top-bottom
+     */
+    private void setRegionReferencePosition(RegionReference rr, Region r, 
+                                  Rectangle2D absRegVPRect) {
+        FODimension reldims = new FODimension(0, 0);
+        rr.setCTM(CTM.getCTMandRelDims(r.getReferenceOrientation(),
+                r.getWritingMode(), absRegVPRect, reldims));
+        rr.setIPD(reldims.ipd);
+        rr.setBPD(reldims.bpd);
+    }    
+    
     /**
      * Set the region on this page.
      *
      * @param areaclass the area class of the region to set
      * @param port the region viewport to set
      */
-    public void setRegionViewport(int areaclass, RegionViewport port) {
+    private void setRegionViewport(int areaclass, RegionViewport port) {
         if (areaclass == Constants.FO_REGION_BEFORE) {
             regionBefore = port;
         } else if (areaclass == Constants.FO_REGION_START) {
@@ -97,7 +203,7 @@ public class Page implements Serializable, Cloneable {
             return true;
         }
         else {
-            BodyRegion body = (BodyRegion)regionBody.getRegion();
+            BodyRegion body = (BodyRegion)regionBody.getRegionReference();
             return body.isEmpty();
         }
     }
index 48c9dd732f285583aab4a036dc447083261fa183..d3e9c6bc01666c328282079fcaf8fb8b4cae99f3 100644 (file)
@@ -18,6 +18,7 @@
 
 package org.apache.fop.area;
 
+import java.awt.Rectangle;
 import java.awt.geom.Rectangle2D;
 import java.io.ObjectOutputStream;
 import java.io.ObjectInputStream;
@@ -31,6 +32,7 @@ import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.pagination.SimplePageMaster;
 
 /**
  * Page viewport that specifies the viewport area and holds the page contents.
@@ -46,6 +48,7 @@ public class PageViewport implements Resolvable, Cloneable {
     private Rectangle2D viewArea;
     private boolean clip = false;
     private String pageNumberString = null;
+    private SimplePageMaster spm = null;
 
     // list of id references and the rectangle on the page
     private Map idReferences = null;
@@ -72,12 +75,26 @@ public class PageViewport implements Resolvable, Cloneable {
 
     /**
      * Create a page viewport.
-     * @param p the page reference area that holds the contents
-     * @param bounds the bounds of this viewport
+     * @param spm SimplePageMaster indicating the page and region dimensions
      */
-    public PageViewport(Page p, Rectangle2D bounds) {
-        page = p;
-        viewArea = bounds;
+    public PageViewport(SimplePageMaster spm) {
+        this.spm = spm;
+        int pageWidth = spm.getPageWidth().getValue();
+        int pageHeight = spm.getPageHeight().getValue();
+        viewArea = new Rectangle(0, 0, pageWidth, pageHeight);
+        page = new Page(spm);
+    }
+
+    /**
+     * Create a page viewport 
+     * @param spm SimplePageMaster indicating the page and region dimensions
+     * @param p Page Reference Area
+     * @param bounds Page Viewport dimensions
+     */
+    public PageViewport(SimplePageMaster spm, Page p, Rectangle2D bounds) {
+        this.spm = spm;
+        this.page = p;
+        this.viewArea = bounds;
     }
 
     /**
@@ -85,8 +102,29 @@ public class PageViewport implements Resolvable, Cloneable {
      * @return BodyRegion object
      */
     public BodyRegion getBodyRegion() {
-        return (BodyRegion)
-            getPage().getRegionViewport(Constants.FO_REGION_BODY).getRegion();
+        return (BodyRegion) getPage().getRegionViewport(
+                Constants.FO_REGION_BODY).getRegionReference();
+    }    
+
+    /**
+     * Convenience method to create a new Span for this
+     * this PageViewport.
+     * 
+     * @param spanAll whether this is a single-column span
+     * @return Span object created
+     */
+    public Span createSpan(boolean spanAll) {
+        return getBodyRegion().getMainReference().createSpan(spanAll);
+    }    
+
+    /**
+     * Convenience method to get the span-reference-area currently
+     * being processed
+     * 
+     * @return span currently being processed.
+     */
+    public Span getCurrentSpan() {
+        return getBodyRegion().getMainReference().getCurrentSpan();
     }    
 
     /**
@@ -229,6 +267,11 @@ public class PageViewport implements Resolvable, Cloneable {
      */
     public void addMarkers(Map marks, boolean starting,
             boolean isfirst, boolean islast) {
+
+        if (marks == null) {
+            return;
+        }
+        
         // at the start of the area, register is-first and any areas
         if (starting) {
             if (isfirst) {
@@ -385,7 +428,7 @@ public class PageViewport implements Resolvable, Cloneable {
      */
     public Object clone() {
         Page p = (Page)page.clone();
-        PageViewport ret = new PageViewport(p, (Rectangle2D)viewArea.clone());
+        PageViewport ret = new PageViewport(spm, p, (Rectangle2D)viewArea.clone());
         return ret;
     }
 
@@ -407,4 +450,10 @@ public class PageViewport implements Resolvable, Cloneable {
         sb.append(getPageNumberString());
         return sb.toString();
     }
+    /**
+     * @return Returns the spm.
+     */
+    public SimplePageMaster getSPM() {
+        return spm;
+    }
 }
\ No newline at end of file
index e1ebf97f31783d7679f30028c0b5d12207d9e992..6f671e96f3bde86e131454120cdd1d6d27c913d8 100644 (file)
@@ -24,26 +24,29 @@ import java.util.List;
 import org.apache.fop.fo.Constants;
 
 /**
- * This is a region reference area for the page regions.
- * This area represents a region on the page. It is cloneable
+ * This is a region reference area for a page regions.
+ * This area is the direct child of a region-viewport-area. It is cloneable
  * so the page master can make copies from the original page and regions.
  */
 public class RegionReference extends Area implements Cloneable {
     private int regionClass = Constants.FO_REGION_BEFORE;
     private CTM ctm;
+
     // the list of block areas from the static flow
     private List blocks = new ArrayList();
-
-    private int bpd;
+    
+    // the parent RegionViewport for this object
+    protected RegionViewport regionViewport;
 
     /**
      * Create a new region reference area.
      *
      * @param type the region class type
      */
-    public RegionReference(int type) {
+    public RegionReference(int type, RegionViewport parent) {
         regionClass = type;
         addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
+        regionViewport = parent;
     }
 
     /**
@@ -58,6 +61,13 @@ public class RegionReference extends Area implements Cloneable {
     public void setCTM(CTM ctm) {
         this.ctm = ctm;
     }
+    
+    /**
+     * @return Returns the parent RegionViewport.
+     */
+    public RegionViewport getRegionViewport() {
+        return regionViewport;
+    }
 
     /**
      * Get the current transform of this region.
@@ -95,24 +105,6 @@ public class RegionReference extends Area implements Cloneable {
         blocks.add(block);
     }
 
-    /**
-     * Set the block-progression-dimension.
-     *
-     * @return the footnote area
-     */
-    public void setBPD(int bpd) {
-        this.bpd = bpd;
-    }
-
-    /**
-     * Set the block-progression-dimension.
-     *
-     * @return the footnote area
-     */
-    public int getBPD() {
-        return bpd;
-    }
-
     /**
      * Clone this region.
      * This is used when cloning the page by the page master.
@@ -121,7 +113,7 @@ public class RegionReference extends Area implements Cloneable {
      * @return a copy of this region reference area
      */
     public Object clone() {
-        RegionReference rr = new RegionReference(regionClass);
+        RegionReference rr = new RegionReference(regionClass, regionViewport);
         rr.ctm = ctm;
         rr.setIPD(getIPD());
         return rr;
index 3e138131058a05ef74b64b9c0f376617bb3c03b4..ff3146e17f000cf0a782c2c2108bb5a5d2be15cc 100644 (file)
@@ -23,17 +23,19 @@ import java.io.IOException;
 import java.util.HashMap;
 
 /**
- * Region Viewport reference area.
- * This area is the viewport for a region and contains a region area.
+ * Region Viewport area.
+ * This object represents the region-viewport-area.  It has a 
+ * region-reference-area as its child.  These areas are described
+ * in the fo:region-body description in the XSL Recommendation.
  */
 public class RegionViewport extends Area implements Cloneable {
     // this rectangle is relative to the page
-    private RegionReference region;
+    private RegionReference regionReference;
     private Rectangle2D viewArea;
     private boolean clip = false;
 
     /**
-     * Create a new region viewport.
+     * Create a new region-viewport-area
      *
      * @param viewArea the view area of this viewport
      */
@@ -43,21 +45,21 @@ public class RegionViewport extends Area implements Cloneable {
     }
 
     /**
-     * Set the region for this region viewport.
+     * Set the region-reference-area for this region viewport.
      *
-     * @param reg the child region inside this viewport
+     * @param reg the child region-reference-area inside this viewport
      */
-    public void setRegion(RegionReference reg) {
-        region = reg;
+    public void setRegionReference(RegionReference reg) {
+        regionReference = reg;
     }
 
     /**
-     * Get the region for this region viewport.
+     * Get the region-reference-area for this region viewport.
      *
-     * @return the child region inside this viewport
+     * @return the child region-reference-area inside this viewport
      */
-    public RegionReference getRegion() {
-        return region;
+    public RegionReference getRegionReference() {
+        return regionReference;
     }
 
     /**
@@ -69,6 +71,11 @@ public class RegionViewport extends Area implements Cloneable {
         clip = c;
     }
 
+    /** @return true if the viewport should be clipped. */
+    public boolean isClip() {
+        return this.clip;
+    }
+    
     /**
      * Get the view area of this viewport.
      *
@@ -86,7 +93,7 @@ public class RegionViewport extends Area implements Cloneable {
         out.writeFloat((float) viewArea.getHeight());
         out.writeBoolean(clip);
         out.writeObject(props);
-        out.writeObject(region);
+        out.writeObject(regionReference);
     }
 
     private void readObject(java.io.ObjectInputStream in)
@@ -95,7 +102,7 @@ public class RegionViewport extends Area implements Cloneable {
                                          in.readFloat(), in.readFloat());
         clip = in.readBoolean();
         props = (HashMap)in.readObject();
-        setRegion((RegionReference) in.readObject());
+        setRegionReference((RegionReference) in.readObject());
     }
 
     /**
@@ -106,7 +113,7 @@ public class RegionViewport extends Area implements Cloneable {
      */
     public Object clone() {
         RegionViewport rv = new RegionViewport((Rectangle2D)viewArea.clone());
-        rv.region = (RegionReference)region.clone();
+        rv.regionReference = (RegionReference)regionReference.clone();
         if (props != null) {
             rv.props = (HashMap)props.clone();
         }
index ff289d2ca2890714cd671e83a597999e1e7cf9f4..e080ea5990d478ce4013b1382fa88b5fe214b841 100644 (file)
@@ -32,36 +32,37 @@ public class Span extends Area {
     // the list of flow reference areas in this span area
     private List flowAreas;
     private int height;
-    private int columnCount;
+    private int colCount;
+    private int colGap;
+    private int colWidth; // width for each normal flow, calculated value
 
     /**
      * Create a span area with the number of columns for this span area.
      *
-     * @param cols the number of columns in the span
-     * @param ipd the ipd of the span 
+     * @param colCount the number of columns in the span
+     * @param colGap the column gap between each column 
+     * @param ipd the total ipd of the span 
      */
-    public Span(int cols, int ipd) {
+    public Span(int colCount, int colGap, int ipd) {
         addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
-        columnCount = cols;
+        this.colCount = colCount;
+        this.colGap = colGap;
         this.ipd = ipd;
-        flowAreas = new java.util.ArrayList(cols);
-        addAdditionalNormalFlow(); // one normal flow is required
+        createNormalFlows();
     }
 
     /**
-     * Create a new normal flow and add it to this span area
-     *
-     * @return the newly made NormalFlow object
+     * Create the normal flows for this Span
      */
-    public NormalFlow addAdditionalNormalFlow() {
-        if (flowAreas.size() >= columnCount) { // internal error
-            throw new IllegalStateException("Maximum number of flow areas (" +
-                    columnCount + ") for this span reached.");
+    private void createNormalFlows() {
+        flowAreas = new java.util.ArrayList(colCount);        
+        colWidth = (ipd - ((colCount - 1) * colGap)) / colCount;
+
+        for (int i=0; i< colCount; i++) {
+            NormalFlow newFlow = new NormalFlow(colWidth);
+            newFlow.setIPD(getIPD());
+            flowAreas.add(newFlow);
         }
-        NormalFlow newFlow = new NormalFlow();
-        newFlow.setIPD(getIPD());
-        flowAreas.add(newFlow);
-        return newFlow;
     }
 
     /**
@@ -70,16 +71,16 @@ public class Span extends Area {
      * @return the number of columns defined for this span area
      */
     public int getColumnCount() {
-        return columnCount;
+        return colCount;
     }
 
     /**
-     * Get the count of normal flows for this span area.
+     * Get the width of a single column within this Span
      *
-     * @return the number of normal flows attached to this span
+     * @return the width of a single column
      */
-    public int getNormalFlowCount() {
-        return flowAreas.size();
+    public int getColumnWidth() {
+        return colWidth;
     }
 
     /**
@@ -91,15 +92,21 @@ public class Span extends Area {
         return height;
     }
 
+
     /**
      * Get the normal flow area for a particular column.
      *
-     * @param count the column number for the flow
+     * @param colRequested the zero-based column number of the flow
      * @return the flow area for the requested column
      */
-    public NormalFlow getNormalFlow(int columnNumber) {
-        return (NormalFlow) flowAreas.get(columnNumber);
+    public NormalFlow getNormalFlow(int colRequested) {
+        if (colRequested >= 0 && colRequested < colCount) {
+            return (NormalFlow) flowAreas.get(colRequested);
+        } else { // internal error
+            throw new IllegalArgumentException("Invalid column number " + 
+                    colRequested + " requested; only 0-" + (colCount-1) +
+                    " available.");
+        }
     }
-
 }
 
index fb2dc7fe9d025893fc043abf9ddd2eabc10bd42e..9a0423bc4398cb0dae56115d982bbc94b3ff963d 100644 (file)
@@ -375,7 +375,8 @@ public interface Constants {
     int PR_INTRUSION_DISPLACE = 247;
     int PR_INDEX_CLASS = 248;   // XSL 1.1
     int PR_INDEX_KEY = 249;     // XSL 1.1
-    int PROPERTY_COUNT = 249;
+    int PR_X_BLOCK_PROGRESSION_UNIT = 250; //Custom extension
+    int PROPERTY_COUNT = 250;
 
     // compound property constants
 
@@ -553,5 +554,7 @@ public interface Constants {
     int EN_VISIBLE = 159;
     int EN_WIDER = 160;
     int EN_WRAP = 161;
-    int ENUM_COUNT = 161;
+    int EN_X_FILL = 162; //non-standard for display-align
+    int EN_X_DISTRIBUTE = 163; //non-standard for display-align
+    int ENUM_COUNT = 163;
 }
index a1328b9bfa69e71eaff2c5c665ce8cba8abc6bb2..11b69080df129a637c128d7a5c83746b06c6d4d6 100644 (file)
@@ -1354,6 +1354,8 @@ public class FOPropertyMapping implements Constants {
         m.addEnum("after", getEnumProperty(EN_AFTER, "AFTER"));
         m.addEnum("center", getEnumProperty(EN_CENTER, "CENTER"));
         m.addEnum("auto", getEnumProperty(EN_AUTO, "AUTO"));
+/*LF*/  m.addEnum("distribute", getEnumProperty(EN_X_DISTRIBUTE, "DISTRIBUTE"));
+/*LF*/  m.addEnum("fill", getEnumProperty(EN_X_FILL, "FILL"));
         m.setDefault("auto");
         addPropertyMaker("display-align", m);
 
@@ -1536,6 +1538,12 @@ public class FOPropertyMapping implements Constants {
         l.setPercentBase(LengthBase.BLOCK_WIDTH);
         l.setDefault("auto");
         addPropertyMaker("width", l);
+
+/*LF*/  // block-progression-unit (**CUSTOM EXTENSION**)
+/*LF*/  l  = new LengthProperty.Maker(PR_X_BLOCK_PROGRESSION_UNIT);
+/*LF*/  l.setInherited(false);
+/*LF*/  l.setDefault("0pt");
+/*LF*/  addPropertyMaker("block-progression-unit", l);
     }
     
     private void createBlockAndLineProperties() {
index b3982bfa5069a9a9ad55399965920ec6e1a75280..b2b79eceb26d9b972debde553ca2bdbf12fc2e53 100644 (file)
@@ -18,9 +18,8 @@
 
 package org.apache.fop.fo;
 
-import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
 import java.util.Set;
@@ -39,7 +38,7 @@ public abstract class FObj extends FONode implements Constants {
     public static PropertyMaker[] propertyListTable = null;
     
     /** The immediate child nodes of this node. */
-    public ArrayList childNodes = null;
+    public List childNodes = null;
 
     /** Used to indicate if this FO is either an Out Of Line FO (see rec)
         or a descendant of one.  Used during validateChildNode() FO 
@@ -164,7 +163,7 @@ public abstract class FObj extends FONode implements Constants {
                 addMarker((Marker) child);
         } else {
             if (childNodes == null) {
-                childNodes = new ArrayList();
+                childNodes = new java.util.ArrayList();
             }
             childNodes.add(child);
         }
@@ -198,7 +197,7 @@ public abstract class FObj extends FONode implements Constants {
      */
     public void setLayoutDimension(PercentBase.LayoutDimension key, int dimension) {
         if (layoutDimension == null) {
-            layoutDimension = new HashMap();
+            layoutDimension = new java.util.HashMap();
         }
         layoutDimension.put(key, new Integer(dimension));
     }
@@ -210,7 +209,7 @@ public abstract class FObj extends FONode implements Constants {
      */
     public void setLayoutDimension(PercentBase.LayoutDimension key, float dimension) {
         if (layoutDimension == null) {
-            layoutDimension = new HashMap();
+            layoutDimension = new java.util.HashMap();
         }
         layoutDimension.put(key, new Float(dimension));
     }
@@ -307,7 +306,7 @@ public abstract class FObj extends FONode implements Constants {
             }
         }
         if (markers == null) {
-            markers = new HashMap();
+            markers = new java.util.HashMap();
         }
         if (!markers.containsKey(mcname)) {
             markers.put(mcname, marker);
index c00b15782426292bc870b5d54d2d1f989512ee90..3209cb58b2780d18f6050a5125a695cf75d461fe 100644 (file)
@@ -395,6 +395,7 @@ public class PropertySets {
         elem.addProperties(CommonBorderPaddingBackgroundProperties);
         elem.addProperties(CommonMarginPropertiesBlock);
         elem.addProperty(Constants.PR_BLOCK_PROGRESSION_DIMENSION);
+        elem.addProperty(Constants.PR_X_BLOCK_PROGRESSION_UNIT);
         elem.addProperty(Constants.PR_PAGE_BREAK_AFTER);
         elem.addProperty(Constants.PR_PAGE_BREAK_BEFORE);
         elem.addProperty(Constants.PR_BREAK_AFTER);
index 82d1db9feebf8a7ad6cdc9d780c8ccde36e3c951..f396c7dc2d7220b1c1572e629778e46c8add7768 100644 (file)
@@ -28,7 +28,6 @@ import org.apache.fop.fo.InlineCharIterator;
 import org.apache.fop.fo.PropertyList;
 import org.apache.fop.fo.ValidationException;
 import org.apache.fop.fo.properties.CommonRelativePosition;
-import org.apache.fop.fo.properties.CommonTextDecoration;
 import org.apache.fop.fo.properties.KeepProperty;
 import org.apache.fop.fo.properties.LengthRangeProperty;
 
index 96c99b3e45b328560a338f9326cdfc2c09233e97..15fe81aed06a9e4a4bbf4c602971a497da0dfb7d 100644 (file)
@@ -20,8 +20,6 @@ package org.apache.fop.fo.flow;
 
 import java.util.HashMap;
 import java.util.Iterator;
-import java.util.List;
-import java.util.ListIterator;
 
 
 import org.xml.sax.Locator;
@@ -29,7 +27,6 @@ import org.xml.sax.Locator;
 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.FObj;
 import org.apache.fop.fo.FObjMixed;
 import org.apache.fop.fo.PropertyList;
@@ -60,7 +57,17 @@ public class Marker extends FObjMixed {
      * @see org.apache.fop.fo.FObj#bind(PropertyList)
      */
     public void bind(PropertyList pList) throws FOPException {
+        if (findAncestor(FO_FLOW) < 0) {
+            invalidChildError(locator, FO_URI, "marker", 
+                "An fo:marker is permitted only as the descendant " +
+                "of an fo:flow");
+        }
+        
         markerClassName = pList.get(PR_MARKER_CLASS_NAME).getString();
+        
+        if (markerClassName == null || markerClassName.equals("")) {
+            missingPropertyError("marker-class-name");
+        }        
     }
     
     /**
index 744728ae20f72bfb5b0f0c0feab01031547f0737..9992d52d6afd85d74342a9af73f91e463fccf8e7 100644 (file)
@@ -22,7 +22,6 @@ import java.util.Map;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.ArrayList;
-import java.util.List;
 
 import org.xml.sax.Locator;
 
@@ -66,9 +65,19 @@ public class RetrieveMarker extends FObjMixed {
      * @see org.apache.fop.fo.FObj#bind(PropertyList)
      */
     public void bind(PropertyList pList) throws FOPException {
+        if (findAncestor(FO_STATIC_CONTENT) < 0) {
+            invalidChildError(locator, FO_URI, "retrieve-marker", 
+                "An fo:retrieve-marker is permitted only as the " +
+                " descendant of an fo:static-content.");
+        }
+
         retrieveClassName = pList.get(PR_RETRIEVE_CLASS_NAME).getString();
         retrievePosition = pList.get(PR_RETRIEVE_POSITION).getEnum();
         retrieveBoundary = pList.get(PR_RETRIEVE_BOUNDARY).getEnum();
+        
+        if (retrieveClassName == null || retrieveClassName.equals("")) {
+            missingPropertyError("retrieve-class-name");
+        }        
     }
     
     /**
index 44ee433395ee0bc1c80871c40ad2e74b6b0ac9d9..a6534e3d119fe1b3d3696565639c77d4f07d0908 100644 (file)
@@ -238,6 +238,14 @@ public class Table extends FObj {
     public List getColumns() {
         return columns;
     }
+    
+    /**
+     * @param index index of the table-body element.
+     * @return the requested table-body element
+     */
+    public TableBody getBody(int index) {
+        return (TableBody)childNodes.get(index);
+    }
 
     public TableBody getTableHeader() {
         return tableHeader;
@@ -285,20 +293,31 @@ public class Table extends FObj {
         return commonBorderPaddingBackground;
     }
 
-    /**
-     * @return the "break-after" property.
-     */
+    /** @return the "break-after" property. */
     public int getBreakAfter() {
         return breakAfter;
     }
 
-    /**
-     * @return the "break-before" property.
-     */
+    /** @return the "break-before" property. */
     public int getBreakBefore() {
         return breakBefore;
     }
-    
+
+    /** @return the "keep-with-next" property.  */
+    public KeepProperty getKeepWithNext() {
+        return keepWithNext;
+    }
+
+    /** @return the "keep-with-previous" property.  */
+    public KeepProperty getKeepWithPrevious() {
+        return keepWithPrevious;
+    }
+
+    /** @return the "keep-together" property.  */
+    public KeepProperty getKeepTogether() {
+        return keepTogether;
+    }
+
     /** @return the "border-collapse" property. */
     public int getBorderCollapse() {
         return borderCollapse;
index da1dc92f9b64f718e60d36614f536c97d75305d7..cae2fd2ce759844aa79cdca7029f63a30d7c1de5 100644 (file)
@@ -102,9 +102,10 @@ public class TableBody extends FObj {
                 getParent().removeChild(this);
             }
         }
+        /*
         if (tableCellsFound) {
             convertCellsToRows();
-        }
+        }*/
         savedPropertyList = null; //Release reference
     }
 
@@ -148,7 +149,7 @@ public class TableBody extends FObj {
      */
     private void convertCellsToRows() throws FOPException {
         //getLogger().debug("Converting cells to rows...");
-        List cells = (List)childNodes.clone();
+        List cells = new java.util.ArrayList(childNodes);
         childNodes.clear();
         Iterator i = cells.iterator();
         TableRow row = null;
index 6466da45393f5f0f8bf35e2283e88f174ed385ae..dcc7a0a4c0c4599a27cc81479841075a80c7abbf 100644 (file)
@@ -156,6 +156,7 @@ public class TableCell extends FObj {
         if (!blockItemFound) {
             missingChildElementError("marker* (%block;)+");
         }
+        //TODO Complain about startsRow|endsRow=true if parent is a table-row
         getFOEventHandler().endCell(this);
     }
 
index 4a0b7d037e9d3366c77e3cc5579ae045431f25bc..2f8bf5ada31a2005e49bd83ac48a90ea5406526a 100644 (file)
@@ -137,13 +137,16 @@ public class TableColumn extends FObj {
         return columnNumber.getValue();
     }
 
-    /**
-     * @return value for number of columns repeated
-     */
+    /** @return value for number-columns-repeated. */
     public int getNumberColumnsRepeated() {
         return numberColumnsRepeated.getValue();
     }
     
+    /** @return value for number-columns-spanned. */
+    public int getNumberColumnsSpanned() {
+        return numberColumnsSpanned.getValue();
+    }
+    
     /** @see org.apache.fop.fo.FONode#getName() */
     public String getName() {
         return "fo:table-column";
@@ -153,5 +156,21 @@ public class TableColumn extends FObj {
     public int getNameId() {
         return FO_TABLE_COLUMN;
     }
+    
+    /** @see java.lang.Object#toString() */
+    public String toString() {
+        StringBuffer sb = new StringBuffer("fo:table-column");
+        if (hasColumnNumber()) {
+            sb.append(" column-number=").append(getColumnNumber());
+        }
+        if (getNumberColumnsRepeated() > 1) {
+            sb.append(" number-columns-repeated=").append(getNumberColumnsRepeated());
+        }
+        if (getNumberColumnsSpanned() > 1) {
+            sb.append(" number-columns-spanned=").append(getNumberColumnsSpanned());
+        }
+        sb.append(" column-width=").append(getColumnWidth());
+        return sb.toString();
+    }
 }
 
index 1de4327e20ac44d262fc27a120feda5303175422..c8bb26f738a2d2ef2f0c55461e8c26e2afc4cadb 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 package org.apache.fop.fo.flow;
 
 // Java
-import java.util.List;
-import java.util.ListIterator;
-
 import org.apache.fop.apps.FOPException;
 import org.apache.fop.fo.FONode;
 import org.apache.fop.fo.FObjMixed;
 import org.apache.fop.fo.PropertyList;
+import org.apache.fop.fo.ValidationException;
+import org.xml.sax.Locator;
 
 /**
  * Implementation for fo:wrapper formatting object.
  * The wrapper object serves as
  * a property holder for its child node objects.
- *
- * Content: (#PCDATA|%inline;|%block;)*
- * Properties: id
- * @todo implement validateChildNode()
  */
 public class Wrapper extends FObjMixed {
     // The value of properties relevant for fo:wrapper.
     private String id;
     // End of property values
     
+    // used for FO validation
+    private boolean blockOrInlineItemFound = false;
+
     /**
      * @param parent FONode that is the parent of this object
      */
@@ -62,6 +60,27 @@ public class Wrapper extends FObjMixed {
         checkId(id);
     }
 
+    /**
+     * @see org.apache.fop.fo.FONode#validateChildNode(Locator, String, String)
+     * XSL Content Model: marker* (#PCDATA|%inline;|%block;)*
+     * Additionally (unimplemented): "An fo:wrapper that is a child of an 
+     * fo:multi-properties is only permitted to have children that would 
+     * be permitted in place of the fo:multi-properties."
+     */
+    protected void validateChildNode(Locator loc, String nsURI, String localName) 
+        throws ValidationException {
+        if (nsURI == FO_URI && localName.equals("marker")) {
+            if (blockOrInlineItemFound) {
+               nodesOutOfOrderError(loc, "fo:marker", 
+                    "(#PCDATA|%inline;|%block;)");
+            }
+        } else if (isBlockOrInlineItem(nsURI, localName)) {
+            blockOrInlineItemFound = true;
+        } else {
+            invalidChildError(loc, nsURI, localName);
+        }
+    }
+
     /**
      * Return the "id" property.
      */
index b06e53138c5ad227d7943c1df91bfe0a0afae5e9..6d35dae20c581e0aea7507ab823d3d90d6101f1d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,16 +22,14 @@ package org.apache.fop.fo.pagination;
 import java.awt.Rectangle;
 
 import org.apache.fop.apps.FOPException;
-import org.apache.fop.datatypes.Length;
 import org.apache.fop.fo.FONode;
 import org.apache.fop.fo.PropertyList;
 
 /**
  * Abstract base class for fo:region-before and fo:region-after.
  */
-public abstract class RegionBA extends Region {
+public abstract class RegionBA extends SideRegion {
     // The value of properties relevant for fo:region-[before|after].
-    private Length extent;
     private int precedence;
     // End of property values
     
@@ -47,19 +45,11 @@ public abstract class RegionBA extends Region {
      */
     public void bind(PropertyList pList) throws FOPException {
         super.bind(pList);
-        extent = pList.get(PR_EXTENT).getLength();
         precedence = pList.get(PR_PRECEDENCE).getEnum();
     }
 
     /**
-     * Return the "extent" property.
-     */
-    public Length getExtent() {
-        return extent;
-    }
-
-    /**
-     * Return the "precedence" property.
+     * @return the "precedence" property.
      */
     public int getPrecedence() {
         return precedence;
index 47fb7e8e8078c4cdfaa681dfbff19f15ec91271b..a4bd827009fe9a49ef93bbc4e67ee9ca9cb1d4c4 100644 (file)
@@ -25,6 +25,7 @@ import org.apache.fop.apps.FOPException;
 import org.apache.fop.datatypes.FODimension;
 import org.apache.fop.datatypes.Length;
 import org.apache.fop.datatypes.Numeric;
+import org.apache.fop.fo.Constants;
 import org.apache.fop.fo.FONode;
 import org.apache.fop.fo.PropertyList;
 import org.apache.fop.fo.properties.CommonMarginBlock;
@@ -54,6 +55,16 @@ public class RegionBody extends Region {
         commonMarginBlock = pList.getMarginBlockProps();
         columnCount = pList.get(PR_COLUMN_COUNT).getNumeric();
         columnGap = pList.get(PR_COLUMN_GAP).getLength();
+        
+        if ((getColumnCount() > 1) && (getOverflow() == EN_SCROLL)) {
+            /* This is an error (See XSL Rec, fo:region-body description).
+             * The Rec allows for acting as if "1" is chosen in
+             * these cases, but we will need to be able to change Numeric
+             * values in order to do this.
+             */
+            attributeError("If overflow property is set to \"scroll\"," +
+                    " a column-count other than \"1\" may not be specified.");
+        }
     }
 
     /**
index a47326778302b7bf31a71b67326759a56a52a240..9100c098156654401725ebd30a40c4af79964b18 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,16 +22,14 @@ package org.apache.fop.fo.pagination;
 import java.awt.Rectangle;
 
 import org.apache.fop.apps.FOPException;
-import org.apache.fop.datatypes.Length;
 import org.apache.fop.fo.FONode;
 import org.apache.fop.fo.PropertyList;
 
 /**
  * Abstract base class for fo:region-start and fo:region-end.
  */
-public abstract class RegionSE extends Region {
+public abstract class RegionSE extends SideRegion {
     // The value of properties relevant for fo:region-[start|end].
-    private Length extent;
     // End of property values
 
     /**
@@ -46,16 +44,8 @@ public abstract class RegionSE extends Region {
      */
     public void bind(PropertyList pList) throws FOPException {
         super.bind(pList);
-        extent = pList.get(PR_EXTENT).getLength();
     }
 
-    /**
-     * Return the "extent" property.
-     */
-    public Length getExtent() {
-        return extent;
-    }
-    
     /**
      * Adjust the viewport reference rectangle for a region as a function
      * of precedence.
diff --git a/src/java/org/apache/fop/fo/pagination/SideRegion.java b/src/java/org/apache/fop/fo/pagination/SideRegion.java
new file mode 100644 (file)
index 0000000..1475bc3
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.fo.pagination;
+
+import org.apache.fop.apps.FOPException;
+import org.apache.fop.datatypes.Length;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.PropertyList;
+
+/**
+ * Common base class for side regions (before, after, start, end).
+ */
+public abstract class SideRegion extends Region {
+
+    private Length extent;
+    
+    /** @see org.apache.fop.fo.FONode#FONode(FONode) */
+    protected SideRegion(FONode parent) {
+        super(parent);
+    }
+
+    /** @see org.apache.fop.fo.FObj#bind(PropertyList) */
+    public void bind(PropertyList pList) throws FOPException {
+        super.bind(pList);
+        extent = pList.get(PR_EXTENT).getLength();
+    }
+    
+    /** @return the "extent" property. */
+    public Length getExtent() {
+        return extent;
+    }
+    
+}
diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
new file mode 100644 (file)
index 0000000..36adad7
--- /dev/null
@@ -0,0 +1,661 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fop.fo.Constants;
+import org.apache.fop.traits.MinOptMax;
+
+/**
+ * Abstract base class for breakers (page breakers, static region handlers etc.).
+ */
+public abstract class AbstractBreaker {
+
+    /** logging instance */
+    protected static Log log = LogFactory.getLog(AbstractBreaker.class);
+
+    /*LF*/
+    public static class PageBreakPosition extends LeafPosition {
+        double bpdAdjust; // Percentage to adjust (stretch or shrink)
+        int difference;
+
+        PageBreakPosition(LayoutManager lm, int iBreakIndex,
+                          double bpdA, int diff) {
+            super(lm, iBreakIndex);
+            bpdAdjust = bpdA;
+            difference = diff;
+        }
+    }
+
+    public class BlockSequence extends KnuthSequence {
+
+        /**
+         * startOn represents where on the page/which page layout
+         * should start for this BlockSequence.  Acceptable values:
+         * Constants.EN_ANY (can continue from finished location 
+         * of previous BlockSequence?), EN_COLUMN, EN_ODD_PAGE, 
+         * EN_EVEN_PAGE. 
+         */
+        private int startOn;
+
+        public BlockSequence(int iStartOn) {
+            super();
+            startOn = iStartOn;
+        }
+        
+        public int getStartOn() {
+            return this.startOn;
+        }
+
+        public BlockSequence endBlockSequence() {
+            KnuthSequence temp = super.endSequence();
+            if (temp != null) {
+                BlockSequence returnSequence = new BlockSequence(startOn);
+                returnSequence.addAll(temp);
+                returnSequence.ignoreAtEnd = this.ignoreAtEnd;
+                return returnSequence;
+            } else {
+                return null;
+            }
+        }
+    }
+
+    /** blockListIndex of the current BlockSequence in blockLists */
+    private int blockListIndex = 0;
+/*LF*/
+    /*LF*/
+    private List blockLists = null;
+
+    private int alignment;
+    private int alignmentLast;
+    /*LF*/
+
+    protected abstract int getCurrentDisplayAlign();
+    protected abstract boolean hasMoreContent();
+    protected abstract void addAreas(PositionIterator posIter, LayoutContext context);
+    protected abstract LayoutManager getTopLevelLM();
+    protected abstract LayoutManager getCurrentChildLM();
+    protected abstract LinkedList getNextKnuthElements(LayoutContext context, int alignment);
+
+    /** @return true if there's no content that could be handled. */
+    public boolean isEmpty() {
+        return (blockLists.size() == 0);
+    }
+    
+    protected void startPart(BlockSequence list, boolean bIsFirstPage) {
+        //nop
+    }
+    
+    protected abstract void finishPart();
+
+    protected LayoutContext createLayoutContext() {
+        return new LayoutContext(0);
+    }
+    
+    public void doLayout(int flowBPD) {
+        LayoutContext childLC = createLayoutContext();
+        childLC.setStackLimit(new MinOptMax(flowBPD));
+
+        //System.err.println("Vertical alignment: " +
+        // currentSimplePageMaster.getRegion(FO_REGION_BODY).getDisplayAlign());
+        if (getCurrentDisplayAlign() == Constants.EN_X_FILL) {
+            //EN_FILL is non-standard (by LF)
+            alignment = Constants.EN_JUSTIFY;
+        } else {
+            alignment = Constants.EN_START;
+        }
+        alignmentLast = Constants.EN_START;
+
+        BlockSequence blockList;
+        blockLists = new java.util.ArrayList();
+
+        System.out.println("PLM> flow BPD =" + flowBPD);
+        
+        //*** Phase 1: Get Knuth elements ***
+        int nextSequenceStartsOn = Constants.EN_ANY;
+        while (hasMoreContent()) {
+            nextSequenceStartsOn = getNextBlockList(childLC, nextSequenceStartsOn, blockLists);
+        }
+
+        //*** Phase 2: Alignment and breaking ***
+        System.out.println("PLM> blockLists.size() = " + blockLists.size());
+        for (blockListIndex = 0; blockListIndex < blockLists.size(); blockListIndex++) {
+            blockList = (BlockSequence) blockLists.get(blockListIndex);
+            
+            //debug code start
+            System.err.println("  blockListIndex = " + blockListIndex);
+            String pagina = (blockList.startOn == Constants.EN_ANY) ? "any page"
+                    : (blockList.startOn == Constants.EN_ODD_PAGE) ? "odd page"
+                            : "even page";
+            System.err.println("  sequence starts on " + pagina);
+            logBlocklist(blockList);
+            //debug code end
+
+            System.out.println("PLM> start of algorithm (" + this.getClass().getName() 
+                    + "), flow BPD =" + flowBPD);
+            PageBreakingAlgorithm alg = new PageBreakingAlgorithm(getTopLevelLM(),
+                    alignment, alignmentLast);
+            int iOptPageNumber;
+
+            BlockSequence effectiveList;
+            if (alignment == Constants.EN_JUSTIFY) {
+                /* justification */
+                effectiveList = justifyBoxes(blockList, alg, flowBPD);
+            } else {
+                /* no justification */
+                effectiveList = blockList;
+            }
+
+            //iOptPageNumber = alg.firstFit(effectiveList, flowBPD, 1, true);
+            iOptPageNumber = alg.findBreakingPoints(effectiveList, flowBPD, 1,
+                    true, true);
+            System.out.println("PLM> iOptPageNumber= " + iOptPageNumber
+                    + " pageBreaks.size()= " + alg.getPageBreaks().size());
+
+            
+            //*** Phase 3: Add areas ***
+            doPhase3(alg, iOptPageNumber, blockList, effectiveList);
+        }
+    }
+
+    /**
+     * Phase 3 of Knuth algorithm: Adds the areas 
+     * @param alg PageBreakingAlgorithm instance which determined the breaks
+     * @param partCount number of parts (pages) to be rendered
+     * @param originalList original Knuth element list
+     * @param effectiveList effective Knuth element list (after adjustments)
+     */
+    protected abstract void doPhase3(PageBreakingAlgorithm alg, int partCount, 
+            BlockSequence originalList, BlockSequence effectiveList);
+    
+    /**
+     * Phase 3 of Knuth algorithm: Adds the areas 
+     * @param alg PageBreakingAlgorithm instance which determined the breaks
+     * @param partCount number of parts (pages) to be rendered
+     * @param originalList original Knuth element list
+     * @param effectiveList effective Knuth element list (after adjustments)
+     */
+    protected void addAreas(PageBreakingAlgorithm alg, int partCount, 
+            BlockSequence originalList, BlockSequence effectiveList) {
+        LayoutContext childLC;
+        // add areas
+        ListIterator effectiveListIterator = effectiveList.listIterator();
+        int startElementIndex = 0;
+        int endElementIndex = 0;
+        for (int p = 0; p < partCount; p++) {
+            PageBreakPosition pbp = (PageBreakPosition) alg.getPageBreaks().get(p);
+            endElementIndex = pbp.getLeafPos();
+            System.out.println("PLM> part: " + (p + 1)
+                    + ", break at position " + endElementIndex);
+
+            startPart(effectiveList, (p == 0));
+            
+            int displayAlign = getCurrentDisplayAlign();
+            
+            // ignore the first elements added by the
+            // PageSequenceLayoutManager
+            startElementIndex += (startElementIndex == 0) 
+                    ? effectiveList.ignoreAtStart
+                    : 0;
+
+            // ignore the last elements added by the
+            // PageSequenceLayoutManager
+            endElementIndex -= (endElementIndex == (originalList.size() - 1)) 
+                    ? effectiveList.ignoreAtEnd
+                    : 0;
+
+            // ignore the last element in the page if it is a KnuthGlue
+            // object
+            if (((KnuthElement) effectiveList.get(endElementIndex))
+                    .isGlue()) {
+                endElementIndex--;
+            }
+
+            // ignore KnuthGlue and KnuthPenalty objects
+            // at the beginning of the line
+            effectiveListIterator = effectiveList
+                    .listIterator(startElementIndex);
+            while (effectiveListIterator.hasNext()
+                    && !((KnuthElement) effectiveListIterator.next())
+                            .isBox()) {
+                startElementIndex++;
+            }
+
+            if (startElementIndex <= endElementIndex) {
+                System.out.println("     addAreas da " + startElementIndex
+                        + " a " + endElementIndex);
+                childLC = new LayoutContext(0);
+                // add space before if display-align is center or bottom
+                // add space after if display-align is distribute and
+                // this is not the last page
+                if (pbp.difference != 0 && displayAlign == Constants.EN_CENTER) {
+                    childLC.setSpaceBefore(pbp.difference / 2);
+                } else if (pbp.difference != 0 && displayAlign == Constants.EN_AFTER) {
+                    childLC.setSpaceBefore(pbp.difference);
+                } else if (pbp.difference != 0 && displayAlign == Constants.EN_X_DISTRIBUTE
+                        && p < (partCount - 1)) {
+                    // count the boxes whose width is not 0
+                    int boxCount = 0;
+                    effectiveListIterator = effectiveList
+                            .listIterator(startElementIndex);
+                    while (effectiveListIterator.nextIndex() <= endElementIndex) {
+                        KnuthElement tempEl = (KnuthElement)effectiveListIterator.next();
+                        if (tempEl.isBox() && tempEl.getW() > 0) {
+                            boxCount++;
+                        }
+                    }
+                    // split the difference
+                    if (boxCount >= 2) {
+                        childLC.setSpaceAfter(pbp.difference / (boxCount - 1));
+                    }
+                }
+
+                /* *** *** non-standard extension *** *** */
+                if (displayAlign == Constants.EN_X_FILL) {
+                    int averageLineLength = optimizeLineLength(effectiveList, startElementIndex, endElementIndex);
+                    if (averageLineLength != 0) {
+                        childLC.setStackLimit(new MinOptMax(averageLineLength));
+                    }
+                }
+                /* *** *** non-standard extension *** *** */
+
+                addAreas(new KnuthPossPosIter(effectiveList,
+                        startElementIndex, endElementIndex + 1), childLC);
+            }
+
+            finishPart();
+
+            startElementIndex = pbp.getLeafPos() + 1;
+        }
+    }
+    
+    /**
+     * Gets the next block list (sequence) and adds it to a list of block lists if it's not empty.
+     * @param childLC LayoutContext to use
+     * @param nextSequenceStartsOn indicates on what page the next sequence should start
+     * @param blockLists list of block lists (sequences)
+     * @return the page on which the next content should appear after a hard break
+     */
+    private int getNextBlockList(LayoutContext childLC, int nextSequenceStartsOn, List blockLists) {
+        LinkedList returnedList;
+        BlockSequence blockList;
+        if ((returnedList = getNextKnuthElements(childLC, alignment)) != null) {
+            if (returnedList.size() == 0) {
+                return nextSequenceStartsOn;
+            }
+            blockList = new BlockSequence(nextSequenceStartsOn);
+            if (((KnuthElement) returnedList.getLast()).isPenalty()
+                    && ((KnuthPenalty) returnedList.getLast()).getP() == -KnuthElement.INFINITE) {
+                KnuthPenalty breakPenalty = (KnuthPenalty) returnedList
+                        .removeLast();
+                switch (breakPenalty.getBreakClass()) {
+                case Constants.EN_PAGE:
+                    System.err.println("PLM> break - PAGE");
+                    nextSequenceStartsOn = Constants.EN_ANY;
+                    break;
+                case Constants.EN_COLUMN:
+                    System.err.println("PLM> break - COLUMN");
+                    //TODO Fix this when implementing multi-column layout
+                    nextSequenceStartsOn = Constants.EN_COLUMN;
+                    break;
+                case Constants.EN_ODD_PAGE:
+                    System.err.println("PLM> break - ODD PAGE");
+                    nextSequenceStartsOn = Constants.EN_ODD_PAGE;
+                    break;
+                case Constants.EN_EVEN_PAGE:
+                    System.err.println("PLM> break - EVEN PAGE");
+                    nextSequenceStartsOn = Constants.EN_EVEN_PAGE;
+                    break;
+                default:
+                    throw new IllegalStateException("Invalid break class: " 
+                            + breakPenalty.getBreakClass());
+                }
+            }
+            blockList.addAll(returnedList);
+            BlockSequence seq = null;
+            seq = blockList.endBlockSequence();
+            if (seq != null) {
+                blockLists.add(seq);
+            }
+        }
+        return nextSequenceStartsOn;
+    }
+
+    /**
+     * @param effectiveList effective block list to work on
+     * @param startElementIndex
+     * @param endElementIndex
+     * @return the average line length, 0 if there's no content
+     */
+    private int optimizeLineLength(KnuthSequence effectiveList, int startElementIndex, int endElementIndex) {
+        ListIterator effectiveListIterator;
+        // optimize line length
+        //System.out.println(" ");
+        int boxCount = 0;
+        int accumulatedLineLength = 0;
+        int greatestMinimumLength = 0;
+        effectiveListIterator = effectiveList
+                .listIterator(startElementIndex);
+        while (effectiveListIterator.nextIndex() <= endElementIndex) {
+            KnuthElement tempEl = (KnuthElement) effectiveListIterator
+                    .next();
+            if (tempEl instanceof KnuthBlockBox) {
+                KnuthBlockBox blockBox = (KnuthBlockBox) tempEl;
+                if (blockBox.getBPD() > 0) {
+                    log.debug("PSLM> nominal length of line = " + blockBox.getBPD());
+                    log.debug("      range = "
+                            + blockBox.getIPDRange());
+                    boxCount++;
+                    accumulatedLineLength += ((KnuthBlockBox) tempEl)
+                            .getBPD();
+                }
+                if (blockBox.getIPDRange().min > greatestMinimumLength) {
+                    greatestMinimumLength = blockBox
+                            .getIPDRange().min;
+                }
+            }
+        }
+        int averageLineLength = 0;
+        if (accumulatedLineLength > 0 && boxCount > 0) {
+            averageLineLength = (int) (accumulatedLineLength / boxCount);
+            //System.out.println("PSLM> lunghezza media = " + averageLineLength);
+            if (averageLineLength < greatestMinimumLength) {
+                averageLineLength = greatestMinimumLength;
+                //System.out.println("      correzione, ora e' = " + averageLineLength);
+            }
+        }
+        return averageLineLength;
+    }
+
+    /**
+     * Justifies the boxes and returns them as a new KnuthSequence.
+     * @param blockList block list to justify
+     * @param alg reference to the algorithm instance
+     * @param availableBPD the available BPD 
+     * @return the effective list
+     */
+    private BlockSequence justifyBoxes(BlockSequence blockList, PageBreakingAlgorithm alg, int availableBPD) {
+        int iOptPageNumber;
+        iOptPageNumber = alg.findBreakingPoints(blockList, availableBPD, 1,
+                true, true);
+        System.out.println("PLM> iOptPageNumber= " + iOptPageNumber);
+
+        // 
+        ListIterator sequenceIterator = blockList.listIterator();
+        ListIterator breakIterator = alg.getPageBreaks().listIterator();
+        KnuthElement thisElement = null;
+        PageBreakPosition thisBreak;
+        int accumulatedS; // accumulated stretch or shrink
+        int adjustedDiff; // difference already adjusted
+        int firstElementIndex;
+
+        while (breakIterator.hasNext()) {
+            thisBreak = (PageBreakPosition) breakIterator.next();
+            System.out.println("| first page: break= "
+                    + thisBreak.getLeafPos() + " difference= "
+                    + thisBreak.difference + " ratio= "
+                    + thisBreak.bpdAdjust);
+            accumulatedS = 0;
+            adjustedDiff = 0;
+
+            // glue and penalty items at the beginning of the page must
+            // be ignored:
+            // the first element returned by sequenceIterator.next()
+            // inside the
+            // while loop must be a box
+            KnuthElement firstElement;
+            while (!(firstElement = (KnuthElement) sequenceIterator
+                    .next()).isBox()) {
+                // 
+                System.out.println("PLM> ignoring glue or penalty element "
+                        + "at the beginning of the sequence");
+                if (firstElement.isGlue()) {
+                    ((BlockLevelLayoutManager) firstElement
+                            .getLayoutManager())
+                            .discardSpace((KnuthGlue) firstElement);
+                }
+            }
+            firstElementIndex = sequenceIterator.previousIndex();
+            sequenceIterator.previous();
+
+            // scan the sub-sequence representing a page,
+            // collecting information about potential adjustments
+            MinOptMax lineNumberMaxAdjustment = new MinOptMax(0);
+            MinOptMax spaceMaxAdjustment = new MinOptMax(0);
+            double spaceAdjustmentRatio = 0.0;
+            LinkedList blockSpacesList = new LinkedList();
+            LinkedList unconfirmedList = new LinkedList();
+            LinkedList adjustableLinesList = new LinkedList();
+            boolean bBoxSeen = false;
+            while (sequenceIterator.hasNext()
+                    && sequenceIterator.nextIndex() <= thisBreak
+                            .getLeafPos()) {
+                thisElement = (KnuthElement) sequenceIterator.next();
+                if (thisElement.isGlue()) {
+                    // glue elements are used to represent adjustable
+                    // lines
+                    // and adjustable spaces between blocks
+                    switch (((KnuthGlue) thisElement)
+                            .getAdjustmentClass()) {
+                    case BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT:
+                    // fall through
+                    case BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT:
+                        // potential space adjustment
+                        // glue items before the first box or after the
+                        // last one
+                        // must be ignored
+                        unconfirmedList.add(thisElement);
+                        break;
+                    case BlockLevelLayoutManager.LINE_NUMBER_ADJUSTMENT:
+                        // potential line number adjustment
+                        lineNumberMaxAdjustment.max += ((KnuthGlue) thisElement)
+                                .getY();
+                        lineNumberMaxAdjustment.min -= ((KnuthGlue) thisElement)
+                                .getZ();
+                        adjustableLinesList.add(thisElement);
+                        break;
+                    case BlockLevelLayoutManager.LINE_HEIGHT_ADJUSTMENT:
+                        // potential line height adjustment
+                        break;
+                    default:
+                    // nothing
+                    }
+                } else if (thisElement.isBox()) {
+                    if (!bBoxSeen) {
+                        // this is the first box met in this page
+                        bBoxSeen = true;
+                    } else if (unconfirmedList.size() > 0) {
+                        // glue items in unconfirmedList were not after
+                        // the last box
+                        // in this page; they must be added to
+                        // blockSpaceList
+                        while (unconfirmedList.size() > 0) {
+                            KnuthGlue blockSpace = (KnuthGlue) unconfirmedList
+                                    .removeFirst();
+                            spaceMaxAdjustment.max += ((KnuthGlue) blockSpace)
+                                    .getY();
+                            spaceMaxAdjustment.min -= ((KnuthGlue) blockSpace)
+                                    .getZ();
+                            blockSpacesList.add(blockSpace);
+                        }
+                    }
+                }
+            }
+            System.out.println("| line number adj= "
+                    + lineNumberMaxAdjustment);
+            System.out.println("| space adj      = "
+                    + spaceMaxAdjustment);
+
+            if (thisElement.isPenalty() && thisElement.getW() > 0) {
+                System.out
+                        .println("  mandatory variation to the number of lines!");
+                ((BlockLevelLayoutManager) thisElement
+                        .getLayoutManager()).negotiateBPDAdjustment(
+                        thisElement.getW(), thisElement);
+            }
+
+            if (thisBreak.bpdAdjust != 0
+                    && (thisBreak.difference > 0 && thisBreak.difference <= spaceMaxAdjustment.max)
+                    || (thisBreak.difference < 0 && thisBreak.difference >= spaceMaxAdjustment.min)) {
+                // modify only the spaces between blocks
+                spaceAdjustmentRatio = ((double) thisBreak.difference / (thisBreak.difference > 0 ? spaceMaxAdjustment.max
+                        : spaceMaxAdjustment.min));
+                adjustedDiff += adjustBlockSpaces(
+                        blockSpacesList,
+                        thisBreak.difference,
+                        (thisBreak.difference > 0 ? spaceMaxAdjustment.max
+                                : -spaceMaxAdjustment.min));
+                System.out.println("single space: "
+                        + (adjustedDiff == thisBreak.difference
+                                || thisBreak.bpdAdjust == 0 ? "ok"
+                                : "ERROR"));
+            } else if (thisBreak.bpdAdjust != 0) {
+                adjustedDiff += adjustLineNumbers(
+                        adjustableLinesList,
+                        thisBreak.difference,
+                        (thisBreak.difference > 0 ? lineNumberMaxAdjustment.max
+                                : -lineNumberMaxAdjustment.min));
+                adjustedDiff += adjustBlockSpaces(
+                        blockSpacesList,
+                        thisBreak.difference - adjustedDiff,
+                        ((thisBreak.difference - adjustedDiff) > 0 ? spaceMaxAdjustment.max
+                                : -spaceMaxAdjustment.min));
+                System.out.println("lines and space: "
+                        + (adjustedDiff == thisBreak.difference
+                                || thisBreak.bpdAdjust == 0 ? "ok"
+                                : "ERROR"));
+
+            }
+        }
+
+        // create a new sequence: the new elements will contain the
+        // Positions
+        // which will be used in the addAreas() phase
+        BlockSequence effectiveList = new BlockSequence(blockList.getStartOn());
+        effectiveList.addAll(getCurrentChildLM().getChangedKnuthElements(
+                blockList.subList(0, blockList.size() - blockList.ignoreAtEnd),
+                /* 0, */0));
+        //effectiveList.add(new KnuthPenalty(0, -KnuthElement.INFINITE,
+        // false, new Position(this), false));
+        effectiveList.endSequence();
+
+        logEffectiveList(effectiveList);
+
+        alg.getPageBreaks().clear(); //Why this?
+        return effectiveList;
+    }
+
+    /**
+     * Logs the contents of a block list for debugging purposes
+     * @param blockList block list to log
+     */
+    private void logBlocklist(KnuthSequence blockList) {
+        ListIterator tempIter = blockList.listIterator();
+
+        KnuthElement temp;
+        System.out.println(" ");
+        while (tempIter.hasNext()) {
+            temp = (KnuthElement) tempIter.next();
+            if (temp.isBox()) {
+                System.out.println(tempIter.previousIndex()
+                        + ") " + temp);
+            } else if (temp.isGlue()) {
+                System.out.println(tempIter.previousIndex()
+                        + ") " + temp);
+            } else {
+                System.out.println(tempIter.previousIndex()
+                        + ") " + temp);
+            }
+            if (temp.getPosition() != null) {
+                System.out.println("            " + temp.getPosition());
+            }
+        }
+        System.out.println(" ");
+    }
+
+    /**
+     * Logs the contents of an effective block list for debugging purposes
+     * @param effectiveList block list to log
+     */
+    private void logEffectiveList(KnuthSequence effectiveList) {
+        System.out.println("Effective list");
+        logBlocklist(effectiveList);
+    }
+
+    private int adjustBlockSpaces(LinkedList spaceList, int difference, int total) {
+    /*LF*/  System.out.println("AdjustBlockSpaces: difference " + difference + " / " + total + " on " + spaceList.size() + " spaces in block");
+            ListIterator spaceListIterator = spaceList.listIterator();
+            int adjustedDiff = 0;
+            int partial = 0;
+            while (spaceListIterator.hasNext()) {
+                KnuthGlue blockSpace = (KnuthGlue)spaceListIterator.next();
+                partial += (difference > 0 ? blockSpace.getY() : blockSpace.getZ());
+                System.out.println("available = " + partial +  " / " + total);
+                System.out.println("competenza  = " + (((int) ((float) partial * difference / total)) - adjustedDiff) + " / " + difference);
+                int newAdjust = ((BlockLevelLayoutManager) blockSpace.getLayoutManager()).negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, blockSpace);
+                adjustedDiff += newAdjust;
+            }
+            return adjustedDiff;
+        }
+
+    private int adjustLineNumbers(LinkedList lineList, int difference, int total) {
+    /*LF*/  System.out.println("AdjustLineNumbers: difference " + difference + " / " + total + " on " + lineList.size() + " elements");
+
+//            int adjustedDiff = 0;
+//            int partial = 0;
+//            KnuthGlue prevLine = null;
+//            KnuthGlue currLine = null;
+//            ListIterator lineListIterator = lineList.listIterator();
+//            while (lineListIterator.hasNext()) {
+//                currLine = (KnuthGlue)lineListIterator.next();
+//                if (prevLine != null
+//                    && prevLine.getLayoutManager() != currLine.getLayoutManager()) {
+//                    int newAdjust = ((BlockLevelLayoutManager) prevLine.getLayoutManager())
+//                                    .negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, prevLine);
+//                    adjustedDiff += newAdjust;
+//                }
+//                partial += (difference > 0 ? currLine.getY() : currLine.getZ());
+//                prevLine = currLine;
+//            }
+//            if (currLine != null) {
+//                int newAdjust = ((BlockLevelLayoutManager) currLine.getLayoutManager())
+//                                .negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, currLine);
+//                adjustedDiff += newAdjust;
+//            }
+//            return adjustedDiff;
+
+            ListIterator lineListIterator = lineList.listIterator();
+            int adjustedDiff = 0;
+            int partial = 0;
+            while (lineListIterator.hasNext()) {
+                KnuthGlue line = (KnuthGlue)lineListIterator.next();
+                partial += (difference > 0 ? line.getY() : line.getZ());
+                int newAdjust = ((BlockLevelLayoutManager) line.getLayoutManager()).negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, line);
+                adjustedDiff += newAdjust;
+            }
+            return adjustedDiff;
+        }
+
+    
+}
index 9456b58e38dddfee1e1427d592f9b5a8ffe32979..74b5bbfaf06e3d417df1b4117f201573ef0528b5 100644 (file)
@@ -21,23 +21,21 @@ package org.apache.fop.layoutmgr;
 import org.apache.fop.fo.FObj;
 import org.apache.fop.fo.FONode;
 import org.apache.fop.area.Area;
-import org.apache.fop.area.Resolvable;
 import org.apache.fop.area.PageViewport;
-import org.apache.fop.area.AreaTreeHandler;
 import org.apache.fop.fo.Constants;
 import org.apache.fop.fo.flow.RetrieveMarker;
-import org.apache.fop.fo.flow.Marker;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
+import java.util.LinkedList;
 import java.util.List;
 import java.util.ArrayList;
 import java.util.ListIterator;
 import java.util.Map;
 
 /**
- * The base class for all LayoutManagers.
+ * The base class for most LayoutManagers.
  */
 public abstract class AbstractLayoutManager implements LayoutManager, Constants {
     protected LayoutManager parentLM = null;
@@ -51,7 +49,7 @@ public abstract class AbstractLayoutManager implements LayoutManager, Constants
     
     /**
      * Used during addAreas(): signals that a BreakPoss is not generating areas
-     * and therefore doesn't add IDs and markers to the current page.
+     * and therefore shouldn't add IDs and markers to the current page.
      * @see org.apache.fop.layoutmgr.AbstractLayoutManager#isBogus
      */
     protected boolean bBogus = false;
@@ -80,15 +78,6 @@ public abstract class AbstractLayoutManager implements LayoutManager, Constants
         if (fo == null) {
             throw new IllegalStateException("Null formatting object found.");
         }
-        setFObj(fo);
-    }
-
-    /**
-     * Set the FO object for this layout manager
-     *
-     * @param fo the formatting object for this layout manager
-     */
-    public void setFObj(FObj fo) {
         markers = fo.getMarkers();
         fobjIter = fo.getChildNodes();
         childLMiter = new LMiter(this);
@@ -120,38 +109,6 @@ public abstract class AbstractLayoutManager implements LayoutManager, Constants
         return this.parentLM;
     }
 
-    //     /**
-    //      * Ask the parent LayoutManager to add the current (full) area to the
-    //      * appropriate parent area.
-    //      * @param bFinished If true, this area is finished, either because it's
-    //      * completely full or because there is no more content to put in it.
-    //      * If false, we are in the middle of this area. This can happen,
-    //      * for example, if we find floats in a line. We stop the current area,
-    //      * and add it (temporarily) to its parent so that we can see if there
-    //      * is enough space to place the float(s) anchored in the line.
-    //      */
-    //     protected void flush(Area area, boolean bFinished) {
-    // if (area != null) {
-    //     // area.setFinished(true);
-    //     parentLM.addChildArea(area, bFinished); // ????
-    //     if (bFinished) {
-    // setCurrentArea(null);
-    //     }
-    // }
-    //     }
-
-    /**
-     * Return an Area which can contain the passed childArea. The childArea
-     * may not yet have any content, but it has essential traits set.
-     * In general, if the LayoutManager already has an Area it simply returns
-     * it. Otherwise, it makes a new Area of the appropriate class.
-     * It gets a parent area for its area by calling its parent LM.
-     * Finally, based on the dimensions of the parent area, it initializes
-     * its own area. This includes setting the content IPD and the maximum
-     * BPD.
-     */
-
-
     /** @see org.apache.fop.layoutmgr.LayoutManager#generatesInlineAreas() */
     public boolean generatesInlineAreas() {
         return false;
@@ -296,126 +253,60 @@ public abstract class AbstractLayoutManager implements LayoutManager, Constants
      * interface which are declared abstract in AbstractLayoutManager.
      * ---------------------------------------------------------*/
 
-    /**
-     * @see org.apache.fop.layoutmgr.LayoutManager#getParentArea(org.apache.fop.area.Area)
-     */
-    public Area getParentArea(Area childArea) {
+    public LinkedList getNextKnuthElements(LayoutContext context,
+                                           int alignment) {
+        log.warn("null implementation of getNextKnuthElements() called!");
+        setFinished(true);
         return null;
     }
 
-    protected void flush() {
+    public KnuthElement addALetterSpaceTo(KnuthElement element) {
+        log.warn("null implementation of addALetterSpaceTo() called!");
+        return element;
     }
 
-    public void addChildArea(Area childArea) {
+    public void getWordChars(StringBuffer sbChars, Position pos) {
+        log.warn("null implementation of getWordChars() called!");
     }
 
-    /**
-     * Delegate getting the current page number to the parent layout manager.
-     *
-     * @see org.apache.fop.layoutmgr.LayoutManager
-     */
-    public String getCurrentPageNumberString() {
-        return parentLM.getCurrentPageNumberString();
+    public void hyphenate(Position pos, HyphContext hc) {
+        log.warn("null implementation of hyphenate called!");
     }
 
-    /**
-     * Delegate resolving the id reference to the parent layout manager.
-     *
-     * @see org.apache.fop.layoutmgr.LayoutManager
-     */
-    public PageViewport resolveRefID(String ref) {
-        return parentLM.resolveRefID(ref);
-    }
-
-    /**
-     * Add the id to the page.
-     * If the id string is not null then add the id to the current page.
-     */
-    protected void addID(String foID) {
-        if (foID != null && foID.length() > 0) {
-            addIDToPage(foID);
-        }
-    }
-
-    /**
-     * Delegate adding id reference to the parent layout manager.
-     *
-     * @see org.apache.fop.layoutmgr.LayoutManager
-     */
-    public void addIDToPage(String id) {
-        parentLM.addIDToPage(id);
-    }
-
-    /**
-     * Delegate adding unresolved area to the parent layout manager.
-     *
-     * @see org.apache.fop.layoutmgr.LayoutManager
-     */
-    public void addUnresolvedArea(String id, Resolvable res) {
-        parentLM.addUnresolvedArea(id, res);
+    public boolean applyChanges(List oldList) {
+        log.warn("null implementation of applyChanges() called!");
+        return false;
     }
 
-    /**
-     * Add the markers when adding an area.
-     */
-    protected void addMarkers(boolean starting, boolean isfirst, boolean islast) {
-        // add markers
-        if (markers != null) {
-            addMarkerMap(markers, starting, isfirst, islast);
-        }
+    public LinkedList getChangedKnuthElements(List oldList,
+                                              /*int flaggedPenalty,*/
+                                              int alignment) {
+        log.warn("null implementation of getChangeKnuthElement() called!");
+        return null;
     }
 
-    /**
-     * Delegate adding marker to the parent layout manager.
-     *
-     * @see org.apache.fop.layoutmgr.LayoutManager
-     */
-    public void addMarkerMap(Map marks, boolean starting, boolean isfirst, boolean islast) {
-        parentLM.addMarkerMap(marks, starting, isfirst, islast);
+    public int getWordSpaceIPD() {
+        log.warn("null implementation of getWordSpaceIPD() called!");
+        return 0;
     }
 
     /**
-     * Delegate retrieve marker to the parent layout manager.
-     *
-     * @see org.apache.fop.layoutmgr.LayoutManager
+     * Return an Area which can contain the passed childArea. The childArea
+     * may not yet have any content, but it has essential traits set.
+     * In general, if the LayoutManager already has an Area it simply returns
+     * it. Otherwise, it makes a new Area of the appropriate class.
+     * It gets a parent area for its area by calling its parent LM.
+     * Finally, based on the dimensions of the parent area, it initializes
+     * its own area. This includes setting the content IPD and the maximum
+     * BPD.
      */
-    public Marker retrieveMarker(String name, int pos, int boundary) {
-        return parentLM.retrieveMarker(name, pos, boundary);
+    public Area getParentArea(Area childArea) {
+        return null;
     }
 
-    /**
-     * Delegate getAreaTreeHandler to the parent layout manager.
-     *
-     * @see org.apache.fop.layoutmgr.LayoutManager
-     * @return the AreaTreeHandler object.
-     */
-    public AreaTreeHandler getAreaTreeHandler() {
-        return parentLM.getAreaTreeHandler();
+    public void addChildArea(Area childArea) {
     }
 
-    /**
-     * Handles retrieve-marker nodes as they occur.
-     * @param foNode FO node to check
-     * @return the original foNode or in case of a retrieve-marker the replaced
-     *     FO node. null if the the replacement results in no nodes to be 
-     *     processed.
-     */
-    private FONode handleRetrieveMarker(FONode foNode) {
-        if (foNode instanceof RetrieveMarker) {
-            RetrieveMarker rm = (RetrieveMarker) foNode;
-            Marker marker = retrieveMarker(rm.getRetrieveClassName(),
-                                           rm.getRetrievePosition(),
-                                           rm.getRetrieveBoundary());
-            if (marker == null) {
-                return null;
-            }
-            rm.bindMarker(marker);
-            return rm;
-        } else {
-            return foNode;
-        }
-    }
-    
     /**
      * Convenience method: preload a number of child LMs
      * @param size the requested number of child LMs
@@ -430,9 +321,12 @@ public abstract class AbstractLayoutManager implements LayoutManager, Constants
             Object theobj = fobjIter.next();
             if (theobj instanceof FONode) {
                 FONode foNode = (FONode) theobj;
-                foNode = handleRetrieveMarker(foNode);
+                if (foNode instanceof RetrieveMarker) {
+                    foNode = getPSLM().resolveRetrieveMarker(
+                        (RetrieveMarker) foNode);
+                }
                 if (foNode != null) {
-                    getAreaTreeHandler().getLayoutManagerMaker().
+                    getPSLM().getLayoutManagerMaker().
                         makeLayoutManagers(foNode, newLMs);
                 }
             }
@@ -440,6 +334,20 @@ public abstract class AbstractLayoutManager implements LayoutManager, Constants
         return newLMs;
     }
 
+    /**
+     * @see org.apache.fop.layoutmgr.PageSequenceLayoutManager#getPSLM
+     */
+    public PageSequenceLayoutManager getPSLM() {
+        return parentLM.getPSLM();
+    }
+    
+    /**
+     * @see org.apache.fop.layoutmgr.PageSequenceLayoutManager#getCurrentPV
+     */
+    public PageViewport getCurrentPV() {
+        return getPSLM().getCurrentPV();
+    }  
+    
     /**
      * @see org.apache.fop.layoutmgr.LayoutManager#preLoadNext
      */
@@ -489,6 +397,4 @@ public abstract class AbstractLayoutManager implements LayoutManager, Constants
             addChildLM(lm);
         }
     }
-
 }
-
diff --git a/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java b/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java
new file mode 100644 (file)
index 0000000..57c1dba
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+
+public class AreaAdditionUtil {
+
+    private static class StackingIter extends PositionIterator {
+        StackingIter(Iterator parentIter) {
+            super(parentIter);
+        }
+
+        protected LayoutManager getLM(Object nextObj) {
+            return ((Position) nextObj).getLM();
+        }
+
+        protected Position getPos(Object nextObj) {
+            return ((Position) nextObj);
+        }
+    }
+
+    public static void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
+        LayoutManager childLM = null;
+        LayoutContext lc = new LayoutContext(0);
+        LayoutManager firstLM = null;
+        LayoutManager lastLM = null;
+
+        // "unwrap" the NonLeafPositions stored in parentIter
+        // and put them in a new list; 
+        LinkedList positionList = new LinkedList();
+        Position pos;
+        while (parentIter.hasNext()) {
+            pos = (Position)parentIter.next();
+            if (pos instanceof NonLeafPosition) {
+                // pos was created by a child of this FlowLM
+                positionList.add(((NonLeafPosition) pos).getPosition());
+                lastLM = ((NonLeafPosition) pos).getPosition().getLM();
+                if (firstLM == null) {
+                    firstLM = lastLM;
+                }
+            } else {
+                // pos was created by this LM, so it must be ignored
+            }
+        }
+
+        StackingIter childPosIter = new StackingIter(positionList.listIterator());
+        while ((childLM = childPosIter.getNextChildLM()) != null) {
+            // Add the block areas to Area
+            lc.setFlags(LayoutContext.FIRST_AREA, childLM == firstLM);
+            lc.setFlags(LayoutContext.LAST_AREA, childLM == lastLM);
+            // set space before for the first LM, in order to implement
+            // display-align = center or after
+            lc.setSpaceBefore((childLM == firstLM ? layoutContext.getSpaceBefore() : 0));
+            // set space after for each LM, in order to implement
+            // display-align = distribute
+            lc.setSpaceAfter(layoutContext.getSpaceAfter());
+            lc.setStackLimit(layoutContext.getStackLimit());
+            childLM.addAreas(childPosIter, lc);
+        }
+    }
+    
+}
index 528ce4e6faad27e183b4c9b6fed055e1c160734c..38a4b2957643d56673c482da56329de7d75d1b2e 100644 (file)
@@ -51,13 +51,13 @@ public class BasicLinkLayoutManager extends InlineLayoutManager {
          if (fobj.getExternalDestination() != null) {
              area.addTrait(Trait.EXTERNAL_LINK, fobj.getExternalDestination());
          } else {
-             String link = fobj.getInternalDestination();
-             PageViewport page = parentLM.resolveRefID(link);
+             String idref = fobj.getInternalDestination();
+             PageViewport page = getPSLM().getFirstPVWithID(idref);
              if (page != null) {
                  area.addTrait(Trait.INTERNAL_LINK, page.getKey());
              } else {
-                 LinkResolver res = new LinkResolver(link, area);
-                 parentLM.addUnresolvedArea(link, res);
+                 LinkResolver res = new LinkResolver(idref, area);
+                 getPSLM().addUnresolvedArea(idref, res);
              }
          }
      }
index c20237ac2e0062256c45e2fd290ad8ea641fbde9..bffc4c758440134a3f80e84f5359902ff16a1e30 100644 (file)
@@ -37,7 +37,6 @@ public class BidiLayoutManager extends LeafNodeLayoutManager {
     public BidiLayoutManager(BidiOverride node, InlineLayoutManager cLM) {
         super(node);
         children = new ArrayList();
-        setFObj(node);
 /*
         for (int count = cLM.size() - 1; count >= 0; count--) {
             InlineArea ia = cLM.get(count);
index 36d3389430afca422229d742b5b91c6751e7504b..1fda2cfc483137209c588fd744f7e95e36950315 100644 (file)
 
 package org.apache.fop.layoutmgr;
 
+import java.util.LinkedList;
 import java.util.List;
+import java.util.ListIterator;
 import java.awt.Point;
 import java.awt.geom.Rectangle2D;
 
 import org.apache.fop.area.Area;
 import org.apache.fop.area.BlockViewport;
 import org.apache.fop.area.Block;
-import org.apache.fop.area.PageViewport;
 import org.apache.fop.area.Trait;
 import org.apache.fop.fo.flow.BlockContainer;
 import org.apache.fop.fo.properties.CommonAbsolutePosition;
@@ -40,7 +41,6 @@ import org.apache.fop.traits.SpaceVal;
  * LayoutManager for a block-container FO.
  */
 public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
-    private BlockContainer fobj;
     
     private BlockViewport viewportBlockArea;
     private Block referenceArea;
@@ -56,7 +56,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
     private int vpContentIPD;
     private int vpContentBPD;
     private int usedBPD;
-
+    
     // When viewport should grow with the content.
     private boolean autoHeight = true; 
 
@@ -73,6 +73,15 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
     //TODO space-before|after: handle space-resolution rules
     private MinOptMax foBlockSpaceBefore;
     private MinOptMax foBlockSpaceAfter;
+
+    private boolean bBreakBeforeServed = false;
+    private boolean bSpaceBeforeServed = false;
+
+    /*LF*/
+    /** Only used to store the original list when createUnitElements is called */
+    //TODO Maybe pull up as protected member if also used in this class (JM)
+    private LinkedList storedList = null;
+    
     
     /**
      * Create a new block container layout manager.
@@ -80,45 +89,40 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
      */
     public BlockContainerLayoutManager(BlockContainer node) {
         super(node);
-        fobj = node;
     }
     
-    /**
-     * @return the currently applicable page viewport
-     */
-    protected PageViewport getPageViewport() {
-        LayoutManager lm = this;
-        while (lm != null && !(lm instanceof PageSequenceLayoutManager)) {
-            lm = lm.getParent();
-        }
-        if (lm == null) {
-            return null;
-        } else {
-            return ((PageSequenceLayoutManager)lm).getCurrentPageViewport();
-        }
-    }
-
     /**
      * @see org.apache.fop.layoutmgr.AbstractLayoutManager#initProperties()
      */
     protected void initProperties() {
-        abProps = fobj.getCommonAbsolutePosition();
-        foBlockSpaceBefore = new SpaceVal(fobj.getCommonMarginBlock().spaceBefore).getSpace();
-        foBlockSpaceAfter = new SpaceVal(fobj.getCommonMarginBlock().spaceAfter).getSpace();
+        abProps = getBlockContainerFO().getCommonAbsolutePosition();
+        foBlockSpaceBefore = new SpaceVal(getBlockContainerFO().getCommonMarginBlock().spaceBefore).getSpace();
+        foBlockSpaceAfter = new SpaceVal(getBlockContainerFO().getCommonMarginBlock().spaceAfter).getSpace();
 
-        boolean rotated = (fobj.getReferenceOrientation() % 180 != 0);
+        boolean rotated = (getBlockContainerFO().getReferenceOrientation() % 180 != 0);
         if (rotated) {
-            height = fobj.getInlineProgressionDimension().getOptimum().getLength();
-            width = fobj.getBlockProgressionDimension().getOptimum().getLength();
+            height = getBlockContainerFO().getInlineProgressionDimension().getOptimum().getLength();
+            width = getBlockContainerFO().getBlockProgressionDimension().getOptimum().getLength();
         } else {
-            height = fobj.getBlockProgressionDimension().getOptimum().getLength();
-            width = fobj.getInlineProgressionDimension().getOptimum().getLength();
+            height = getBlockContainerFO().getBlockProgressionDimension().getOptimum().getLength();
+            width = getBlockContainerFO().getInlineProgressionDimension().getOptimum().getLength();
         }
+        
+/*LF*/  bpUnit = 0; //layoutProps.blockProgressionUnit;
+/*LF*/  if (bpUnit == 0) {
+/*LF*/      // use optimum space values
+/*LF*/      adjustedSpaceBefore = getBlockContainerFO().getCommonMarginBlock().spaceBefore.getSpace().getOptimum().getLength().getValue();
+/*LF*/      adjustedSpaceAfter = getBlockContainerFO().getCommonMarginBlock().spaceAfter.getSpace().getOptimum().getLength().getValue();
+/*LF*/  } else {
+/*LF*/      // use minimum space values
+/*LF*/      adjustedSpaceBefore = getBlockContainerFO().getCommonMarginBlock().spaceBefore.getSpace().getMinimum().getLength().getValue();
+/*LF*/      adjustedSpaceAfter = getBlockContainerFO().getCommonMarginBlock().spaceAfter.getSpace().getMinimum().getLength().getValue();
+/*LF*/  }
     }
 
     /** @return the content IPD */
     protected int getRotatedIPD() {
-        return fobj.getInlineProgressionDimension().getOptimum().getLength().getValue();
+        return getBlockContainerFO().getInlineProgressionDimension().getOptimum().getLength().getValue();
     }
 
     private int getSpaceBefore() {
@@ -127,16 +131,16 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
     
     private int getBPIndents() {
         int indents = 0;
-        indents += fobj.getCommonMarginBlock().spaceBefore.getOptimum().getLength().getValue();
-        indents += fobj.getCommonMarginBlock().spaceAfter.getOptimum().getLength().getValue();
-        indents += fobj.getCommonBorderPaddingBackground().getBPPaddingAndBorder(false);
+        indents += getBlockContainerFO().getCommonMarginBlock().spaceBefore.getOptimum().getLength().getValue();
+        indents += getBlockContainerFO().getCommonMarginBlock().spaceAfter.getOptimum().getLength().getValue();
+        indents += getBlockContainerFO().getCommonBorderPaddingBackground().getBPPaddingAndBorder(false);
         return indents;
     }
     
     private int getIPIndents() {
         int iIndents = 0;
-        iIndents += fobj.getCommonMarginBlock().startIndent.getValue();
-        iIndents += fobj.getCommonMarginBlock().endIndent.getValue();
+        iIndents += getBlockContainerFO().getCommonMarginBlock().startIndent.getValue();
+        iIndents += getBlockContainerFO().getCommonMarginBlock().endIndent.getValue();
         return iIndents;
     }
     
@@ -149,6 +153,396 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
         return (abProps.absolutePosition == EN_FIXED);
     }
     
+    /**
+     * @see org.apache.fop.layoutmgr.LayoutManager#getNextKnuthElements(org.apache.fop.layoutmgr.LayoutContext, int)
+     */
+    public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+        if (isAbsoluteOrFixed()) {
+            return getNextKnuthElementsAbsolute(context, alignment);
+        }
+        
+        autoHeight = false;
+        boolean rotated = (getBlockContainerFO().getReferenceOrientation() % 180 != 0); //vals[0] == 0.0;
+        referenceIPD = context.getRefIPD();
+        int maxbpd = context.getStackLimit().opt;
+        int allocBPD, allocIPD;
+        if (height.getEnum() != EN_AUTO) {
+            allocBPD = height.getValue(); //this is the content-height
+            allocBPD += getBPIndents();
+        } else {
+            allocBPD = maxbpd;
+            autoHeight = true;
+        }
+        if (width.getEnum() != EN_AUTO) {
+            allocIPD = width.getValue(); //this is the content-width
+            allocIPD += getIPIndents();
+        } else {
+            allocIPD = referenceIPD;
+        }
+
+        vpContentBPD = allocBPD - getBPIndents();
+        vpContentIPD = allocIPD - getIPIndents();
+        
+        double contentRectOffsetX = 0;
+        contentRectOffsetX += getBlockContainerFO().getCommonMarginBlock().startIndent.getValue();
+        double contentRectOffsetY = 0;
+        //contentRectOffsetY += getBlockContainerFO().getCommonMarginBlock().startIndent.getValue();
+        //contentRectOffsetY += getSpaceBefore();
+        contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
+        contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getPaddingBefore(false);
+        
+        Rectangle2D rect = new Rectangle2D.Double(
+                contentRectOffsetX, contentRectOffsetY, 
+                vpContentIPD, vpContentBPD);
+        relDims = new FODimension(0, 0);
+        absoluteCTM = CTM.getCTMandRelDims(getBlockContainerFO().getReferenceOrientation(),
+                getBlockContainerFO().getWritingMode(), rect, relDims);
+
+        MinOptMax stackLimit = new MinOptMax(relDims.bpd);
+
+        LinkedList returnedList = null;
+        LinkedList contentList = new LinkedList();
+        LinkedList returnList = new LinkedList();
+        Position returnPosition = new NonLeafPosition(this, null);
+        
+        if (!bBreakBeforeServed) {
+            try {
+                if (addKnuthElementsForBreakBefore(returnList, returnPosition)) {
+                    return returnList;
+                }
+            } finally {
+                bBreakBeforeServed = true;
+            }
+        }
+
+        if (!bSpaceBeforeServed) {
+            addKnuthElementsForSpaceBefore(returnList, returnPosition, alignment);
+            bSpaceBeforeServed = true;
+        }
+        
+        addKnuthElementsForBorderPaddingBefore(returnList, returnPosition);
+
+        if (autoHeight) {
+            BlockLevelLayoutManager curLM; // currently active LM
+            BlockLevelLayoutManager prevLM = null; // previously active LM
+            while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) {
+                LayoutContext childLC = new LayoutContext(0);
+                // curLM is a ?
+                childLC.setStackLimit(MinOptMax.subtract(context
+                        .getStackLimit(), stackLimit));
+                childLC.setRefIPD(relDims.ipd);
+
+                // get elements from curLM
+                returnedList = curLM.getNextKnuthElements(childLC, alignment);
+                if (returnedList.size() == 1
+                        && ((KnuthElement) returnedList.getFirst()).isPenalty()
+                        && ((KnuthPenalty) returnedList.getFirst()).getP() == -KnuthElement.INFINITE) {
+                    // a descendant of this block has break-before
+                    if (returnList.size() == 0) {
+                        // the first child (or its first child ...) has
+                        // break-before;
+                        // all this block, including space before, will be put in
+                        // the
+                        // following page
+                        bSpaceBeforeServed = false;
+                    }
+                    contentList.addAll(returnedList);
+
+                    // "wrap" the Position inside each element
+                    // moving the elements from contentList to returnList
+                    returnedList = new LinkedList();
+                    wrapPositionElements(contentList, returnList);
+
+                    return returnList;
+                } else {
+                    if (prevLM != null) {
+                        // there is a block handled by prevLM
+                        // before the one handled by curLM
+                        if (mustKeepTogether() 
+                                || prevLM.mustKeepWithNext()
+                                || curLM.mustKeepWithPrevious()) {
+                            // add an infinite penalty to forbid a break between
+                            // blocks
+                            contentList.add(new KnuthPenalty(0,
+                                    KnuthElement.INFINITE, false,
+                                    new Position(this), false));
+                        } else if (!((KnuthElement) contentList.getLast()).isGlue()) {
+                            // add a null penalty to allow a break between blocks
+                            contentList.add(new KnuthPenalty(0, 0, false,
+                                    new Position(this), false));
+                        } else {
+                            // the last element in contentList is a glue;
+                            // it is a feasible breakpoint, there is no need to add
+                            // a penalty
+                        }
+                    }
+                    contentList.addAll(returnedList);
+                    if (returnedList.size() == 0) {
+                        //Avoid NoSuchElementException below (happens with empty blocks)
+                        continue;
+                    }
+                    if (((KnuthElement) returnedList.getLast()).isPenalty()
+                            && ((KnuthPenalty) returnedList.getLast()).getP() == -KnuthElement.INFINITE) {
+                        // a descendant of this block has break-after
+                        if (curLM.isFinished()) {
+                            // there is no other content in this block;
+                            // it's useless to add space after before a page break
+                            setFinished(true);
+                        }
+
+                        returnedList = new LinkedList();
+                        wrapPositionElements(contentList, returnList);
+
+                        return returnList;
+                    }
+                }
+                prevLM = curLM;
+            }
+
+            returnedList = new LinkedList();
+            wrapPositionElements(contentList, returnList);
+
+        } else {
+            MinOptMax range = new MinOptMax(relDims.ipd);
+            BlockContainerBreaker breaker = new BlockContainerBreaker(this, range);
+            breaker.doLayout(relDims.bpd);
+            boolean contentOverflows = false;
+            if (!breaker.isEmpty()) {
+                contentOverflows = (breaker.deferredAlg.getPageBreaks().size() > 1);
+            }
+
+            Position bcPosition = new BlockContainerPosition(this, breaker);
+            returnList.add(new KnuthBox(vpContentBPD, bcPosition, false));
+            //TODO Handle min/opt/max for block-progression-dimension
+            /* These two elements will be used to add stretchability to the above box
+            returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, returnPosition, false));
+            returnList.add(new KnuthGlue(0, 1 * constantLineHeight, 0,
+                                   LINE_NUMBER_ADJUSTMENT, returnPosition, false));
+            */
+
+            if (contentOverflows) {
+                log.warn("Contents overflow block-container viewport: clipping");
+                if (getBlockContainerFO().getOverflow() == EN_HIDDEN) {
+                    clip = true;
+                } else if (getBlockContainerFO().getOverflow() == EN_ERROR_IF_OVERFLOW) {
+                    //TODO Throw layout exception
+                    clip = true;
+                }
+            }
+        }
+        addKnuthElementsForBorderPaddingAfter(returnList, returnPosition);
+        addKnuthElementsForSpaceAfter(returnList, returnPosition, alignment);
+        addKnuthElementsForBreakAfter(returnList, returnPosition);
+
+        setFinished(true);
+        return returnList;
+    }
+    
+    private LinkedList getNextKnuthElementsAbsolute(LayoutContext context, int alignment) {
+        MinOptMax stackSize = new MinOptMax();
+        autoHeight = false;
+
+        Point offset = getAbsOffset();
+        int allocBPD, allocIPD;
+        if (height.getEnum() != EN_AUTO) {
+            allocBPD = height.getValue(); //this is the content-height
+            allocBPD += getBPIndents();
+        } else {
+            allocBPD = 0;
+            if (abProps.bottom.getEnum() != EN_AUTO) {
+                if (isFixed()) {
+                    allocBPD = (int)getCurrentPV().getViewArea().getHeight();
+                } else {
+                    allocBPD = context.getStackLimit().opt; 
+                }
+                allocBPD -= offset.y;
+                if (abProps.bottom.getEnum() != EN_AUTO) {
+                    allocBPD -= abProps.bottom.getValue();
+                }
+            } else {
+                autoHeight = true;
+            }
+        }
+        if (width.getEnum() != EN_AUTO) {
+            allocIPD = width.getValue(); //this is the content-width
+            allocIPD += getIPIndents();
+        } else {
+            if (isFixed()) {
+                allocIPD = (int)getCurrentPV().getViewArea().getWidth(); 
+            } else {
+                allocIPD = context.getRefIPD();
+            }
+            if (abProps.left.getEnum() != EN_AUTO) {
+                allocIPD -= abProps.left.getValue();
+            }
+            if (abProps.right.getEnum() != EN_AUTO) {
+                allocIPD -= abProps.right.getValue();
+            }
+        }
+
+        vpContentBPD = allocBPD - getBPIndents();
+        vpContentIPD = allocIPD - getIPIndents();
+        
+        double contentRectOffsetX = offset.getX();
+        contentRectOffsetX += getBlockContainerFO().getCommonMarginBlock().startIndent.getValue();
+        double contentRectOffsetY = offset.getY();
+        contentRectOffsetY += getSpaceBefore();
+        contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
+        contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getPaddingBefore(false);
+        
+        Rectangle2D rect = new Rectangle2D.Double(
+                contentRectOffsetX, contentRectOffsetY, 
+                vpContentIPD, vpContentBPD);
+        relDims = new FODimension(0, 0);
+        absoluteCTM = CTM.getCTMandRelDims(
+                getBlockContainerFO().getReferenceOrientation(),
+                getBlockContainerFO().getWritingMode(), 
+                rect, relDims);
+
+        MinOptMax range = new MinOptMax(relDims.ipd);
+        BlockContainerBreaker breaker = new BlockContainerBreaker(this, range);
+        breaker.doLayout(relDims.bpd);
+        boolean contentOverflows = breaker.isOverflow();
+        LinkedList returnList = new LinkedList();
+        if (!breaker.isEmpty()) {
+            usedBPD = relDims.bpd - breaker.getDifferenceOfFirstPart(); 
+
+            Position bcPosition = new BlockContainerPosition(this, breaker);
+            returnList.add(new KnuthBox(0, bcPosition, false));
+    
+            //TODO Maybe check for page overflow when autoHeight=true
+            if (!autoHeight & (contentOverflows/*usedBPD > relDims.bpd*/)) {
+                log.warn("Contents overflow block-container viewport: clipping");
+                if (getBlockContainerFO().getOverflow() == EN_HIDDEN) {
+                    clip = true;
+                } else if (getBlockContainerFO().getOverflow() == EN_ERROR_IF_OVERFLOW) {
+                    //TODO Throw layout exception
+                    clip = true;
+                }
+            }
+        }
+
+        setFinished(true);
+        return returnList;
+    }
+    
+    private class BlockContainerPosition extends NonLeafPosition {
+
+        private BlockContainerBreaker breaker;
+
+        public BlockContainerPosition(LayoutManager lm, BlockContainerBreaker breaker) {
+            super(lm, null);
+            this.breaker = breaker;
+        }
+        
+        public BlockContainerBreaker getBreaker() {
+            return this.breaker;
+        }
+        
+    }
+    
+    private class BlockContainerBreaker extends AbstractBreaker {
+        
+        private BlockContainerLayoutManager bclm;
+        private MinOptMax ipd;
+        
+        //Info for deferred adding of areas
+        private PageBreakingAlgorithm deferredAlg;
+        private BlockSequence deferredOriginalList;
+        private BlockSequence deferredEffectiveList;
+        
+        public BlockContainerBreaker(BlockContainerLayoutManager bclm, MinOptMax ipd) {
+            this.bclm = bclm;
+            this.ipd = ipd;
+        }
+
+        public int getDifferenceOfFirstPart() {
+            PageBreakPosition pbp = (PageBreakPosition)this.deferredAlg.getPageBreaks().getFirst();
+            return pbp.difference;
+        }
+        
+        public boolean isOverflow() {
+            if (isEmpty()) {
+                return false;
+            } else {
+                return (deferredAlg.getPageBreaks().size() > 1);
+            }
+        }
+        
+        protected LayoutManager getTopLevelLM() {
+            return bclm;
+        }
+
+        protected LayoutContext createLayoutContext() {
+            LayoutContext lc = super.createLayoutContext();
+            lc.setRefIPD(ipd.opt);
+            return lc;
+        }
+        
+        protected LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+            LayoutManager curLM; // currently active LM
+            LinkedList returnList = new LinkedList();
+
+            while ((curLM = getChildLM()) != null) {
+                LayoutContext childLC = new LayoutContext(0);
+                childLC.setStackLimit(context.getStackLimit());
+                childLC.setRefIPD(context.getRefIPD());
+
+                LinkedList returnedList = null;
+                if (!curLM.isFinished()) {
+                    returnedList = curLM.getNextKnuthElements(childLC, alignment);
+                }
+                if (returnedList != null) {
+                    bclm.wrapPositionElements(returnedList, returnList);
+                    //returnList.addAll(returnedList);
+                }
+            }
+            setFinished(true);
+            return returnList;
+        }
+
+        protected int getCurrentDisplayAlign() {
+            return getBlockContainerFO().getDisplayAlign();
+        }
+        
+        protected boolean hasMoreContent() {
+            return !isFinished();
+        }
+        
+        protected void addAreas(PositionIterator posIter, LayoutContext context) {
+            AreaAdditionUtil.addAreas(posIter, context);    
+        }
+        
+        protected void doPhase3(PageBreakingAlgorithm alg, int partCount, 
+                BlockSequence originalList, BlockSequence effectiveList) {
+            //Defer adding of areas until addAreas is called by the parent LM
+            this.deferredAlg = alg;
+            this.deferredOriginalList = originalList;
+            this.deferredEffectiveList = effectiveList;
+        }
+        
+        protected void finishPart() {
+            //nop for bclm
+        }
+        
+        protected LayoutManager getCurrentChildLM() {
+            return curChildLM;
+        }
+        
+        public void addContainedAreas() {
+            if (isEmpty()) {
+                return;
+            }
+            //Rendering all parts (not just the first) at once for the case where the parts that 
+            //overflow should be visible.
+            //TODO Check if this has any unwanted side-effects. Feels a bit like a hack.
+            addAreas(this.deferredAlg, 
+                    /*1*/ this.deferredAlg.getPageBreaks().size(), 
+                    this.deferredOriginalList, this.deferredEffectiveList);
+        }
+        
+    }
+    
     /**
      * @see org.apache.fop.layoutmgr.LayoutManager#getNextBreakPoss(org.apache.fop.layoutmgr.LayoutContext)
      */
@@ -159,7 +553,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
         }
 
         autoHeight = false;
-        boolean rotated = (fobj.getReferenceOrientation() % 180 != 0); //vals[0] == 0.0;
+        boolean rotated = (getBlockContainerFO().getReferenceOrientation() % 180 != 0); //vals[0] == 0.0;
         referenceIPD = context.getRefIPD();
         int maxbpd = context.getStackLimit().opt;
         int allocBPD, allocIPD;
@@ -181,19 +575,19 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
         vpContentIPD = allocIPD - getIPIndents();
         
         double contentRectOffsetX = 0;
-        contentRectOffsetX += fobj.getCommonMarginBlock().startIndent.getValue();
+        contentRectOffsetX += getBlockContainerFO().getCommonMarginBlock().startIndent.getValue();
         double contentRectOffsetY = 0;
-        //contentRectOffsetY += fobj.getCommonMarginBlock().startIndent.getValue();
+        //contentRectOffsetY += getBlockContainerFO().getCommonMarginBlock().startIndent.getValue();
         //contentRectOffsetY += getSpaceBefore();
-        contentRectOffsetY += fobj.getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
-        contentRectOffsetY += fobj.getCommonBorderPaddingBackground().getPaddingBefore(false);
+        contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
+        contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getPaddingBefore(false);
         
         Rectangle2D rect = new Rectangle2D.Double(
                 contentRectOffsetX, contentRectOffsetY, 
                 vpContentIPD, vpContentBPD);
         relDims = new FODimension(0, 0);
-        absoluteCTM = CTM.getCTMandRelDims(fobj.getReferenceOrientation(),
-                fobj.getWritingMode(), rect, relDims);
+        absoluteCTM = CTM.getCTMandRelDims(getBlockContainerFO().getReferenceOrientation(),
+                getBlockContainerFO().getWritingMode(), rect, relDims);
         //double[] vals = absoluteCTM.toArray();
 
         MinOptMax stackLimit;
@@ -228,10 +622,10 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
         BreakPoss lastPos = null;
 
         //TODO fix layout dimensions!
-        fobj.setLayoutDimension(PercentBase.BLOCK_IPD, allocIPD);
-        fobj.setLayoutDimension(PercentBase.BLOCK_BPD, allocBPD);
-        fobj.setLayoutDimension(PercentBase.REFERENCE_AREA_IPD, relDims.ipd);
-        fobj.setLayoutDimension(PercentBase.REFERENCE_AREA_BPD, relDims.bpd);
+        getBlockContainerFO().setLayoutDimension(PercentBase.BLOCK_IPD, allocIPD);
+        getBlockContainerFO().setLayoutDimension(PercentBase.BLOCK_BPD, allocBPD);
+        getBlockContainerFO().setLayoutDimension(PercentBase.REFERENCE_AREA_IPD, relDims.ipd);
+        getBlockContainerFO().setLayoutDimension(PercentBase.REFERENCE_AREA_BPD, relDims.bpd);
 
         while ((curLM = getChildLM()) != null) {
             //Treat bc with fixed BPD as non-breakable
@@ -348,7 +742,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
             allocBPD = 0;
             if (abProps.bottom.getEnum() != EN_AUTO) {
                 if (isFixed()) {
-                    allocBPD = (int)getPageViewport().getViewArea().getHeight();
+                    allocBPD = (int)getCurrentPV().getViewArea().getHeight();
                 } else {
                     allocBPD = context.getStackLimit().opt; 
                 }
@@ -365,7 +759,7 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
             allocIPD += getIPIndents();
         } else {
             if (isFixed()) {
-                allocIPD = (int)getPageViewport().getViewArea().getWidth(); 
+                allocIPD = (int)getCurrentPV().getViewArea().getWidth(); 
             } else {
                 allocIPD = context.getRefIPD();
             }
@@ -381,19 +775,19 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
         vpContentIPD = allocIPD - getIPIndents();
         
         double contentRectOffsetX = offset.getX();
-        contentRectOffsetX += fobj.getCommonMarginBlock().startIndent.getValue();
+        contentRectOffsetX += getBlockContainerFO().getCommonMarginBlock().startIndent.getValue();
         double contentRectOffsetY = offset.getY();
         contentRectOffsetY += getSpaceBefore();
-        contentRectOffsetY += fobj.getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
-        contentRectOffsetY += fobj.getCommonBorderPaddingBackground().getPaddingBefore(false);
+        contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
+        contentRectOffsetY += getBlockContainerFO().getCommonBorderPaddingBackground().getPaddingBefore(false);
         
         Rectangle2D rect = new Rectangle2D.Double(
                 contentRectOffsetX, contentRectOffsetY, 
                 vpContentIPD, vpContentBPD);
         relDims = new FODimension(0, 0);
         absoluteCTM = CTM.getCTMandRelDims(
-                fobj.getReferenceOrientation(),
-                fobj.getWritingMode(), 
+                getBlockContainerFO().getReferenceOrientation(),
+                getBlockContainerFO().getWritingMode(), 
                 rect, relDims);
         
         while ((curLM = getChildLM()) != null) {
@@ -423,9 +817,9 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
         //TODO Maybe check for page overflow when autoHeight=true
         if (!autoHeight & (usedBPD > relDims.bpd)) {
             log.warn("Contents overflow block-container viewport: clipping");
-            if (fobj.getOverflow() == EN_HIDDEN) {
+            if (getBlockContainerFO().getOverflow() == EN_HIDDEN) {
                 clip = true;
-            } else if (fobj.getOverflow() == EN_ERROR_IF_OVERFLOW) {
+            } else if (getBlockContainerFO().getOverflow() == EN_ERROR_IF_OVERFLOW) {
                 //TODO Throw layout exception
                 clip = true;
             }
@@ -438,6 +832,186 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
      * @see org.apache.fop.layoutmgr.LayoutManager#addAreas(org.apache.fop.layoutmgr.PositionIterator, org.apache.fop.layoutmgr.LayoutContext)
      */
     public void addAreas(PositionIterator parentIter,
+            LayoutContext layoutContext) {
+        getParentArea(null);
+
+        // if this will create the first block area in a page
+        // and display-align is bottom or center, add space before
+        if (layoutContext.getSpaceBefore() > 0) {
+            addBlockSpacing(0.0, new MinOptMax(layoutContext.getSpaceBefore()));
+        }
+
+        getPSLM().addIDToPage(getBlockContainerFO().getId());
+        //addMarkersToPV(true, bp1.isFirstArea(), bp1.isLastArea());
+        getCurrentPV().addMarkers(markers, true, true, false);
+
+        LayoutManager childLM = null;
+        LayoutManager lastLM = null;
+        LayoutContext lc = new LayoutContext(0);
+        // set space after in the LayoutContext for children
+        if (layoutContext.getSpaceAfter() > 0) {
+            lc.setSpaceAfter(layoutContext.getSpaceAfter());
+        }
+        BlockContainerPosition bcpos = null;
+        PositionIterator childPosIter;
+
+        // "unwrap" the NonLeafPositions stored in parentIter
+        // and put them in a new list;
+        LinkedList positionList = new LinkedList();
+        Position pos;
+        boolean bSpaceBefore = false;
+        boolean bSpaceAfter = false;
+        while (parentIter.hasNext()) {
+            pos = (Position) parentIter.next();
+            /* LF *///System.out.println("pos = " + pos.getClass().getName());
+            Position innerPosition = ((NonLeafPosition) pos).getPosition();
+            if (pos instanceof BlockContainerPosition) {
+                if (bcpos != null) {
+                    throw new IllegalStateException("Only one BlockContainerPosition allowed");
+                }
+                bcpos = (BlockContainerPosition)pos; 
+                //Add child areas inside the reference area
+                //bcpos.getBreaker().addContainedAreas();
+            } else if (innerPosition == null) {
+                // pos was created by this BCLM and was inside an element
+                // representing space before or after
+                // this means the space was not discarded
+                if (positionList.size() == 0) {
+                    // pos was in the element representing space-before
+                    bSpaceBefore = true;
+                    /* LF *///System.out.println(" space-before");
+                } else {
+                    // pos was in the element representing space-after
+                    bSpaceAfter = true;
+                    /* LF *///System.out.println(" space-after");
+                }
+            } else if (innerPosition.getLM() == this
+                    && !(innerPosition instanceof MappingPosition)) {
+                // pos was created by this BlockLM and was inside a penalty
+                // allowing or forbidding a page break
+                // nothing to do
+                /* LF *///System.out.println(" penalty");
+            } else {
+                // innerPosition was created by another LM
+                positionList.add(innerPosition);
+                lastLM = innerPosition.getLM();
+                /* LF *///System.out.println(" " +
+                      // innerPosition.getClass().getName());
+            }
+        }
+
+        if (bcpos == null) {
+            
+        if (bpUnit == 0) {
+            // the Positions in positionList were inside the elements
+            // created by the LineLM
+            childPosIter = new StackingIter(positionList.listIterator());
+        } else {
+            // the Positions in positionList were inside the elements
+            // created by the BCLM in the createUnitElements() method
+            //if (((Position) positionList.getLast()) instanceof
+                  // LeafPosition) {
+            //    // the last item inside positionList is a LeafPosition
+            //    // (a LineBreakPosition, more precisely); this means that
+            //    // the whole paragraph is on the same page
+            //    System.out.println("paragrafo intero");
+            //    childPosIter = new KnuthPossPosIter(storedList, 0,
+                  // storedList.size());
+            //} else {
+            //    // the last item inside positionList is a Position;
+            //    // this means that the paragraph has been split
+            //    // between consecutive pages
+            LinkedList splitList = new LinkedList();
+            int splitLength = 0;
+            int iFirst = ((MappingPosition) positionList.getFirst()).getFirstIndex();
+            int iLast = ((MappingPosition) positionList.getLast()).getLastIndex();
+            // copy from storedList to splitList all the elements from
+            // iFirst to iLast
+            ListIterator storedListIterator = storedList.listIterator(iFirst);
+            while (storedListIterator.nextIndex() <= iLast) {
+                KnuthElement element = (KnuthElement) storedListIterator
+                        .next();
+                // some elements in storedList (i.e. penalty items) were created
+                // by this BlockLM, and must be ignored
+                if (element.getLayoutManager() != this) {
+                    splitList.add(element);
+                    splitLength += element.getW();
+                    lastLM = element.getLayoutManager();
+                }
+            }
+            //System.out.println("addAreas riferito a storedList da " +
+                  // iFirst + " a " + iLast);
+            //System.out.println("splitLength= " + splitLength
+            //                   + " (" + neededUnits(splitLength) + " unita') "
+            //                   + (neededUnits(splitLength) * bpUnit - splitLength) + " spazi");
+            // add space before and / or after the paragraph
+            // to reach a multiple of bpUnit
+            if (bSpaceBefore && bSpaceAfter) {
+                foBlockSpaceBefore = new SpaceVal(getBlockContainerFO().getCommonMarginBlock().spaceBefore).getSpace();
+                foBlockSpaceAfter = new SpaceVal(getBlockContainerFO().getCommonMarginBlock().spaceAfter).getSpace();
+                adjustedSpaceBefore = (neededUnits(splitLength
+                        + foBlockSpaceBefore.min
+                        + foBlockSpaceAfter.min)
+                        * bpUnit - splitLength) / 2;
+                adjustedSpaceAfter = neededUnits(splitLength
+                        + foBlockSpaceBefore.min
+                        + foBlockSpaceAfter.min)
+                        * bpUnit - splitLength - adjustedSpaceBefore;
+            } else if (bSpaceBefore) {
+                adjustedSpaceBefore = neededUnits(splitLength
+                        + foBlockSpaceBefore.min)
+                        * bpUnit - splitLength;
+            } else {
+                adjustedSpaceAfter = neededUnits(splitLength
+                        + foBlockSpaceAfter.min)
+                        * bpUnit - splitLength;
+            }
+            //System.out.println("spazio prima = " + adjustedSpaceBefore
+                  // + " spazio dopo = " + adjustedSpaceAfter + " totale = " +
+                  // (adjustedSpaceBefore + adjustedSpaceAfter + splitLength));
+            childPosIter = new KnuthPossPosIter(splitList, 0, splitList
+                    .size());
+            //}
+        }
+
+        // if adjusted space before
+        if (bSpaceBefore) {
+            addBlockSpacing(0, new MinOptMax(adjustedSpaceBefore));
+        }
+
+        while ((childLM = childPosIter.getNextChildLM()) != null) {
+            // set last area flag
+            lc.setFlags(LayoutContext.LAST_AREA,
+                    (layoutContext.isLastArea() && childLM == lastLM));
+            /*LF*/lc.setStackLimit(layoutContext.getStackLimit());
+            // Add the line areas to Area
+            childLM.addAreas(childPosIter, lc);
+        }
+        } else {
+            // if adjusted space before
+            if (bSpaceBefore) {
+                addBlockSpacing(0, new MinOptMax(adjustedSpaceBefore));
+            }
+            //Add child areas inside the reference area
+            bcpos.getBreaker().addContainedAreas();
+        }
+
+        int bIndents = getBlockContainerFO().getCommonBorderPaddingBackground().getBPPaddingAndBorder(false);
+
+        getCurrentPV().addMarkers(markers, false, false, true);
+
+        flush();
+
+        // if adjusted space after
+        if (bSpaceAfter) {
+            addBlockSpacing(0, new MinOptMax(adjustedSpaceAfter));
+        }
+
+        viewportBlockArea = null;
+        referenceArea = null;
+    }
+    
+    public void addAreasOLDOLDOLD(PositionIterator parentIter,
                          LayoutContext layoutContext) {
         getParentArea(null);
 
@@ -450,8 +1024,8 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
         }*/
 
         BreakPoss bp1 = (BreakPoss)parentIter.peekNext();
-        addID(fobj.getId());
-        addMarkers(true, bp1.isFirstArea(), bp1.isLastArea());
+        getPSLM().addIDToPage(getBlockContainerFO().getId());
+        getCurrentPV().addMarkers(markers, true, bp1.isFirstArea(), bp1.isLastArea());
 
         LayoutManager childLM;
         int iStartPos = 0;
@@ -469,12 +1043,12 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
         }
 
         flush();
-        addMarkers(true, bp1.isFirstArea(), bp1.isLastArea());
+        getCurrentPV().addMarkers(markers, true, bp1.isFirstArea(), bp1.isLastArea());
 
         /*
         if (!isAbsoluteOrFixed()) {
             // if adjusted space after
-            foBlockSpaceAfter = new SpaceVal(fobj.getCommonMarginBlock().spaceAfter).getSpace();
+            foBlockSpaceAfter = new SpaceVal(getBlockContainerFO().getCommonMarginBlock().spaceAfter).getSpace();
             addBlockSpacing(adjust, foBlockSpaceAfter);
         }*/
         
@@ -501,11 +1075,11 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
                 viewportBlockArea.setBPD(vpContentBPD);
             }
 
-            TraitSetter.addBorders(viewportBlockArea, fobj.getCommonBorderPaddingBackground());
-            TraitSetter.addBackground(viewportBlockArea, fobj.getCommonBorderPaddingBackground());
+            TraitSetter.addBorders(viewportBlockArea, getBlockContainerFO().getCommonBorderPaddingBackground());
+            TraitSetter.addBackground(viewportBlockArea, getBlockContainerFO().getCommonBorderPaddingBackground());
             TraitSetter.addMargins(viewportBlockArea, 
-                    fobj.getCommonBorderPaddingBackground(),
-                    fobj.getCommonMarginBlock());
+                    getBlockContainerFO().getCommonBorderPaddingBackground(),
+                    getBlockContainerFO().getCommonMarginBlock());
             
             viewportBlockArea.setCTM(absoluteCTM);
             viewportBlockArea.setClip(clip);
@@ -574,15 +1148,16 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
 
         //Handle display-align now that the used BPD can be determined
         usedBPD = referenceArea.getAllocBPD();
+        /* done by the breaker now by inserting additional boxes
         if (!autoHeight & (usedBPD > 0)) {
-            if (fobj.getDisplayAlign() == EN_CENTER) {
+            if (getBlockContainerFO().getDisplayAlign() == EN_CENTER) {
                 viewportBlockArea.setCTM(viewportBlockArea.getCTM().multiply(
                         new CTM().translate(0, (relDims.bpd - usedBPD) / 2)));
-            } else if (fobj.getDisplayAlign() == EN_AFTER) {
+            } else if (getBlockContainerFO().getDisplayAlign() == EN_AFTER) {
                 viewportBlockArea.setCTM(viewportBlockArea.getCTM().multiply(
                         new CTM().translate(0, (relDims.bpd - usedBPD))));
             }
-        }
+        }*/
         
         // Fake a 0 height for absolute positioned blocks.
         int saveBPD = viewportBlockArea.getBPD();
@@ -595,6 +1170,54 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager {
             viewportBlockArea.setBPD(saveBPD);
         }
     }
-    
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#negotiateBPDAdjustment(int, org.apache.fop.layoutmgr.KnuthElement)
+     */
+    public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#discardSpace(org.apache.fop.layoutmgr.KnuthGlue)
+     */
+    public void discardSpace(KnuthGlue spaceGlue) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether()
+     */
+    public boolean mustKeepTogether() {
+        //TODO Keeps will have to be more sophisticated sooner or later
+        return ((BlockLevelLayoutManager)getParent()).mustKeepTogether() 
+                || !getBlockContainerFO().getKeepTogether().getWithinPage().isAuto()
+                || !getBlockContainerFO().getKeepTogether().getWithinColumn().isAuto();
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious()
+     */
+    public boolean mustKeepWithPrevious() {
+        return !getBlockContainerFO().getKeepWithPrevious().getWithinPage().isAuto()
+                || !getBlockContainerFO().getKeepWithPrevious().getWithinColumn().isAuto();
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext()
+     */
+    public boolean mustKeepWithNext() {
+        return !getBlockContainerFO().getKeepWithNext().getWithinPage().isAuto()
+                || !getBlockContainerFO().getKeepWithNext().getWithinColumn().isAuto();
+    }
+
+    /**
+     * convenience method that returns the BlockContainer node
+     */
+    protected BlockContainer getBlockContainerFO() {
+        return (BlockContainer) fobj;
+    }    
 }
 
index 74e29f7846b10f4023431e99f82bc607180b9427..5334bab97c876df818b517177bfce3078ff9ac84 100644 (file)
@@ -18,6 +18,7 @@
 
 package org.apache.fop.layoutmgr;
 
+import java.util.LinkedList;
 import java.util.ListIterator;
 import java.util.List;
 
@@ -35,9 +36,7 @@ import org.apache.fop.traits.MinOptMax;
 public class BlockLayoutManager extends BlockStackingLayoutManager {
     
     private static final int FINISHED_LEAF_POS = -2;
-    
-    private org.apache.fop.fo.flow.Block fobj;
-    
+        
     private Block curBlockArea;
 
     /** Iterator over the child layout managers. */
@@ -64,13 +63,14 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
 
     private int iStartPos = 0;
 
-    private int referenceIPD = 0;
     //private int contentIPD = 0;
     
     /** The list of child BreakPoss instances. */
     protected List childBreaks = new java.util.ArrayList();
 
     private boolean isfirst = true;
+    
+    private LineLayoutManager childLLM = null;
 
     /**
      * Creates a new BlockLayoutManager.
@@ -78,15 +78,15 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
      */
     public BlockLayoutManager(org.apache.fop.fo.flow.Block inBlock) {
         super(inBlock);
-        fobj = inBlock;
         proxyLMiter = new ProxyLMiter();
 
-        Font fs = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo());
+        Font fs = getBlockFO().getCommonFont().getFontState(
+                  getBlockFO().getFOEventHandler().getFontInfo());
         
         lead = fs.getAscender();
         follow = -fs.getDescender();
         middleShift = -fs.getXHeight() / 2;
-        lineHeight = fobj.getLineHeight().getOptimum().getLength().getValue();
+        lineHeight = getBlockFO().getLineHeight().getOptimum().getLength().getValue();
     }
 
     /**
@@ -95,8 +95,18 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
      *      if defined for the block.
      */
     protected void initProperties() {
-        foBlockSpaceBefore = new SpaceVal(fobj.getCommonMarginBlock().spaceBefore).getSpace();
+        foBlockSpaceBefore = new SpaceVal(getBlockFO().getCommonMarginBlock().spaceBefore).getSpace();
         prevFoBlockSpaceAfter = foBlockSpaceAfter;
+/*LF*/  bpUnit = 0; //layoutProps.blockProgressionUnit;
+/*LF*/  if (bpUnit == 0) {
+/*LF*/      // use optimum space values
+/*LF*/      adjustedSpaceBefore = getBlockFO().getCommonMarginBlock().spaceBefore.getSpace().getOptimum().getLength().getValue();
+/*LF*/      adjustedSpaceAfter = getBlockFO().getCommonMarginBlock().spaceAfter.getSpace().getOptimum().getLength().getValue();
+/*LF*/  } else {
+/*LF*/      // use minimum space values
+/*LF*/      adjustedSpaceBefore = getBlockFO().getCommonMarginBlock().spaceBefore.getSpace().getMinimum().getLength().getValue();
+/*LF*/      adjustedSpaceAfter = getBlockFO().getCommonMarginBlock().spaceAfter.getSpace().getMinimum().getLength().getValue();
+/*LF*/  }
     }
 
     /**
@@ -155,7 +165,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
      */
     private LineLayoutManager createLineManager(LayoutManager firstlm) {
         LineLayoutManager llm;
-        llm = new LineLayoutManager(fobj, lineHeight, lead, follow, middleShift);
+        llm = new LineLayoutManager(getBlockFO(), lineHeight, lead, follow, middleShift);
         List inlines = new java.util.ArrayList();
         inlines.add(firstlm);
         while (proxyLMiter.hasNext()) {
@@ -173,15 +183,15 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
 
     private int getIPIndents() {
         int iIndents = 0;
-        iIndents += fobj.getCommonMarginBlock().startIndent.getValue();
-        iIndents += fobj.getCommonMarginBlock().endIndent.getValue();
+        iIndents += getBlockFO().getCommonMarginBlock().startIndent.getValue();
+        iIndents += getBlockFO().getCommonMarginBlock().endIndent.getValue();
         return iIndents;
     }
     
     /**
      * @see org.apache.fop.layoutmgr.LayoutManager#getNextBreakPoss(org.apache.fop.layoutmgr.LayoutContext)
      */
-    public BreakPoss getNextBreakPoss(LayoutContext context) {
+    public BreakPoss getNextBreakPossOLDOLDOLD(LayoutContext context) {
         LayoutManager curLM; // currently active LM
 
         //int refipd = context.getRefIPD();
@@ -204,8 +214,8 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
         BreakPoss lastPos = null;
 
         // Set context for percentage property values.
-        fobj.setLayoutDimension(PercentBase.BLOCK_IPD, contentipd);
-        fobj.setLayoutDimension(PercentBase.BLOCK_BPD, -1);
+        getBlockFO().setLayoutDimension(PercentBase.BLOCK_IPD, contentipd);
+        getBlockFO().setLayoutDimension(PercentBase.BLOCK_BPD, -1);
 
         while ((curLM = getChildLM()) != null) {
             // Make break positions and return blocks!
@@ -263,7 +273,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
             if (getChildLM() == null || over) {
                 if (getChildLM() == null) {
                     setFinished(true);
-                    stackSize.add(new SpaceVal(fobj.getCommonMarginBlock().spaceAfter).getSpace());
+                    stackSize.add(new SpaceVal(getBlockFO().getCommonMarginBlock().spaceAfter).getSpace());
                 }
                 BreakPoss breakPoss = new BreakPoss(
                                     new LeafPosition(this, childBreaks.size() - 1));
@@ -289,10 +299,42 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
         return breakPoss;
     }
 
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether()
+     */
+    public boolean mustKeepTogether() {
+        //TODO Keeps will have to be more sophisticated sooner or later
+        return ((BlockLevelLayoutManager)getParent()).mustKeepTogether() 
+                || !getBlockFO().getKeepTogether().getWithinPage().isAuto()
+                || !getBlockFO().getKeepTogether().getWithinColumn().isAuto();
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious()
+     */
+    public boolean mustKeepWithPrevious() {
+        return !getBlockFO().getKeepWithPrevious().getWithinPage().isAuto()
+            || !getBlockFO().getKeepWithPrevious().getWithinColumn().isAuto();
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext()
+     */
+    public boolean mustKeepWithNext() {
+        return !getBlockFO().getKeepWithNext().getWithinPage().isAuto()
+                || !getBlockFO().getKeepWithNext().getWithinColumn().isAuto();
+    }
+
+    //TODO this method is no longer used
+    public BreakPoss getNextBreakPoss(LayoutContext context) {
+        setFinished(true);
+        return null;
+    }
+
     /**
      * @see org.apache.fop.layoutmgr.LayoutManager#addAreas(org.apache.fop.layoutmgr.PositionIterator, org.apache.fop.layoutmgr.LayoutContext)
      */
-    public void addAreas(PositionIterator parentIter,
+    public void addAreasOLDOLDOLD(PositionIterator parentIter,
                          LayoutContext layoutContext) {
         getParentArea(null);
 
@@ -305,8 +347,9 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
         foBlockSpaceBefore = null;
 
         if (!isBogus()) {
-            addID(fobj.getId());
-            addMarkers(true, bp1.isFirstArea(), bp1.isLastArea());
+            getPSLM().addIDToPage(getBlockFO().getId());
+            getCurrentPV().addMarkers(markers, true, bp1.isFirstArea(), 
+                    bp1.isLastArea());
         }
 
         try {
@@ -328,17 +371,183 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
             }
         } finally {
             if (!isBogus()) {
-                addMarkers(false, bp1.isFirstArea(), bp1.isLastArea());
+                getCurrentPV().addMarkers(markers, false, bp1.isFirstArea(), 
+                    bp1.isLastArea());
             }
             flush();
 
             // if adjusted space after
-            foBlockSpaceAfter = new SpaceVal(fobj.getCommonMarginBlock().spaceAfter).getSpace();
+            foBlockSpaceAfter = new SpaceVal(getBlockFO().getCommonMarginBlock().spaceAfter).getSpace();
             addBlockSpacing(adjust, foBlockSpaceAfter);
             curBlockArea = null;
         }
     }
 
+    public void addAreas(PositionIterator parentIter,
+            LayoutContext layoutContext) {
+        /* LF *///System.out.println(" BLM.addAreas>");
+        getParentArea(null);
+
+        // if this will create the first block area in a page
+        // and display-align is bottom or center, add space before
+        if (layoutContext.getSpaceBefore() > 0) {
+            addBlockSpacing(0.0, new MinOptMax(layoutContext.getSpaceBefore()));
+        }
+
+        getPSLM().addIDToPage(getBlockFO().getId());
+        //addMarkersToPV(true, bp1.isFirstArea(), bp1.isLastArea());
+        getCurrentPV().addMarkers(markers, true, true, false);
+
+        LayoutManager childLM = null;
+        LayoutManager lastLM = null;
+        LayoutContext lc = new LayoutContext(0);
+        /* LF */// set space after in the LayoutContext for children
+        /* LF */if (layoutContext.getSpaceAfter() > 0) {
+            /* LF */lc.setSpaceAfter(layoutContext.getSpaceAfter());
+            /* LF */}
+        /* LF */PositionIterator childPosIter;
+
+        // "unwrap" the NonLeafPositions stored in parentIter
+        // and put them in a new list;
+        LinkedList positionList = new LinkedList();
+        Position pos;
+        boolean bSpaceBefore = false;
+        boolean bSpaceAfter = false;
+        while (parentIter.hasNext()) {
+            pos = (Position) parentIter.next();
+            //log.trace("pos = " + pos.getClass().getName() + "; " + pos);
+            Position innerPosition = pos;
+            if (pos instanceof NonLeafPosition) {
+                //Not all elements are wrapped
+                innerPosition = ((NonLeafPosition) pos).getPosition();
+            }
+            if (innerPosition == null) {
+                // pos was created by this BlockLM and was inside an element
+                // representing space before or after
+                // this means the space was not discarded
+                if (positionList.size() == 0) {
+                    // pos was in the element representing space-before
+                    bSpaceBefore = true;
+                    //log.trace(" space before");
+                } else {
+                    // pos was in the element representing space-after
+                    bSpaceAfter = true;
+                    //log.trace(" space-after");
+                }
+            } else if (innerPosition.getLM() == this
+                    && !(innerPosition instanceof MappingPosition)) {
+                // pos was created by this BlockLM and was inside a penalty
+                // allowing or forbidding a page break
+                // nothing to do
+                //log.trace(" penalty");
+            } else {
+                // innerPosition was created by another LM
+                positionList.add(innerPosition);
+                lastLM = innerPosition.getLM();
+                //log.trace(" " + innerPosition.getClass().getName());
+            }
+        }
+
+        if (bpUnit == 0) {
+            // the Positions in positionList were inside the elements
+            // created by the LineLM
+            childPosIter = new StackingIter(positionList.listIterator());
+            } else {
+            // the Positions in positionList were inside the elements
+            // created by the BlockLM in the createUnitElements() method
+            //if (((Position) positionList.getLast()) instanceof
+                  // LeafPosition) {
+            //    // the last item inside positionList is a LeafPosition
+            //    // (a LineBreakPosition, more precisely); this means that
+            //    // the whole paragraph is on the same page
+            //    System.out.println("paragrafo intero");
+            //    childPosIter = new KnuthPossPosIter(storedList, 0,
+                  // storedList.size());
+            //} else {
+            //    // the last item inside positionList is a Position;
+            //    // this means that the paragraph has been split
+            //    // between consecutive pages
+            LinkedList splitList = new LinkedList();
+            int splitLength = 0;
+            int iFirst = ((MappingPosition) positionList.getFirst()).getFirstIndex();
+            int iLast = ((MappingPosition) positionList.getLast()).getLastIndex();
+            // copy from storedList to splitList all the elements from
+            // iFirst to iLast
+            ListIterator storedListIterator = storedList.listIterator(iFirst);
+            while (storedListIterator.nextIndex() <= iLast) {
+                KnuthElement element = (KnuthElement) storedListIterator
+                        .next();
+                // some elements in storedList (i.e. penalty items) were created
+                // by this BlockLM, and must be ignored
+                if (element.getLayoutManager() != this) {
+                    splitList.add(element);
+                    splitLength += element.getW();
+                    lastLM = element.getLayoutManager();
+                }
+                }
+            //System.out.println("addAreas riferito a storedList da " +
+                  // iFirst + " a " + iLast);
+            //System.out.println("splitLength= " + splitLength
+            //                   + " (" + neededUnits(splitLength) + " unita') "
+            //                   + (neededUnits(splitLength) * bpUnit - splitLength) + " spazi");
+            // add space before and / or after the paragraph
+            // to reach a multiple of bpUnit
+            if (bSpaceBefore && bSpaceAfter) {
+                foBlockSpaceBefore = new SpaceVal(getBlockFO().getCommonMarginBlock().spaceBefore).getSpace();
+                foBlockSpaceAfter = new SpaceVal(getBlockFO().getCommonMarginBlock().spaceAfter).getSpace();
+                adjustedSpaceBefore = (neededUnits(splitLength
+                        + foBlockSpaceBefore.min
+                        + foBlockSpaceAfter.min)
+                        * bpUnit - splitLength) / 2;
+                adjustedSpaceAfter = neededUnits(splitLength
+                        + foBlockSpaceBefore.min
+                        + foBlockSpaceAfter.min)
+                        * bpUnit - splitLength - adjustedSpaceBefore;
+                } else if (bSpaceBefore) {
+                adjustedSpaceBefore = neededUnits(splitLength
+                        + foBlockSpaceBefore.min)
+                        * bpUnit - splitLength;
+                } else {
+                adjustedSpaceAfter = neededUnits(splitLength
+                        + foBlockSpaceAfter.min)
+                        * bpUnit - splitLength;
+                }
+            //System.out.println("spazio prima = " + adjustedSpaceBefore
+                  // + " spazio dopo = " + adjustedSpaceAfter + " totale = " +
+                  // (adjustedSpaceBefore + adjustedSpaceAfter + splitLength));
+            childPosIter = new KnuthPossPosIter(splitList, 0, splitList
+                    .size());
+            //}
+            }
+
+        // if adjusted space before
+        if (bSpaceBefore) {
+            addBlockSpacing(0, new MinOptMax(adjustedSpaceBefore));
+        }
+
+        while ((childLM = childPosIter.getNextChildLM()) != null) {
+            // set last area flag
+            lc.setFlags(LayoutContext.LAST_AREA,
+                    (layoutContext.isLastArea() && childLM == lastLM));
+            /*LF*/lc.setStackLimit(layoutContext.getStackLimit());
+            // Add the line areas to Area
+            childLM.addAreas(childPosIter, lc);
+        }
+
+        int bIndents = getBlockFO().getCommonBorderPaddingBackground().getBPPaddingAndBorder(false);
+
+        getCurrentPV().addMarkers(markers, false, false, true);
+
+        flush();
+
+        // if adjusted space after
+        if (bSpaceAfter) {
+            addBlockSpacing(0, new MinOptMax(adjustedSpaceAfter));
+        }
+
+        curBlockArea = null;
+    }
+
     /**
      * Return an Area which can contain the passed childArea. The childArea
      * may not yet have any content, but it has essential traits set.
@@ -354,20 +563,21 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
         if (curBlockArea == null) {
             curBlockArea = new Block();
 
+            TraitSetter.addBreaks(curBlockArea, 
+                    getBlockFO().getBreakBefore(), getBlockFO().getBreakAfter());
+
             // Must get dimensions from parent area
             //Don't optimize this line away. It can have ugly side-effects.
             /*Area parentArea =*/ parentLM.getParentArea(curBlockArea);
 
             // set traits
             TraitSetter.addBorders(curBlockArea, 
-                    fobj.getCommonBorderPaddingBackground());
+                    getBlockFO().getCommonBorderPaddingBackground());
             TraitSetter.addBackground(curBlockArea, 
-                    fobj.getCommonBorderPaddingBackground());
+                    getBlockFO().getCommonBorderPaddingBackground());
             TraitSetter.addMargins(curBlockArea,
-                    fobj.getCommonBorderPaddingBackground(), 
-                    fobj.getCommonMarginBlock());
-            TraitSetter.addBreaks(curBlockArea, 
-                    fobj.getBreakBefore(), fobj.getBreakAfter());
+                    getBlockFO().getCommonBorderPaddingBackground(), 
+                    getBlockFO().getCommonMarginBlock());
 
             // Set up dimensions
             // Get reference IPD from parentArea
@@ -421,5 +631,12 @@ public class BlockLayoutManager extends BlockStackingLayoutManager {
             LayoutManager lm = resetPos.getLM();
         }
     }
+
+    /**
+     * convenience method that returns the Block node
+     */
+    protected org.apache.fop.fo.flow.Block getBlockFO() {
+        return (org.apache.fop.fo.flow.Block) fobj;
+    }
 }
 
diff --git a/src/java/org/apache/fop/layoutmgr/BlockLevelLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockLevelLayoutManager.java
new file mode 100644 (file)
index 0000000..a523830
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+package org.apache.fop.layoutmgr;
+
+/**
+ * The interface for LayoutManagers which generate block areas
+ */
+public interface BlockLevelLayoutManager extends LayoutManager {
+
+    static final int NO_ADJUSTMENT = -1;
+    static final int SPACE_BEFORE_ADJUSTMENT = 0;
+    static final int SPACE_AFTER_ADJUSTMENT = 1;
+    static final int LINE_NUMBER_ADJUSTMENT = 2;
+    static final int LINE_HEIGHT_ADJUSTMENT = 3;
+
+    int negotiateBPDAdjustment(int adj, KnuthElement lastElement);
+
+    void discardSpace(KnuthGlue spaceGlue);
+
+    /**
+     * @return true if this element must be kept together
+     */
+    boolean mustKeepTogether();
+
+    /**
+     * @return true if this element must be kept with the previous element.
+     */
+    boolean mustKeepWithPrevious();
+
+    /**
+     * @return true if this element must be kept with the next element.
+     */
+    boolean mustKeepWithNext();
+
+}
index 582c5c2857460977ba6789c6262f8cf1a8abe631..ca9c8caad017dced838f0d93c7488689886150e4 100644 (file)
 
 package org.apache.fop.layoutmgr;
 
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
 import org.apache.fop.area.Area;
 import org.apache.fop.area.BlockParent;
 import org.apache.fop.area.Block;
+import org.apache.fop.datatypes.PercentBase;
+import org.apache.fop.fo.Constants;
 import org.apache.fop.fo.FObj;
+import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
+import org.apache.fop.fo.properties.CommonMarginBlock;
+import org.apache.fop.fo.properties.SpaceProperty;
 import org.apache.fop.traits.MinOptMax;
 
 /**
  * Base LayoutManager class for all areas which stack their child
  * areas in the block-progression direction, such as Flow, Block, ListBlock.
  */
-public abstract class BlockStackingLayoutManager extends AbstractLayoutManager {
+public abstract class BlockStackingLayoutManager extends AbstractLayoutManager
+                                                 implements BlockLevelLayoutManager {
     /**
      * Reference to FO whose areas it's managing or to the traits
      * of the FO.
@@ -36,8 +47,24 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager {
     //protected LayoutManager curChildLM = null; AbstractLayoutManager also defines this!
     protected BlockParent parentArea = null;
 
+    /*LF*/
+    /** Value of the block-progression-unit (non-standard property) */
+    protected int bpUnit = 0;
+    /** space-before value adjusted for block-progression-unit handling */
+    protected int adjustedSpaceBefore = 0;
+    /** space-after value adjusted for block-progression-unit handling */
+    protected int adjustedSpaceAfter = 0;
+    /** Only used to store the original list when createUnitElements is called */
+    protected LinkedList storedList = null;
+    protected FObj fobj;
+    private boolean bBreakBeforeServed = false;
+    private boolean bSpaceBeforeServed = false;
+    protected int referenceIPD = 0;
+    /*LF*/
+
     public BlockStackingLayoutManager(FObj node) {
         super(node);
+        fobj = node;
     }
 
     private BreakCost evaluateBreakCost(Area parent, Area child) {
@@ -135,5 +162,1053 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager {
         }
     }
 
+    /**
+     * @param len length in millipoints to span with bp units
+     * @return the minimum integer n such that n * bpUnit >= len
+     */
+    protected int neededUnits(int len) {
+        return (int) Math.ceil((float)len / bpUnit);
+    }
+
+    public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+        /* LF *///System.err.println("BLM.getNextKnuthElements> keep-together = "
+              // + layoutProps.keepTogether.getType());
+        /* LF *///System.err.println(" keep-with-previous = " +
+              // layoutProps.keepWithPrevious.getType());
+        /* LF *///System.err.println(" keep-with-next = " +
+              // layoutProps.keepWithNext.getType());
+        BlockLevelLayoutManager curLM; // currently active LM
+        BlockLevelLayoutManager prevLM = null; // previously active LM
+
+        referenceIPD = context.getRefIPD();
+        int iIndents = 0;
+        int bIndents = 0;
+        if (fobj instanceof org.apache.fop.fo.flow.Block) {
+            iIndents = ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().startIndent.getValue() 
+                     + ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().endIndent.getValue();
+            bIndents = ((org.apache.fop.fo.flow.Block) fobj).getCommonBorderPaddingBackground().getBPPaddingAndBorder(false);
+        }
+        int ipd = referenceIPD - iIndents;
+
+        MinOptMax stackSize = new MinOptMax();
+
+        // Set context for percentage property values.
+        fobj.setLayoutDimension(PercentBase.BLOCK_IPD, ipd);
+        fobj.setLayoutDimension(PercentBase.BLOCK_BPD, -1);
+
+        LinkedList returnedList = null;
+        LinkedList contentList = new LinkedList();
+        LinkedList returnList = new LinkedList();
+        Position returnPosition = new NonLeafPosition(this, null);
+
+        if (!bBreakBeforeServed) {
+            try {
+                if (addKnuthElementsForBreakBefore(returnList, returnPosition)) {
+                    return returnList;
+                }
+            } finally {
+                bBreakBeforeServed = true;
+            }
+        }
+
+        if (!bSpaceBeforeServed) {
+            addKnuthElementsForSpaceBefore(returnList, returnPosition, alignment);
+            bSpaceBeforeServed = true;
+        }
+        
+        addKnuthElementsForBorderPaddingBefore(returnList, returnPosition);
+        
+        while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) {
+            LayoutContext childLC = new LayoutContext(0);
+            if (curLM instanceof LineLayoutManager) {
+                // curLM is a LineLayoutManager
+                // set stackLimit for lines
+                childLC.setStackLimit(new MinOptMax(ipd/*
+                                                         * - iIndents -
+                                                         * iTextIndent
+                                                         */));
+                childLC.setRefIPD(ipd);
+            } else {
+                // curLM is a ?
+                childLC.setStackLimit(MinOptMax.subtract(context
+                        .getStackLimit(), stackSize));
+                childLC.setRefIPD(referenceIPD);
+            }
+
+            // get elements from curLM
+            returnedList = curLM.getNextKnuthElements(childLC, alignment);
+            if (returnedList.size() == 1
+                    && ((KnuthElement) returnedList.getFirst()).isPenalty()
+                    && ((KnuthPenalty) returnedList.getFirst()).getP() == -KnuthElement.INFINITE) {
+                // a descendant of this block has break-before
+                if (returnList.size() == 0) {
+                    // the first child (or its first child ...) has
+                    // break-before;
+                    // all this block, including space before, will be put in
+                    // the
+                    // following page
+                    bSpaceBeforeServed = false;
+                }
+                contentList.addAll(returnedList);
+
+                /* extension: conversione di tutta la sequenza fin'ora ottenuta */
+                if (bpUnit > 0) {
+                    storedList = contentList;
+                    contentList = createUnitElements(contentList);
+                }
+                /* end of extension */
+
+                // "wrap" the Position inside each element
+                // moving the elements from contentList to returnList
+                returnedList = new LinkedList();
+                wrapPositionElements(contentList, returnList);
+
+                return returnList;
+            } else {
+                if (prevLM != null) {
+                    // there is a block handled by prevLM
+                    // before the one handled by curLM
+                    if (mustKeepTogether() 
+                            || prevLM.mustKeepWithNext()
+                            || curLM.mustKeepWithPrevious()) {
+                        // add an infinite penalty to forbid a break between
+                        // blocks
+                        contentList.add(new KnuthPenalty(0,
+                                KnuthElement.INFINITE, false,
+                                new Position(this), false));
+                    } else if (!((KnuthElement) contentList.getLast()).isGlue()) {
+                        // add a null penalty to allow a break between blocks
+                        contentList.add(new KnuthPenalty(0, 0, false,
+                                new Position(this), false));
+                    } else {
+                        // the last element in contentList is a glue;
+                        // it is a feasible breakpoint, there is no need to add
+                        // a penalty
+                    }
+                }
+                if (returnedList.size() == 0) {
+                    //Avoid NoSuchElementException below (happens with empty blocks)
+                    continue;
+                }
+                contentList.addAll(returnedList);
+                if (((KnuthElement) returnedList.getLast()).isPenalty()
+                        && ((KnuthPenalty) returnedList.getLast()).getP() == -KnuthElement.INFINITE) {
+                    // a descendant of this block has break-after
+                    if (curLM.isFinished()) {
+                        // there is no other content in this block;
+                        // it's useless to add space after before a page break
+                        setFinished(true);
+                    }
+
+                    /* extension: conversione di tutta la sequenza fin'ora ottenuta */
+                    if (bpUnit > 0) {
+                        storedList = contentList;
+                        contentList = createUnitElements(contentList);
+                    }
+                    /* end of extension */
+
+                    returnedList = new LinkedList();
+                    wrapPositionElements(contentList, returnList);
+
+                    return returnList;
+                }
+                /*
+                if (allocatedSpace.min > context.getStackLimit().max) {
+                    log.debug("Allocated space exceeds stack limit, returning early.");
+                    return returnList;
+                }*/
+            }
+            prevLM = curLM;
+        }
+
+        /* Extension: conversione di tutta la sequenza fin'ora ottenuta */
+        if (bpUnit > 0) {
+            storedList = contentList;
+            contentList = createUnitElements(contentList);
+        }
+        /* end of extension */
+
+        returnedList = new LinkedList();
+        wrapPositionElements(contentList, returnList);
+
+        addKnuthElementsForBorderPaddingAfter(returnList, returnPosition);
+        addKnuthElementsForSpaceAfter(returnList, returnPosition, alignment);
+        addKnuthElementsForBreakAfter(returnList, returnPosition);
+
+        setFinished(true);
+
+        return returnList;
+    }
+
+    public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
+/*LF*/  //System.out.println("  BLM.negotiateBPDAdjustment> " + adj);
+/*LF*/  //System.out.println("  lastElement e' " + (lastElement.isPenalty() ? "penalty" : (lastElement.isGlue() ? "glue" : "box" )));
+/*LF*/  //System.out.println("  position e' " + lastElement.getPosition().getClass().getName());
+/*LF*/  //System.out.println("  " + (bpUnit > 0 ? "unit" : ""));
+        Position innerPosition = ((NonLeafPosition) lastElement.getPosition()).getPosition();
+
+        if (innerPosition == null && lastElement.isGlue()) {
+            // this adjustment applies to space-before or space-after of this block
+            if (((KnuthGlue) lastElement).getAdjustmentClass() == SPACE_BEFORE_ADJUSTMENT) {
+                // this adjustment applies to space-before
+                adjustedSpaceBefore += adj;
+/*LF*/          //System.out.println("  BLM.negotiateBPDAdjustment> spazio prima: " + adj);
+            } else {
+                // this adjustment applies to space-after
+                adjustedSpaceAfter += adj;
+/*LF*/          //System.out.println("  BLM.negotiateBPDAdjustment> spazio dopo: " + adj);
+            }
+            return adj;
+        } else if (innerPosition instanceof MappingPosition) {
+            // this block has block-progression-unit > 0: the adjustment can concern
+            // - the space-before or space-after of this block, 
+            // - the line number of a descendant of this block
+            if (lastElement.isGlue()) {
+                // lastElement is a glue
+/*LF*/          //System.out.println("  BLM.negotiateBPDAdjustment> bpunit con glue");
+                ListIterator storedListIterator = storedList.listIterator(((MappingPosition) innerPosition).getFirstIndex());
+                int newAdjustment = 0;
+                while (storedListIterator.nextIndex() <= ((MappingPosition) innerPosition).getLastIndex()) {
+                    KnuthElement storedElement = (KnuthElement) storedListIterator.next();
+                    if (storedElement.isGlue()) {
+                        newAdjustment += ((BlockLevelLayoutManager) storedElement.getLayoutManager()).negotiateBPDAdjustment(adj - newAdjustment, storedElement);
+/*LF*/                  //System.out.println("  BLM.negotiateBPDAdjustment> (progressivo) righe: " + newAdjustment);
+                    }
+                }
+                newAdjustment = (newAdjustment > 0 ? bpUnit * neededUnits(newAdjustment)
+                                                   : - bpUnit * neededUnits(- newAdjustment));
+                return newAdjustment;
+            } else {
+                // lastElement is a penalty: this means that the paragraph
+                // has been split between consecutive pages:
+                // this may involve a change in the number of lines
+/*LF*/          //System.out.println("  BLM.negotiateBPDAdjustment> bpunit con penalty");
+                KnuthPenalty storedPenalty = (KnuthPenalty)
+                                             storedList.get(((MappingPosition) innerPosition).getLastIndex());
+                if (storedPenalty.getW() > 0) {
+                    // the original penalty has width > 0
+/*LF*/              //System.out.println("  BLM.negotiateBPDAdjustment> chiamata passata");
+                    return ((BlockLevelLayoutManager) storedPenalty.getLayoutManager())
+                           .negotiateBPDAdjustment(storedPenalty.getW(), (KnuthElement) storedPenalty);
+                } else {
+                    // the original penalty has width = 0
+                    // the adjustment involves only the spaces before and after
+/*LF*/              //System.out.println("  BLM.negotiateBPDAdjustment> chiamata gestita");
+                    return adj;
+                }
+            }
+        } else if (innerPosition.getLM() != this) {
+            // this adjustment concerns another LM
+            NonLeafPosition savedPos = (NonLeafPosition) lastElement.getPosition();
+            lastElement.setPosition(innerPosition);
+            int returnValue = ((BlockLevelLayoutManager) lastElement.getLayoutManager()).negotiateBPDAdjustment(adj, lastElement);
+            lastElement.setPosition(savedPos);
+/*LF*/      //System.out.println("  BLM.negotiateBPDAdjustment> righe: " + returnValue);
+            return returnValue;
+        } else {
+            // this should never happen
+            System.err.println("BlockLayoutManager.negotiateBPDAdjustment(): unexpected Position");
+            return 0;
+        }
+    }
+
+    public void discardSpace(KnuthGlue spaceGlue) {
+/*LF*/  //System.out.println("  BLM.discardSpace> " + spaceGlue.getPosition().getClass().getName());
+        Position innerPosition = ((NonLeafPosition) spaceGlue.getPosition()).getPosition();
+
+/*LF*/  if (innerPosition == null || innerPosition.getLM() == this) {
+            // if this block has block-progression-unit > 0, innerPosition can be
+            // a MappingPosition
+            // spaceGlue represents space before or space after of this block
+            if (spaceGlue.getAdjustmentClass() == SPACE_BEFORE_ADJUSTMENT) {
+                // space-before must be discarded
+                adjustedSpaceBefore = 0;
+            } else {
+                // space-after must be discarded
+                adjustedSpaceAfter = 0;
+                //TODO Why are both cases handled in the same way?
+            }
+/*LF*/  } else {
+            // this element was not created by this BlockLM
+            NonLeafPosition savedPos = (NonLeafPosition)spaceGlue.getPosition();
+            spaceGlue.setPosition(innerPosition);
+            ((BlockLevelLayoutManager) spaceGlue.getLayoutManager()).discardSpace(spaceGlue);
+            spaceGlue.setPosition(savedPos);
+        }
+    }
+
+    public LinkedList getChangedKnuthElements(List oldList, /*int flaggedPenalty,*/ int alignment) {
+/*LF*/  //System.out.println("");
+/*LF*/  //System.out.println("  BLM.getChangedKnuthElements> inizio: oldList.size() = " + oldList.size());
+        ListIterator oldListIterator = oldList.listIterator();
+        KnuthElement returnedElement;
+        KnuthElement currElement = null;
+        KnuthElement prevElement = null;
+        LinkedList returnedList = new LinkedList();
+        LinkedList returnList = new LinkedList();
+        int fromIndex = 0;
+
+        // "unwrap" the Positions stored in the elements
+        KnuthElement oldElement = null;
+        while (oldListIterator.hasNext()) {
+            oldElement = (KnuthElement)oldListIterator.next();
+            Position innerPosition = ((NonLeafPosition) oldElement.getPosition()).getPosition();
+/*LF*/      //System.out.println(" BLM> unwrapping: " + (oldElement.isBox() ? "box    " : (oldElement.isGlue() ? "glue   " : "penalty")) + " creato da " + oldElement.getLayoutManager().getClass().getName());
+/*LF*/      //System.out.println(" BLM> unwrapping:         " + oldElement.getPosition().getClass().getName());
+            if (innerPosition != null) {
+                // oldElement was created by a descendant of this BlockLM
+                oldElement.setPosition(innerPosition);
+            } else {
+                // thisElement was created by this BlockLM
+                // modify its position in order to recognize it was not created
+                // by a child
+                oldElement.setPosition(new Position(this));
+            }
+        }
+
+        // create the iterator
+        List workList;
+        if (bpUnit == 0) {
+            workList = oldList;
+        } else {
+            // the storedList must be used instead of oldList;
+            // find the index of the first element of returnedList
+            // corresponding to the first element of oldList
+            oldListIterator = oldList.listIterator();
+            KnuthElement el = (KnuthElement) oldListIterator.next();
+            while (!(el.getPosition() instanceof MappingPosition)) {
+                el = (KnuthElement) oldListIterator.next();
+            }
+            int iFirst = ((MappingPosition) el.getPosition()).getFirstIndex();
+
+            // find the index of the last element of returnedList
+            // corresponding to the last element of oldList
+            oldListIterator = oldList.listIterator(oldList.size());
+            el = (KnuthElement) oldListIterator.previous();
+            while (!(el.getPosition() instanceof MappingPosition)) {
+                el = (KnuthElement) oldListIterator.previous();
+            }
+            int iLast = ((MappingPosition) el.getPosition()).getLastIndex();
+
+/*LF*/      //System.out.println("  si usa storedList da " + iFirst + " a " + iLast + " compresi su " + storedList.size() + " elementi totali");
+            workList = storedList.subList(iFirst, iLast + 1);
+        }
+        ListIterator workListIterator = workList.listIterator();
+
+/*LF*/  //System.out.println("");
+/*LF*/  //System.out.println("  BLM.getChangedKnuthElements> workList.size() = " + workList.size() + " da 0 a " + (workList.size() - 1));
+
+        while (workListIterator.hasNext()) {
+            currElement = (KnuthElement) workListIterator.next();
+/*LF*/      //System.out.println("elemento n. " + workListIterator.previousIndex() + " nella workList");
+            if (prevElement != null
+                && prevElement.getLayoutManager() != currElement.getLayoutManager()) {
+                // prevElement is the last element generated by the same LM
+                BlockLevelLayoutManager prevLM = (BlockLevelLayoutManager)
+                                                 prevElement.getLayoutManager();
+                BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
+                                                 currElement.getLayoutManager();
+                boolean bSomethingAdded = false;
+                if (prevLM != this) {
+/*LF*/              //System.out.println(" BLM.getChangedKnuthElements> chiamata da " + fromIndex + " a " + workListIterator.previousIndex() + " su " + prevLM.getClass().getName());
+                    returnedList.addAll(prevLM.getChangedKnuthElements(workList.subList(fromIndex, workListIterator.previousIndex()),
+                                                                       /*flaggedPenalty,*/ alignment));
+                    bSomethingAdded = true;
+                } else {
+                    // prevLM == this
+                    // do nothing
+/*LF*/              //System.out.println(" BLM.getChangedKnuthElements> elementi propri, ignorati, da " + fromIndex + " a " + workListIterator.previousIndex() + " su " + prevLM.getClass().getName());
+                }
+                fromIndex = workListIterator.previousIndex();
+
+                // there is another block after this one
+                if (bSomethingAdded
+                    && (this.mustKeepTogether()
+                        || prevLM.mustKeepWithNext()
+                        || currLM.mustKeepWithPrevious())) {
+                    // add an infinite penalty to forbid a break between blocks
+                    returnedList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, new Position(this), false));
+                } else if (bSomethingAdded && !((KnuthElement) returnedList.getLast()).isGlue()) {
+                    // add a null penalty to allow a break between blocks
+                    returnedList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
+                }
+            }
+            prevElement = currElement;
+        }
+        if (currElement != null) {
+            BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
+                                             currElement.getLayoutManager();
+            if (currLM != this) {
+/*LF*/          //System.out.println(" BLM.getChangedKnuthElements> chiamata da " + fromIndex + " a " + oldList.size() + " su " + currLM.getClass().getName());
+                returnedList.addAll(currLM.getChangedKnuthElements(workList.subList(fromIndex, workList.size()),
+                                                                   /*flaggedPenalty,*/ alignment));
+            } else {
+                // currLM == this
+                // there are no more elements to add
+                // remove the last penalty added to returnedList
+                if (returnedList.size() > 0) {
+                    returnedList.removeLast();
+                }
+/*LF*/          //System.out.println(" BLM.getChangedKnuthElements> elementi propri, ignorati, da " + fromIndex + " a " + workList.size());
+            }
+        }
+
+        // append elements representing space-before
+        boolean spaceBeforeIsConditional = true;
+        if (fobj instanceof org.apache.fop.fo.flow.Block) {
+            spaceBeforeIsConditional =
+                ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().spaceBefore.getSpace().isDiscard();
+        }
+        if (bpUnit > 0
+            || adjustedSpaceBefore != 0) {
+            if (!spaceBeforeIsConditional) {
+                // add elements to prevent the glue to be discarded
+                returnList.add(new KnuthBox(0,
+                                            new NonLeafPosition(this, null), false));
+                returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
+                                                new NonLeafPosition(this, null), false));
+            }
+            if (bpUnit > 0) {
+                returnList.add(new KnuthGlue(0, 0, 0,
+                                             SPACE_BEFORE_ADJUSTMENT, new NonLeafPosition(this, null), true));
+            } else {
+                returnList.add(new KnuthGlue(adjustedSpaceBefore, 0, 0,
+                                             SPACE_BEFORE_ADJUSTMENT, new NonLeafPosition(this, null), true));
+            }
+        }
+
+/*LF*/  //System.out.println("  BLM.getChangedKnuthElements> intermedio: returnedList.size() = " + returnedList.size());
+
+/* estensione: conversione complessiva */
+/*LF*/  if (bpUnit > 0) {
+/*LF*/      storedList = returnedList;
+/*LF*/      returnedList = createUnitElements(returnedList);
+/*LF*/  }
+/* estensione */
+
+        // "wrap" the Position stored in each element of returnedList
+        // and add elements to returnList
+        ListIterator listIter = returnedList.listIterator();
+        while (listIter.hasNext()) {
+            returnedElement = (KnuthElement)listIter.next();
+            returnedElement.setPosition(new NonLeafPosition(this, returnedElement.getPosition()));
+            returnList.add(returnedElement);
+        }
+
+        // append elements representing space-after
+        boolean spaceAfterIsConditional = true;
+        if (fobj instanceof org.apache.fop.fo.flow.Block) {
+            spaceAfterIsConditional =
+                ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().spaceAfter.getSpace().isDiscard();
+        }
+        if (bpUnit > 0
+            || adjustedSpaceAfter != 0) {
+            if (!spaceAfterIsConditional) {
+                returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
+                                                new NonLeafPosition(this, null), false));
+            }
+            if (bpUnit > 0) {
+                returnList.add(new KnuthGlue(0, 0, 0,
+                                             SPACE_AFTER_ADJUSTMENT, new NonLeafPosition(this, null),
+                                             (!spaceAfterIsConditional) ? false : true));
+            } else {
+                returnList.add(new KnuthGlue(adjustedSpaceAfter, 0, 0,
+                                             SPACE_AFTER_ADJUSTMENT, new NonLeafPosition(this, null),
+                                             (!spaceAfterIsConditional) ? false : true));
+            }
+            if (!spaceAfterIsConditional) {
+                returnList.add(new KnuthBox(0,
+                                            new NonLeafPosition(this, null), true));
+            }
+        }
+
+/*LF*/  //System.out.println("  BLM.getChangedKnuthElements> fine: returnList.size() = " + returnList.size());
+        return returnList;
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether()
+     */
+    public boolean mustKeepTogether() {
+        return false;
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious()
+     */
+    public boolean mustKeepWithPrevious() {
+        return false;
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext()
+     */
+    public boolean mustKeepWithNext() {
+        return false;
+    }
+
+    /**
+     * Creates Knuth elements for before border padding and adds them to the return list.
+     * @param returnList return list to add the additional elements to
+     * @param returnPosition applicable return position
+     */
+    protected void addKnuthElementsForBorderPaddingBefore(LinkedList returnList, 
+            Position returnPosition) {
+        //Border and Padding (before)
+        CommonBorderPaddingBackground borderAndPadding = null;
+        if (fobj instanceof org.apache.fop.fo.flow.Block) {
+            borderAndPadding =
+                ((org.apache.fop.fo.flow.Block) fobj).getCommonBorderPaddingBackground();
+        } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
+            borderAndPadding =
+                ((org.apache.fop.fo.flow.BlockContainer) fobj).getCommonBorderPaddingBackground();
+        }
+        if (borderAndPadding != null) {
+            //TODO Handle conditionality
+            int bpBefore = borderAndPadding.getBorderBeforeWidth(false)
+                         + borderAndPadding.getPaddingBefore(false);
+            if (bpBefore > 0) {
+                returnList.add(new KnuthBox(bpBefore, returnPosition, true));
+            }
+        }
+    }
+
+    /**
+     * Creates Knuth elements for after border padding and adds them to the return list.
+     * @param returnList return list to add the additional elements to
+     * @param returnPosition applicable return position
+     */
+    protected void addKnuthElementsForBorderPaddingAfter(LinkedList returnList, 
+            Position returnPosition) {
+        //Border and Padding (after)
+        CommonBorderPaddingBackground borderAndPadding = null;
+        if (fobj instanceof org.apache.fop.fo.flow.Block) {
+            borderAndPadding =
+                ((org.apache.fop.fo.flow.Block) fobj).getCommonBorderPaddingBackground();
+        } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
+            borderAndPadding =
+                ((org.apache.fop.fo.flow.BlockContainer) fobj).getCommonBorderPaddingBackground();
+        }
+        if (borderAndPadding != null) {
+            //TODO Handle conditionality
+            int bpAfter = borderAndPadding.getBorderAfterWidth(false)
+                        + borderAndPadding.getPaddingAfter(false);
+            if (bpAfter > 0) {
+                returnList.add(new KnuthBox(bpAfter, returnPosition, true));
+            }
+        }
+    }
+
+    /**
+     * Creates Knuth elements for break-before and adds them to the return list.
+     * @param returnList return list to add the additional elements to
+     * @param returnPosition applicable return position
+     * @return true if an element has been added due to a break-before.
+     */
+    protected boolean addKnuthElementsForBreakBefore(LinkedList returnList, 
+            Position returnPosition) {
+        int breakBefore = -1;
+        if (fobj instanceof org.apache.fop.fo.flow.Block) {
+            breakBefore = ((org.apache.fop.fo.flow.Block) fobj).getBreakBefore();
+        } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
+            breakBefore = ((org.apache.fop.fo.flow.BlockContainer) fobj).getBreakBefore();
+        }
+        if (breakBefore == EN_PAGE
+                || breakBefore == EN_COLUMN 
+                || breakBefore == EN_EVEN_PAGE 
+                || breakBefore == EN_ODD_PAGE) {
+            // return a penalty element, representing a forced page break
+            returnList.add(new KnuthPenalty(0, -KnuthElement.INFINITE, false,
+                    breakBefore, returnPosition, false));
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Creates Knuth elements for break-after and adds them to the return list.
+     * @param returnList return list to add the additional elements to
+     * @param returnPosition applicable return position
+     * @return true if an element has been added due to a break-after.
+     */
+    protected boolean addKnuthElementsForBreakAfter(LinkedList returnList, 
+            Position returnPosition) {
+        int breakAfter = -1;
+        if (fobj instanceof org.apache.fop.fo.flow.Block) {
+            breakAfter = ((org.apache.fop.fo.flow.Block) fobj).getBreakAfter();
+        } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
+            breakAfter = ((org.apache.fop.fo.flow.BlockContainer) fobj).getBreakAfter();
+        }
+        if (breakAfter == EN_PAGE
+                || breakAfter == EN_COLUMN
+                || breakAfter == EN_EVEN_PAGE
+                || breakAfter == EN_ODD_PAGE) {
+            // add a penalty element, representing a forced page break
+            returnList.add(new KnuthPenalty(0, -KnuthElement.INFINITE, false,
+                    breakAfter, returnPosition, false));
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Creates Knuth elements for space-before and adds them to the return list.
+     * @param returnList return list to add the additional elements to
+     * @param returnPosition applicable return position
+     * @param alignment vertical alignment
+     */
+    protected void addKnuthElementsForSpaceBefore(LinkedList returnList, 
+            Position returnPosition, int alignment) {
+        SpaceProperty spaceBefore = null;
+        if (fobj instanceof org.apache.fop.fo.flow.Block) {
+            spaceBefore = ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().spaceBefore;
+        } else if (fobj instanceof org.apache.fop.fo.flow.BlockContainer) {
+            spaceBefore = ((org.apache.fop.fo.flow.BlockContainer) fobj).getCommonMarginBlock().spaceBefore;
+        }
+        // append elements representing space-before
+        if (bpUnit > 0
+                || spaceBefore != null
+                   && !(spaceBefore.getMinimum().getLength().getValue() == 0 
+                        && spaceBefore.getMaximum().getLength().getValue() == 0)) {
+            if (spaceBefore != null && !spaceBefore.getSpace().isDiscard()) {
+                // add elements to prevent the glue to be discarded
+                returnList.add(new KnuthBox(0, returnPosition, false));
+                returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE,
+                        false, returnPosition, false));
+            }
+            if (bpUnit > 0) {
+                returnList.add(new KnuthGlue(0, 0, 0,
+                        BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT, 
+                        returnPosition, true));
+            } else /*if (alignment == EN_JUSTIFY)*/ {
+                returnList.add(new KnuthGlue(
+                        spaceBefore.getOptimum().getLength().getValue(),
+                        spaceBefore.getMaximum().getLength().getValue()
+                                - spaceBefore.getOptimum().getLength().getValue(),
+                        spaceBefore.getOptimum().getLength().getValue()
+                                - spaceBefore.getMinimum().getLength().getValue(),
+                        BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT, 
+                        returnPosition, true));
+            } /*else {
+                returnList.add(new KnuthGlue(
+                        spaceBefore.getOptimum().getLength().getValue(), 
+                        0, 0, BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT,
+                        returnPosition, true));
+            }*/
+        }
+    }
+
+    /**
+     * Creates Knuth elements for space-after and adds them to the return list.
+     * @param returnList return list to add the additional elements to
+     * @param returnPosition applicable return position
+     * @param alignment vertical alignment
+     */
+    protected void addKnuthElementsForSpaceAfter(LinkedList returnList, Position returnPosition, 
+                int alignment) {
+        SpaceProperty spaceAfter = null;
+        if (fobj instanceof org.apache.fop.fo.flow.Block) {
+            spaceAfter = ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().spaceAfter;
+        } else if (fobj instanceof org.apache.fop.fo.flow.Block) {
+            spaceAfter = ((org.apache.fop.fo.flow.BlockContainer) fobj).getCommonMarginBlock().spaceAfter;
+        }
+        // append elements representing space-after
+        if (bpUnit > 0
+                || spaceAfter != null
+                   && !(spaceAfter.getMinimum().getLength().getValue() == 0 
+                        && spaceAfter.getMaximum().getLength().getValue() == 0)) {
+            if (spaceAfter != null && !spaceAfter.getSpace().isDiscard()) {
+                returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE,
+                        false, returnPosition, false));
+            }
+            if (bpUnit > 0) {
+                returnList.add(new KnuthGlue(0, 0, 0, 
+                        BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT,
+                        returnPosition, true));
+            } else /*if (alignment == EN_JUSTIFY)*/ {
+                returnList.add(new KnuthGlue(
+                        spaceAfter.getOptimum().getLength().getValue(),
+                        spaceAfter.getMaximum().getLength().getValue()
+                                - spaceAfter.getOptimum().getLength().getValue(),
+                        spaceAfter.getOptimum().getLength().getValue()
+                                - spaceAfter.getMinimum().getLength().getValue(),
+                        BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT, returnPosition,
+                        (!spaceAfter.getSpace().isDiscard()) ? false : true));
+            } /*else {
+                returnList.add(new KnuthGlue(
+                        spaceAfter.getOptimum().getLength().getValue(), 0, 0,
+                        BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT, returnPosition,
+                        (!spaceAfter.getSpace().isDiscard()) ? false : true));
+            }*/
+            if (spaceAfter != null && !spaceAfter.getSpace().isDiscard()) {
+                returnList.add(new KnuthBox(0, returnPosition, true));
+            }
+        }
+    }
+
+    protected LinkedList createUnitElements(LinkedList oldList) {
+        //System.out.println(" ");
+        //System.out.println("Inizio conversione: " + oldList.size() + " elementi, spazio minimo prima= " + layoutProps.spaceBefore.getSpace().min
+        //                   + " spazio minimo dopo= " + layoutProps.spaceAfter.getSpace().min);
+        // add elements at the beginning and at the end of oldList
+        // representing minimum spaces
+        LayoutManager lm = ((KnuthElement)oldList.getFirst()).getLayoutManager();
+        boolean bAddedBoxBefore = false;
+        boolean bAddedBoxAfter = false;
+        if (adjustedSpaceBefore > 0) {
+            oldList.addFirst(new KnuthBox(adjustedSpaceBefore,
+                                          new Position(lm), true));
+            bAddedBoxBefore = true;
+        }
+        if (adjustedSpaceAfter > 0) {
+            oldList.addLast(new KnuthBox(adjustedSpaceAfter,
+                                         new Position(lm), true));
+            bAddedBoxAfter = true;
+        }
+
+        MinOptMax totalLength = new MinOptMax(0);
+        MinOptMax totalUnits = new MinOptMax(0);
+        LinkedList newList = new LinkedList();
+
+        //System.out.println(" ");
+        //System.out.println(" Prima scansione");
+        // scan the list once to compute total min, opt and max length
+        ListIterator oldListIterator = oldList.listIterator();
+        while (oldListIterator.hasNext()) {
+            KnuthElement element = (KnuthElement) oldListIterator.next();
+            if (element.isBox()) {
+/*LF*/          totalLength.add(new MinOptMax(element.getW()));
+/*LF*/          //System.out.println("box " + element.getW());               
+/*LF*/      } else if (element.isGlue()) {
+/*LF*/          totalLength.min -= ((KnuthGlue) element).getZ();
+/*LF*/          totalLength.max += ((KnuthGlue) element).getY();
+/*LF*/          //leafValue = ((LeafPosition) element.getPosition()).getLeafPos();
+/*LF*/          //System.out.println("glue " + element.getW() + " + " + ((KnuthGlue) element).getY() + " - " + ((KnuthGlue) element).getZ());
+/*LF*/      } else {
+/*LF*/          //System.out.println((((KnuthPenalty)element).getP() == KnuthElement.INFINITE ? "PENALTY " : "penalty ") + element.getW());
+            }
+        }
+        // compute the total amount of "units"
+        totalUnits = new MinOptMax(neededUnits(totalLength.min),
+                                   neededUnits(totalLength.opt),
+                                   neededUnits(totalLength.max));
+        //System.out.println(" totalLength= " + totalLength);
+        //System.out.println(" unita'= " + totalUnits);
+
+        //System.out.println(" ");
+        //System.out.println(" Seconda scansione");
+        // scan the list once more, stopping at every breaking point
+        // in order to compute partial min, opt and max length
+        // and create the new elements
+        oldListIterator = oldList.listIterator();
+        boolean bPrevIsBox = false;
+        MinOptMax lengthBeforeBreak = new MinOptMax(0);
+        MinOptMax lengthAfterBreak = (MinOptMax) totalLength.clone();
+        MinOptMax unitsBeforeBreak;
+        MinOptMax unitsAfterBreak;
+        MinOptMax unsuppressibleUnits = new MinOptMax(0);
+        int firstIndex = 0;
+        int lastIndex = -1;
+        while (oldListIterator.hasNext()) {
+            KnuthElement element = (KnuthElement) oldListIterator.next();
+            lastIndex ++;
+            if (element.isBox()) {
+                lengthBeforeBreak.add(new MinOptMax(element.getW()));
+                lengthAfterBreak.subtract(new MinOptMax(element.getW()));
+                bPrevIsBox = true;
+            } else if (element.isGlue()) {
+                lengthBeforeBreak.min -= ((KnuthGlue) element).getZ();
+                lengthAfterBreak.min += ((KnuthGlue) element).getZ();
+                lengthBeforeBreak.max += ((KnuthGlue) element).getY();
+                lengthAfterBreak.max -= ((KnuthGlue) element).getY();
+                bPrevIsBox = false;
+            } else {
+                lengthBeforeBreak.add(new MinOptMax(element.getW()));
+                bPrevIsBox = false;
+            }
+
+            // create the new elements
+            if (element.isPenalty() && ((KnuthPenalty) element).getP() < KnuthElement.INFINITE
+                || element.isGlue() && bPrevIsBox
+                || !oldListIterator.hasNext()) {
+                // suppress elements after the breaking point
+                int iStepsForward = 0;
+                while (oldListIterator.hasNext()) {
+                    KnuthElement el = (KnuthElement) oldListIterator.next();
+                    iStepsForward++;
+                    if (el.isGlue()) {
+                        // suppressed glue
+                        lengthAfterBreak.min += ((KnuthGlue) el).getZ();
+                        lengthAfterBreak.max -= ((KnuthGlue) el).getY();
+                    } else if (el.isPenalty()) {
+                        // suppressed penalty, do nothing
+                    } else {
+                        // box, end of suppressions
+                        break;
+                    }
+                }
+                // compute the partial amount of "units" before and after the break
+                unitsBeforeBreak = new MinOptMax(neededUnits(lengthBeforeBreak.min),
+                                                 neededUnits(lengthBeforeBreak.opt),
+                                                 neededUnits(lengthBeforeBreak.max));
+                unitsAfterBreak = new MinOptMax(neededUnits(lengthAfterBreak.min),
+                                                neededUnits(lengthAfterBreak.opt),
+                                                neededUnits(lengthAfterBreak.max));
+
+                // rewind the iterator and lengthAfterBreak
+                for (int i = 0; i < iStepsForward; i++) {
+                    KnuthElement el = (KnuthElement) oldListIterator.previous();
+                    if (el.isGlue()) {
+                        lengthAfterBreak.min -= ((KnuthGlue) el).getZ();
+                        lengthAfterBreak.max += ((KnuthGlue) el).getY();
+                    }
+                }
+
+                // compute changes in length, stretch and shrink
+                int uLengthChange = unitsBeforeBreak.opt + unitsAfterBreak.opt - totalUnits.opt;
+                int uStretchChange = (unitsBeforeBreak.max + unitsAfterBreak.max - totalUnits.max)
+                                     - (unitsBeforeBreak.opt + unitsAfterBreak.opt - totalUnits.opt);
+                int uShrinkChange = (unitsBeforeBreak.opt + unitsAfterBreak.opt - totalUnits.opt)
+                                    - (unitsBeforeBreak.min + unitsAfterBreak.min - totalUnits.min);
+
+                // compute the number of normal, stretch and shrink unit
+                // that must be added to the new sequence
+                int uNewNormal = unitsBeforeBreak.opt - unsuppressibleUnits.opt;
+                int uNewStretch = (unitsBeforeBreak.max - unitsBeforeBreak.opt)
+                                  - (unsuppressibleUnits.max - unsuppressibleUnits.opt);
+                int uNewShrink = (unitsBeforeBreak.opt - unitsBeforeBreak.min)
+                                 - (unsuppressibleUnits.opt - unsuppressibleUnits.min);
+
+/*LF*/          //System.out.println("(" + unsuppressibleUnits.min + "-" + unsuppressibleUnits.opt + "-" +  unsuppressibleUnits.max + ") "
+/*LF*/          //                   + " -> " + unitsBeforeBreak.min + "-" + unitsBeforeBreak.opt + "-" +  unitsBeforeBreak.max
+/*LF*/          //                   + " + " + unitsAfterBreak.min + "-" + unitsAfterBreak.opt + "-" +  unitsAfterBreak.max
+/*LF*/          //                   + (uLengthChange != 0 ? " [length " + uLengthChange + "] " : "")
+/*LF*/          //                   + (uStretchChange != 0 ? " [stretch " + uStretchChange + "] " : "")
+/*LF*/          //                   + (uShrinkChange != 0 ? " [shrink " + uShrinkChange + "]" : "")
+/*LF*/          //                   );
+
+                // create the MappingPosition which will be stored in the new elements
+                // correct firstIndex and lastIndex
+                int firstIndexCorrection = 0;
+                int lastIndexCorrection = 0;
+                if (bAddedBoxBefore) {
+                    if (firstIndex != 0) {
+                        firstIndexCorrection ++;
+                    }
+                    lastIndexCorrection ++;
+                }
+                if (bAddedBoxAfter && lastIndex == (oldList.size() - 1)) {
+                    lastIndexCorrection ++;
+                }
+                MappingPosition mappingPos = new MappingPosition(this,
+                                                                 firstIndex - firstIndexCorrection,
+                                                                 lastIndex - lastIndexCorrection);
+
+                // new box
+                newList.add(new KnuthBox((uNewNormal - uLengthChange) * bpUnit,
+                                         mappingPos,
+                                         false));
+                unsuppressibleUnits.add(new MinOptMax(uNewNormal - uLengthChange));
+                //System.out.println("        box " + (uNewNormal - uLengthChange));
+
+                // new infinite penalty, glue and box, if necessary
+                if (uNewStretch - uStretchChange > 0
+                    || uNewShrink - uShrinkChange > 0) {
+                    int iStretchUnits = (uNewStretch - uStretchChange > 0 ? (uNewStretch - uStretchChange) : 0);
+                    int iShrinkUnits = (uNewShrink - uShrinkChange > 0 ? (uNewShrink - uShrinkChange) : 0);
+                    newList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
+                                                 mappingPos,
+                                                 false));
+                    newList.add(new KnuthGlue(0,
+                                              iStretchUnits * bpUnit,
+                                              iShrinkUnits * bpUnit,
+                                              LINE_NUMBER_ADJUSTMENT,
+                                              mappingPos,
+                                              false));
+                    //System.out.println("        PENALTY");
+                    //System.out.println("        glue 0 " + iStretchUnits + " " + iShrinkUnits);
+                    unsuppressibleUnits.max += iStretchUnits;
+                    unsuppressibleUnits.min -= iShrinkUnits;
+                    if (!oldListIterator.hasNext()) {
+                        newList.add(new KnuthBox(0,
+                                                 mappingPos,
+                                                 false));
+                        //System.out.println("        box 0");
+                    }
+                }
+
+                // new breaking sequence
+                if (uStretchChange != 0
+                    || uShrinkChange != 0) {
+                    // new infinite penalty, glue, penalty and glue
+                    newList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
+                                                 mappingPos,
+                                                 false));
+                    newList.add(new KnuthGlue(0,
+                                              uStretchChange * bpUnit,
+                                              uShrinkChange * bpUnit,
+                                              LINE_NUMBER_ADJUSTMENT,
+                                              mappingPos,
+                                              false));
+                    newList.add(new KnuthPenalty(uLengthChange * bpUnit,
+                                                 0, false, element.getPosition(), false));
+                    newList.add(new KnuthGlue(0,
+                                              - uStretchChange * bpUnit,
+                                              - uShrinkChange * bpUnit,
+                                              LINE_NUMBER_ADJUSTMENT,
+                                              mappingPos,
+                                              false));
+                    //System.out.println("        PENALTY");
+                    //System.out.println("        glue 0 " + uStretchChange + " " + uShrinkChange);
+                    //System.out.println("        penalty " + uLengthChange + " * unit");
+                    //System.out.println("        glue 0 " + (- uStretchChange) + " " + (- uShrinkChange));
+                } else if (oldListIterator.hasNext()){
+                    // new penalty
+                    newList.add(new KnuthPenalty(uLengthChange * bpUnit,
+                                                 0, false,
+                                                 mappingPos,
+                                                 false));
+                    //System.out.println("        penalty " + uLengthChange + " * unit");
+                }
+                // update firstIndex
+                firstIndex = lastIndex + 1;
+            }
+
+            if (element.isPenalty()) {
+                lengthBeforeBreak.add(new MinOptMax(-element.getW()));
+            }
+
+        }
+
+        // remove elements at the beginning and at the end of oldList
+        // representing minimum spaces
+        if (adjustedSpaceBefore > 0) {
+            oldList.removeFirst();
+        }
+        if (adjustedSpaceAfter > 0) {
+            oldList.removeLast();
+        }
+
+        // if space-before.conditionality is "discard", correct newList
+        boolean correctFirstElement = false;
+        if (fobj instanceof org.apache.fop.fo.flow.Block) {
+            correctFirstElement =
+                ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().spaceBefore.getSpace().isDiscard();
+        }
+        if (correctFirstElement) {
+            // remove the wrong element
+            KnuthBox wrongBox = (KnuthBox) newList.removeFirst();
+            // if this paragraph is at the top of a page, the space before
+            // must be ignored; compute the length change
+            int decreasedLength = (neededUnits(totalLength.opt)
+                                   - neededUnits(totalLength.opt - adjustedSpaceBefore))
+                                  * bpUnit;
+            // insert the correct elements
+            newList.addFirst(new KnuthBox(wrongBox.getW() - decreasedLength,
+                                          wrongBox.getPosition(), false));
+            newList.addFirst(new KnuthGlue(decreasedLength, 0, 0, SPACE_BEFORE_ADJUSTMENT,
+                                           wrongBox.getPosition(), false));
+            //System.out.println("        rimosso box " + neededUnits(wrongBox.getW()));
+            //System.out.println("        aggiunto glue " + neededUnits(decreasedLength) + " 0 0");
+            //System.out.println("        aggiunto box " + neededUnits(wrongBox.getW() - decreasedLength));
+        }
+
+        // if space-after.conditionality is "discard", correct newList
+        boolean correctLastElement = false;
+        if (fobj instanceof org.apache.fop.fo.flow.Block) {
+            correctLastElement =
+                ((org.apache.fop.fo.flow.Block) fobj).getCommonMarginBlock().spaceAfter.getSpace().isDiscard();
+        }
+        if (correctLastElement) {
+            // remove the wrong element
+            KnuthBox wrongBox = (KnuthBox) newList.removeLast();
+            // if the old sequence is box(h) penalty(inf) glue(x,y,z) box(0)
+            // (it cannot be parted and has some stretch or shrink)
+            // the wrong box is the first one, not the last one
+            LinkedList preserveList = new LinkedList();
+            if (wrongBox.getW() == 0) {
+                preserveList.add(wrongBox);
+                preserveList.addFirst((KnuthGlue) newList.removeLast());
+                preserveList.addFirst((KnuthPenalty) newList.removeLast());
+                wrongBox = (KnuthBox) newList.removeLast();
+            }
+
+            // if this paragraph is at the bottom of a page, the space after
+            // must be ignored; compute the length change
+            int decreasedLength = (neededUnits(totalLength.opt)
+                                   - neededUnits(totalLength.opt - adjustedSpaceAfter))
+                                  * bpUnit;
+            // insert the correct box
+            newList.addLast(new KnuthBox(wrongBox.getW() - decreasedLength,
+                                         wrongBox.getPosition(), false));
+            // add preserved elements
+            if (preserveList.size() > 0) {
+                newList.addAll(preserveList);
+            }
+            // insert the correct glue
+            newList.addLast(new KnuthGlue(decreasedLength, 0, 0, SPACE_AFTER_ADJUSTMENT,
+                                          wrongBox.getPosition(), false));
+            //System.out.println("        rimosso box " + neededUnits(wrongBox.getW()));
+            //System.out.println("        aggiunto box " + neededUnits(wrongBox.getW() - decreasedLength));
+            //System.out.println("        aggiunto glue " + neededUnits(decreasedLength) + " 0 0");
+        }
+
+        return newList;
+    }
+
+    protected static class StackingIter extends PositionIterator {
+        StackingIter(Iterator parentIter) {
+            super(parentIter);
+        }
+    
+        protected LayoutManager getLM(Object nextObj) {
+            return ((Position) nextObj).getLM();
+        }
+    
+        protected Position getPos(Object nextObj) {
+            return ((Position) nextObj);
+        }
+    }
+
+    protected static class MappingPosition extends Position {
+        private int iFirstIndex;
+        private int iLastIndex;
+    
+        public MappingPosition(LayoutManager lm, int first, int last) {
+            super(lm);
+            iFirstIndex = first;
+            iLastIndex = last;
+        }
+        
+        public int getFirstIndex() {
+            return iFirstIndex;
+        }
+        
+        public int getLastIndex() {
+            return iLastIndex;
+        }
+    }
+
+    /**
+     * "wrap" the Position inside each element moving the elements from 
+     * SourceList to targetList
+     * @param sourceList source list
+     * @param targetList target list receiving the wrapped position elements
+     */
+    protected void wrapPositionElements(List sourceList, List targetList) {
+        ListIterator listIter = sourceList.listIterator();
+        while (listIter.hasNext()) {
+            KnuthElement tempElement;
+            tempElement = (KnuthElement) listIter.next();
+            if (tempElement.getLayoutManager() != this) {
+                tempElement.setPosition(new NonLeafPosition(this,
+                        tempElement.getPosition()));
+            }
+            targetList.add(tempElement);
+        }
+    }
+
 }
 
diff --git a/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java
new file mode 100644 (file)
index 0000000..c0d87a7
--- /dev/null
@@ -0,0 +1,783 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import java.util.ArrayList;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.traits.MinOptMax;
+
+/**
+ * The set of nodes is sorted into lines indexed into activeLines.
+ * The nodes in each line are linked together in a single linked list by the 
+ * KnuthNode.next field. The activeLines array contains a link to the head of
+ * the linked list in index 'line*2' and a link to the tail at index 'line*2+1'.
+ * <p>
+ * The set of active nodes can be traversed by 
+ * <pre>
+ * for (int line = startLine; line < endLine; line++) {
+ *     for (KnuthNode node = getNode(line); node != null; node = node.next) {
+ *         // Do something with 'node'
+ *     }
+ * }
+ * </pre> 
+ */
+public abstract class BreakingAlgorithm {
+    // parameters of Knuth's algorithm:
+    // penalty value for flagged penalties
+    private int flaggedPenalty = 50;
+    // demerit for consecutive lines ending at flagged penalties
+    private int repeatedFlaggedDemerit = 50;
+    // demerit for consecutive lines belonging to incompatible fitness classes 
+    private int incompatibleFitnessDemerit = 50;
+    // suggested modification to the "optimum" number of lines
+    private int looseness = 0;
+
+    /**
+     * The threshold for considering breaks to be acceptable.
+     */
+    private double threshold;
+
+    /**
+     * The paragraph of KnuthElements.
+     */
+    private KnuthSequence par;
+    
+    /**
+     * The width of a line.
+     */
+    private int lineWidth = 0;
+    private boolean force =  false;
+
+    protected KnuthNode lastDeactivatedNode = null;
+    private KnuthNode lastTooLong;
+    private KnuthNode lastTooShort;
+    private KnuthNode lastDeactivated;
+
+    protected int alignment;
+    protected int alignmentLast;
+    protected boolean bFirst;
+
+    /**
+     * The set of active nodes.
+     */
+    private KnuthNode[] activeLines;
+    
+    /**
+     * The number of active nodes.
+     */
+    protected int activeNodeCount;
+    
+    /**
+     * The lowest available line in the set of active nodes.
+     */
+    protected int startLine = 0;
+
+    /**
+     * The highest + 1 available line in the set of active nodes.
+     */
+    protected int endLine = 0;
+
+    /**
+     * The total width of all elements handled so far.
+     */
+    private int totalWidth;
+
+    /**
+     * The total stretch of all elements handled so far.
+     */
+    private int totalStretch = 0;
+
+    /**
+     * The total shrink of all elements handled so far.
+     */
+    private int totalShrink = 0;
+
+    private BestRecords best;
+    private KnuthNode[] positions;
+
+    private static final int INFINITE_RATIO = 1000;
+
+    protected static Log log = LogFactory.getLog(KnuthParagraph.class);
+
+    public BreakingAlgorithm(int align, int alignLast,
+                             boolean first) {
+        alignment = align;
+        alignmentLast = alignLast;
+        bFirst = first;
+        this.best = new BestRecords();
+    }
+
+
+    // this class represent a feasible breaking point
+    public class KnuthNode {
+        // index of the breakpoint represented by this node
+        public int position;
+
+        // number of the line ending at this breakpoint
+        public int line;
+
+        // fitness class of the line ending at his breakpoint
+        public int fitness;
+
+        // accumulated width of the KnuthElements
+        public int totalWidth;
+
+        // accumulated stretchability of the KnuthElements
+        public int totalStretch;
+
+        // accumulated shrinkability of the KnuthElements
+        public int totalShrink;
+
+        // adjustment ratio if the line ends at this breakpoint
+        public double adjustRatio;
+
+        // available stretch of the line ending at this breakpoint
+        public int availableShrink;
+
+        // available shrink of the line ending at this breakpoint
+        public int availableStretch;
+
+        // difference between target and actual line width
+        public int difference;
+
+        // minimum total demerits up to this breakpoint
+        public double totalDemerits;
+
+        // best node for the preceding breakpoint
+        public KnuthNode previous;
+
+        // next possible node in the same line 
+        public KnuthNode next;
+
+
+        public KnuthNode(int position, int line, int fitness,
+                         int totalWidth, int totalStretch, int totalShrink,
+                         double adjustRatio, int availableShrink, int availableStretch, int difference,
+                         double totalDemerits, KnuthNode previous) {
+            this.position = position;
+            this.line = line;
+            this.fitness = fitness;
+            this.totalWidth = totalWidth;
+            this.totalStretch = totalStretch;
+            this.totalShrink = totalShrink;
+            this.adjustRatio = adjustRatio;
+            this.availableShrink = availableShrink;
+            this.availableStretch = availableStretch;
+            this.difference = difference;
+            this.totalDemerits = totalDemerits;
+            this.previous = previous;
+        }
+
+        public String toString() {
+            return "<KnuthNode at " + position + " " +
+            totalWidth + "+" + totalStretch + "-" + totalShrink +
+            " line:" + line +
+            " prev:" + (previous != null ? previous.position : -1) +
+            " dem:" + totalDemerits +
+            ">"; 
+        }
+    }
+
+    // this class stores information about how the nodes
+    // which could start a line
+    // ending at the current element
+    private class BestRecords {
+        private static final double INFINITE_DEMERITS = Double.POSITIVE_INFINITY;
+        //private static final double INFINITE_DEMERITS = 1E11;
+
+        private double bestDemerits[] = new double[4];
+        private KnuthNode bestNode[] = new KnuthNode[4];
+        private double bestAdjust[] = new double[4];
+        private int bestDifference[] = new int[4];
+        private int bestAvailableShrink[] = new int[4];
+        private int bestAvailableStretch[] = new int[4];
+        private int bestIndex = -1;
+
+        public BestRecords() {
+            reset();
+        }
+
+        public void addRecord(double demerits, KnuthNode node, double adjust,
+                              int availableShrink, int availableStretch, int difference, int fitness) {
+            if (demerits > bestDemerits[fitness]) {
+                log.error("New demerits value greter than the old one");
+            }
+            bestDemerits[fitness] = demerits;
+            bestNode[fitness] = node;
+            bestAdjust[fitness] = adjust;
+            bestAvailableShrink[fitness] = availableShrink;
+            bestAvailableStretch[fitness] = availableStretch;
+            bestDifference[fitness] = difference;
+            if (bestIndex == -1 || demerits < bestDemerits[bestIndex]) {
+                bestIndex = fitness;
+            }
+        }
+
+        public boolean hasRecords() {
+            return (bestIndex != -1);
+        }
+
+        public boolean notInfiniteDemerits(int fitness) {
+            return (bestDemerits[fitness] != INFINITE_DEMERITS);
+        }
+
+        public double getDemerits(int fitness) {
+            return bestDemerits[fitness];
+        }
+
+        public KnuthNode getNode(int fitness) {
+            return bestNode[fitness];
+        }
+
+        public double getAdjust(int fitness) {
+            return bestAdjust[fitness];
+        }
+
+        public int getAvailableShrink(int fitness) {
+            return bestAvailableShrink[fitness];
+        }
+
+        public int getAvailableStretch(int fitness) {
+            return bestAvailableStretch[fitness];
+        }
+
+       public int getDifference(int fitness) {
+            return bestDifference[fitness];
+        }
+
+        public double getMinDemerits() {
+            if (bestIndex != -1) {
+                return getDemerits(bestIndex);
+            } else {
+                // anyway, this should never happen
+                return INFINITE_DEMERITS;
+            }
+        }
+
+        public void reset() {
+            for (int i = 0; i < 4; i ++) {
+                bestDemerits[i] = INFINITE_DEMERITS;
+                bestNode[i] = null;
+                bestAdjust[i] = 0.0;
+                bestDifference[i] = 0;
+                bestAvailableShrink[i] = 0;
+                bestAvailableStretch[i] = 0;
+            }
+            bestIndex = -1;
+        }
+    }
+
+
+
+    public abstract void updateData1(int total, double demerits) ;
+
+    public abstract void updateData2(KnuthNode bestActiveNode,
+                                     KnuthSequence sequence,
+                                     int total) ;
+
+    public int findBreakingPoints(KnuthSequence par, int lineWidth,
+                                  double threshold, boolean force,
+                                  boolean hyphenationAllowed) {
+        this.par = par;
+        this.threshold = threshold;
+        this.force = force;
+        this.lineWidth = lineWidth;
+        this.totalWidth = 0;
+        this.totalStretch = 0;
+        this.totalShrink = 0;
+
+        activeLines = new KnuthNode[20];
+
+        // reset lastTooShort and lastTooLong, as they could be not null
+        // because of previous calls to findBreakingPoints
+        lastTooShort = lastTooLong = null; 
+        // reset startLine and endLine
+        startLine = endLine = 0;
+        // current element in the paragraph
+        KnuthElement thisElement = null;
+        // previous element in the paragraph is a KnuthBox?
+        boolean previousIsBox = false;
+
+        // index of the first KnuthBox in the sequence
+        int firstBoxIndex = 0;
+        while (alignment != org.apache.fop.fo.Constants.EN_CENTER
+               && ! ((KnuthElement) par.get(firstBoxIndex)).isBox()) {
+            firstBoxIndex++;
+        }
+
+        // create an active node representing the starting point
+        activeLines = new KnuthNode[20];
+        addNode(0, new KnuthNode(firstBoxIndex, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, null));
+
+        if (log.isTraceEnabled()) {
+            log.trace("Looping over " + par.size() + " box objects");
+        }
+        
+        KnuthNode lastForced = getNode(0);
+
+        // main loop
+        for (int i = 0; i < par.size(); i++) {
+            thisElement = getElement(i);
+            if (thisElement.isBox()) {
+                // a KnuthBox object is not a legal line break
+                totalWidth += thisElement.getW();
+                previousIsBox = true;
+            } else if (thisElement.isGlue()) {
+                // a KnuthGlue object is a legal line break
+                // only if the previous object is a KnuthBox
+                if (previousIsBox) {
+                    considerLegalBreak(thisElement, i);
+                }
+                totalWidth += thisElement.getW();
+                totalStretch += thisElement.getY();
+                totalShrink += thisElement.getZ();
+                previousIsBox = false;
+            } else {
+                // a KnuthPenalty is a legal line break
+                // only if its penalty is not infinite;
+                // if hyphenationAllowed is false, ignore flagged penalties
+                if (((KnuthPenalty) thisElement).getP()
+                    < KnuthElement.INFINITE
+                    && (hyphenationAllowed || !((KnuthPenalty) thisElement).isFlagged())) {
+                    considerLegalBreak(thisElement, i);
+                }
+                previousIsBox = false;
+            }
+            if (activeNodeCount == 0) {
+                if (!force) {
+                    log.debug("Could not find a set of breaking points " + threshold);
+                    return 0;
+                }
+                if (lastTooShort == null || lastForced.position == lastTooShort.position) {
+                    lastForced = lastTooLong;
+                } else {
+                    lastForced = lastTooShort;
+                }
+
+                log.debug("Restarting at node " + lastForced);
+                lastForced.totalDemerits = 0;
+                addNode(lastForced.line, lastForced);
+                i = lastForced.position;
+                startLine = lastForced.line;
+                endLine = startLine + 1;
+                totalWidth = lastForced.totalWidth;
+                totalStretch = lastForced.totalStretch;
+                totalShrink = lastForced.totalShrink;
+                lastTooShort = lastTooLong = null;
+            }
+        }
+        if (log.isTraceEnabled()) {
+            log.trace("Main loop completed " + activeNodeCount);
+            log.trace("Active nodes=" + toString(""));
+        }
+
+        // there is at least one set of breaking points
+        // select one or more active nodes, removing the others from the list
+        int line = filterActiveNodes();
+
+        // for each active node, create a set of breaking points
+        for (int i = startLine; i < endLine; i++) {
+            for (KnuthNode node = getNode(line); node != null; node = node.next) {
+                updateData1(node.line, node.totalDemerits);
+                calculateBreakPoints(node, par, node.line);
+            }
+        }
+
+        activeLines = null;
+        return line;
+    }
+
+    private void considerLegalBreak(KnuthElement element, int elementIdx) {
+
+        if (log.isTraceEnabled()) {
+            log.trace("Feasible breakpoint at " + par.indexOf(element) + " " + totalWidth + "+" + totalStretch + "-" + totalShrink);
+            log.trace("\tCurrent active node list: " + activeNodeCount + " " + this.toString("\t"));
+        }
+
+        lastDeactivated = null;
+        lastTooLong = null;
+        for (int line = startLine; line < endLine; line++) {
+            for (KnuthNode node = getNode(line); node != null; node = node.next) {
+                if (node.position == elementIdx) {
+                    continue;
+                }
+                int difference = computeDifference(node, element);
+                double r = computeAdjustmentRatio(node, difference);
+                int availableShrink = totalShrink - node.totalShrink;
+                int availableStretch = totalStretch - node.totalStretch;
+                if (log.isTraceEnabled()) {
+                    log.trace("\tr=" + r);
+                    log.trace("\tline=" + line);
+                }
+
+                // The line would be too long.
+                if (r < -1 || element.isForcedBreak()) {
+                    // Deactivate node.
+                    if (log.isTraceEnabled()) {
+                        log.trace("Removing " + node);
+                    }
+                    removeNode(line, node);
+                    lastDeactivated = compareNodes(lastDeactivated, node);
+                }
+    
+                // The line is within the available shrink and the threshold.
+                if (r >= -1 && r <= threshold) {
+                    int fitnessClass = computeFitness(r);
+                    double demerits = computeDemerits(node, element, fitnessClass, r);
+    
+                    if (log.isTraceEnabled()) {
+                        log.trace("\tDemerits=" + demerits);
+                        log.trace("\tFitness class=" + fitnessClass);
+                    }
+    
+                    if (demerits < best.getDemerits(fitnessClass)) {
+                        // updates best demerits data
+                        best.addRecord(demerits, node, r, availableShrink, availableStretch,
+                                       difference, fitnessClass);
+                        lastTooShort = null;
+                    }
+                }
+                
+                // The line is way too short, but we are in forcing mode, so a node is
+                // calculated and stored in lastValidNode.
+                if (force && (r <= -1 || r > threshold)) {
+                    int fitnessClass = computeFitness(r);
+                    double demerits = computeDemerits(node, element, fitnessClass, r);
+                    if (r <= -1) {
+                        if (lastTooLong == null || demerits < lastTooLong.totalDemerits) {
+                            lastTooLong = new KnuthNode(elementIdx, line + 1, fitnessClass,
+                                    totalWidth, totalStretch, totalShrink,
+                                    r, availableShrink, availableStretch,
+                                    difference, demerits, node);
+                            if (log.isTraceEnabled()) {
+                                log.trace("Picking tooLong " + lastTooLong);
+                            }
+                        }
+                    } else {
+                        if (lastTooShort == null || demerits <= lastTooShort.totalDemerits) {
+                            lastTooShort = new KnuthNode(elementIdx, line + 1, fitnessClass,
+                                    totalWidth, totalStretch, totalShrink,
+                                    r, availableShrink, availableStretch,
+                                    difference, demerits, node);
+                            if (log.isTraceEnabled()) {
+                                log.trace("Picking tooShort " + lastTooShort);
+                            }
+                        }
+                    }
+                }
+            }
+            addBreaks(line, elementIdx);
+        }
+    }
+
+    private void addBreaks(int line, int elementIdx) {
+        if (!best.hasRecords()) {
+            return;
+        }
+
+        int newWidth = totalWidth;
+        int newStretch = totalStretch;
+        int newShrink = totalShrink;
+
+        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;
+        for (int i = 0; i <= 3; i++) {
+            if (best.notInfiniteDemerits(i) && best.getDemerits(i) <= minimumDemerits) {
+                // the nodes in activeList must be ordered
+                // by line number and position;
+                if (log.isTraceEnabled()) {
+                    log.trace("\tInsert new break in list of " + activeNodeCount);
+                }
+                KnuthNode newNode = new KnuthNode(elementIdx, line + 1, i,
+                                   newWidth, newStretch, newShrink,
+                                   best.getAdjust(i),
+                                   best.getAvailableShrink(i),
+                                   best.getAvailableStretch(i),
+                                   best.getDifference(i),
+                                   best.getDemerits(i),
+                                   best.getNode(i));
+                addNode(line + 1, newNode);
+            }
+        }
+        best.reset();
+    }
+
+    /**
+     * Return the difference between the line width and the width of the break that
+     * ends in 'element'.
+     * @param activeNode
+     * @param element
+     * @return The difference in width. Positive numbers mean extra space in the line,
+     * negative number that the line overflows. 
+     */
+    private int computeDifference(KnuthNode activeNode, KnuthElement element) {
+        // compute the adjustment ratio
+        int actualWidth = totalWidth - activeNode.totalWidth;
+        if (element.isPenalty()) {
+            actualWidth += element.getW();
+        }
+        return lineWidth - actualWidth;
+    }
+
+    /**
+     * Return the adjust ration needed to make up for the difference. A ration of 
+     * <ul>
+     *    <li>0 means that the break has the exact right width</li>
+     *    <li>&gt;= -1 && &lt; 0  means that the break is to wider than the line, 
+     *        but within the minimim values of the glues.</li> 
+     *    <li>&gt;0 && &lt 1 means that the break is smaller than the line width, 
+     *        but within the maximum values of the glues.</li>
+     *    <li>&gt 1 means that the break is too small to make up for the glues.</li> 
+     * </ul>
+     * @param activeNode
+     * @param difference
+     * @return The ration.
+     */
+    private double computeAdjustmentRatio(KnuthNode activeNode, int difference) {
+        // compute the adjustment ratio
+        if (difference > 0) {
+            int maxAdjustment = totalStretch - activeNode.totalStretch;
+            if (maxAdjustment > 0) {
+                return (double) difference / maxAdjustment;
+            } else {
+                return INFINITE_RATIO;
+            }
+        } else if (difference < 0) {
+            int maxAdjustment = totalShrink - activeNode.totalShrink;
+            if (maxAdjustment > 0) {
+                return (double) difference / maxAdjustment;
+            } else {
+                return -INFINITE_RATIO;
+            }
+        } else {
+            return 0;
+        }
+    }
+    
+    /**
+     * Figure out the fitness class of this line (tight, loose,
+     * very tight or very loose).
+     * @param r
+     * @return
+     */
+    private int computeFitness(double r) {
+        int newFitnessClass;
+        if (r < -0.5) {
+            return 0;
+        } else if (r <= 0.5) {
+            return 1;
+        } else if (r <= 1) {
+            return 2;
+        } else {
+            return 3;
+        }
+    }
+
+    private double computeDemerits(KnuthNode activeNode, KnuthElement element, 
+                                  int fitnessClass, double r) {
+        double demerits = 0;
+        // compute demerits
+        double f = Math.abs(r);
+        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;
+        }
+    
+        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;
+        }
+        demerits += activeNode.totalDemerits;
+        return demerits;
+    }
+
+    /**
+     * Return the element at index idx in the paragraph.
+     * @param idx index of the element.
+     * @return
+     */
+    private KnuthElement getElement(int idx) {
+        return (KnuthElement) par.get(idx);
+    }
+
+    /**
+     * Compare two KnuthNodes and return the node with the least demerit. 
+     * @param node1 The first knuth node.
+     * @param node2 The other knuth node.
+     * @return
+     */
+    protected KnuthNode compareNodes(KnuthNode node1, KnuthNode node2) {
+        if (node1 == null || node2.position > node1.position) {
+            return node2;
+        }
+        if (node2.position == node1.position) {
+            if (node2.totalDemerits < node1.totalDemerits) {
+                return node2;
+            }
+        }
+        return node1;
+    }
+
+    /**
+     * Add a KnuthNode at the end of line 'line'. 
+     * If this is the first node in the line, adjust endLine accordingly.
+     * @param line
+     * @param node
+     */
+    private void addNode(int line, KnuthNode node) {
+        int headIdx = line * 2;
+        if (headIdx >= activeLines.length) {
+            KnuthNode[] oldList = activeLines;
+            activeLines = new KnuthNode[headIdx + headIdx];
+            System.arraycopy(oldList, 0, activeLines, 0, oldList.length);
+        }
+        node.next = null;
+        if (activeLines[headIdx + 1] != null) {
+            activeLines[headIdx + 1].next = node;
+        } else {
+            activeLines[headIdx] = node;
+            endLine = line+1;
+        }
+        activeLines[headIdx + 1] = node;
+        activeNodeCount++;
+    }
+
+    /**
+     * Remove the first node in line 'line'. If the line then becomes empty, adjust the
+     * startLine accordingly.
+     * @param line
+     * @param node
+     */
+    protected void removeNode(int line, KnuthNode node) {
+        KnuthNode n = getNode(line);
+        if (n != node) {
+            log.error("Should be first");
+        } else {
+            activeLines[line*2] = node.next;
+            if (node.next == null) {
+                activeLines[line*2+1] = null;
+            }
+            while (startLine < endLine && getNode(startLine) == null) {
+                startLine++;
+            }
+        }
+        activeNodeCount--;
+    }
+
+    protected KnuthNode getNode(int line) {
+        return activeLines[line * 2];
+    }
+
+    /**
+     * Return true if the position 'idx' is a legal breakpoint.
+     * @param idx
+     * @return
+     */
+    private boolean isLegalBreakpoint(int idx) {
+        KnuthElement elm = getElement(idx);
+        if (elm.isPenalty() && elm.getP() != KnuthElement.INFINITE) {
+            return true;
+        } else if (idx > 0 && elm.isGlue() && getElement(idx-1).isBox()) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+    
+    public int getDifference(int line) {
+        return positions[line].difference;
+    }
+
+    public double getAdjustRatio(int line) {
+        return positions[line].adjustRatio;
+    }
+
+    public int getStart(int line) {
+        KnuthNode previous = positions[line].previous;
+        return line == 0 ? 0 : previous.position + 1; 
+    }
+
+    public int getEnd(int line) {
+        return positions[line].position;
+    }
+
+    /**
+     * Return a string representation of a MinOptMax in the form of a 
+     * "width+stretch-shrink". Useful only for debugging.
+     * @param mom
+     * @return 
+     */
+    private static String width(MinOptMax mom) {
+        return mom.opt + "+" + (mom.max - mom.opt) + "-" + (mom.opt - mom.min); 
+
+    }
+
+    public String toString(String prepend) {
+        StringBuffer sb = new StringBuffer();
+        sb.append("[\n");
+        for (int i = startLine; i < endLine; i++) {
+            for (KnuthNode node = getNode(i); node != null; node = node.next) {
+                sb.append(prepend + "\t" + node + ",\n");
+            }
+        }
+        sb.append(prepend + "]");
+        return sb.toString();
+    }
+
+    protected abstract int filterActiveNodes() ;
+
+    private void calculateBreakPoints(KnuthNode node, KnuthSequence par,
+                                      int total) {
+        KnuthNode bestActiveNode = node;
+        // use bestActiveNode to determine the optimum breakpoints
+        for (int i = node.line; i > 0; i--) {
+            updateData2(bestActiveNode, par, total);
+            bestActiveNode = bestActiveNode.previous;
+        }
+    }
+}
index 3a067d524d78b5cb955602bc831a0aa9bdb5b256..2f366108924ccf02face8347dba63a9329bc17ee 100644 (file)
@@ -137,7 +137,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
         // node is a fo:Character
         if (letterSpaceIPD.min == letterSpaceIPD.max) {
             // constant letter space, only return a box
-            returnList.add(new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
+            returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
                                         areaInfo.total, areaInfo.middle,
                                         new LeafPosition(this, 0), false));
         } else {
@@ -145,14 +145,14 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
             // at the moment the character is supposed to have no letter spaces,
             // but returning this sequence allows us to change only one element
             // if addALetterSpaceTo() is called
-            returnList.add(new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
+            returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
                                         areaInfo.total, areaInfo.middle,
                                         new LeafPosition(this, 0), false));
             returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
                                             new LeafPosition(this, -1), true));
             returnList.add(new KnuthGlue(0, 0, 0,
                                          new LeafPosition(this, -1), true));
-            returnList.add(new KnuthBox(0, 0, 0, 0,
+            returnList.add(new KnuthInlineBox(0, 0, 0, 0,
                                         new LeafPosition(this, -1), true));
         }
 
@@ -171,7 +171,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
 
         if (letterSpaceIPD.min == letterSpaceIPD.max) {
             // constant letter space, return a new box
-            return new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
+            return new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
                                 areaInfo.total, areaInfo.middle,
                                 new LeafPosition(this, 0), false);
         } else {
@@ -220,7 +220,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
         if (letterSpaceIPD.min == letterSpaceIPD.max
             || areaInfo.iLScount == 0) {
             // constant letter space, or no letter space
-            returnList.add(new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
+            returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
                                         areaInfo.total, areaInfo.middle,
                                         new LeafPosition(this, 0), false));
             if (areaInfo.bHyphenated) {
@@ -231,7 +231,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
         } else {
             // adjustable letter space
             returnList.add
-                (new KnuthBox(areaInfo.ipdArea.opt
+                (new KnuthInlineBox(areaInfo.ipdArea.opt
                               - areaInfo.iLScount * letterSpaceIPD.opt,
                               areaInfo.lead, areaInfo.total, areaInfo.middle,
                               new LeafPosition(this, 0), false));
@@ -242,7 +242,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
                                areaInfo.iLScount * letterSpaceIPD.max - letterSpaceIPD.opt,
                                areaInfo.iLScount * letterSpaceIPD.opt - letterSpaceIPD.min,
                                new LeafPosition(this, -1), true));
-            returnList.add(new KnuthBox(0, 0, 0, 0,
+            returnList.add(new KnuthInlineBox(0, 0, 0, 0,
                                         new LeafPosition(this, -1), true));
             if (areaInfo.bHyphenated) {
                 returnList.add
@@ -256,7 +256,7 @@ public class CharacterLayoutManager extends LeafNodeLayoutManager {
     }
 
     protected void addId() {
-        addID(fobj.getId());
+        getPSLM().addIDToPage(fobj.getId());
     }
 }
 
index 093439fbadc590510640e1d0b6995e6437da848a..626532fa44bf8af9a429cc505b6b9f0b7fb468f1 100644 (file)
 package org.apache.fop.layoutmgr;
 
 import org.apache.fop.apps.FOUserAgent;
-import org.apache.fop.fo.FObj;
 import org.apache.fop.fo.Constants;
 import org.apache.fop.fo.pagination.Title;
-import org.apache.fop.fo.flow.Marker;
 import org.apache.fop.area.Area;
-import org.apache.fop.area.AreaTreeHandler;
 import org.apache.fop.area.LineArea;
 import org.apache.fop.area.inline.InlineArea;
-import org.apache.fop.area.Resolvable;
-import org.apache.fop.area.PageViewport;
 
 import java.util.LinkedList;
 import java.util.List;
 import java.util.ListIterator;
-import java.util.Map;
 import java.util.ArrayList;
 import org.apache.fop.traits.MinOptMax;
 
@@ -47,7 +41,6 @@ import org.apache.commons.logging.LogFactory;
  */
 public class ContentLayoutManager implements InlineLevelLayoutManager {
     private FOUserAgent userAgent;
-    private AreaTreeHandler areaTreeHandler;
     private Area holder;
     private int stackSize;
     private LayoutManager parentLM;
@@ -117,7 +110,7 @@ public class ContentLayoutManager implements InlineLevelLayoutManager {
         while (contentIter.hasNext()) {
             KnuthElement element = (KnuthElement) contentIter.next();
             if (element.isBox()) {
-                KnuthBox box = (KnuthBox) element;
+                KnuthInlineBox box = (KnuthInlineBox) element;
                 if (box.getLead() > lineLead) {
                     lineLead = box.getLead();
                 }
@@ -205,10 +198,6 @@ public class ContentLayoutManager implements InlineLevelLayoutManager {
         return userAgent;
     }
 
-    /** @see org.apache.fop.layoutmgr.LayoutManager */
-    public void setFObj(FObj fobj) {
-    }
-
     /** @see org.apache.fop.layoutmgr.LayoutManager */
     public void setParent(LayoutManager lm) {
         parentLM = lm;
@@ -252,44 +241,6 @@ public class ContentLayoutManager implements InlineLevelLayoutManager {
     public void getWordChars(StringBuffer sbChars, Position bp1,
             Position bp2) { }
 
-    /** @see org.apache.fop.layoutmgr.LayoutManager */
-    public String getCurrentPageNumberString() {
-        return parentLM.getCurrentPageNumberString();
-    }
-
-    /** @see org.apache.fop.layoutmgr.LayoutManager */
-    public PageViewport resolveRefID(String ref) {
-        return parentLM.resolveRefID(ref);
-    }
-
-    /** @see org.apache.fop.layoutmgr.LayoutManager */
-    public void addIDToPage(String id) {
-        parentLM.addIDToPage(id);
-    }
-
-    /** @see org.apache.fop.layoutmgr.LayoutManager */
-    public void addUnresolvedArea(String id, Resolvable res) {
-        parentLM.addUnresolvedArea(id, res);
-    }
-
-    /** @see org.apache.fop.layoutmgr.LayoutManager */
-    public void addMarkerMap(Map marks, boolean starting, boolean isfirst, boolean islast) {
-        parentLM.addMarkerMap(marks, starting, isfirst, islast);
-    }
-
-    /** @see org.apache.fop.layoutmgr.LayoutManager */
-    public Marker retrieveMarker(String name, int pos, int boundary) {
-        return parentLM.retrieveMarker(name, pos, boundary);
-    }
-
-    /**
-     * @see org.apache.fop.layoutmgr.LayoutManager
-     * @return the AreaTreeHandler object.
-     */
-    public AreaTreeHandler getAreaTreeHandler() {
-        return parentLM.getAreaTreeHandler();
-    }
-
     /**
      * @see org.apache.fop.layoutmgr.LayoutManager#preLoadNext
      */
@@ -358,8 +309,8 @@ public class ContentLayoutManager implements InlineLevelLayoutManager {
         return contentList;
     }
 
-    public KnuthElement addALetterSpaceTo(KnuthElement element) {
-        return element;
+    public List addALetterSpaceTo(List oldList) {
+        return oldList;
     }
 
     public void getWordChars(StringBuffer sbChars, Position pos) {
@@ -373,10 +324,13 @@ public class ContentLayoutManager implements InlineLevelLayoutManager {
     }
 
     public LinkedList getChangedKnuthElements(List oldList,
-                                              int flaggedPenalty,
+                                              /*int flaggedPenalty,*/
                                               int alignment) {
         return null;
     }
-
+    
+    public PageSequenceLayoutManager getPSLM() {
+        return parentLM.getPSLM();
+    }
 }
 
diff --git a/src/java/org/apache/fop/layoutmgr/ElementListUtils.java b/src/java/org/apache/fop/layoutmgr/ElementListUtils.java
new file mode 100644 (file)
index 0000000..c1100d0
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.apache.fop.traits.MinOptMax;
+
+/**
+ * Utilities for Knuth element lists.
+ */
+public class ElementListUtils {
+
+    /**
+     * Removes all legal breaks in an element list.
+     * @param elements the element list
+     */
+    public static void removeLegalBreaks(LinkedList elements) {
+        ListIterator i = elements.listIterator();
+        while (i.hasNext()) {
+            KnuthElement el = (KnuthElement)i.next();
+            if (el.isPenalty()) {
+                KnuthPenalty penalty = (KnuthPenalty)el;
+                //Convert all penalties no break inhibitors
+                if (penalty.getP() < KnuthPenalty.INFINITE) {
+                    i.set(new KnuthPenalty(penalty.getW(), KnuthPenalty.INFINITE, 
+                            penalty.isFlagged(), penalty.getPosition(), penalty.isAuxiliary()));
+                }
+            } else if (el.isGlue()) {
+                i.previous();
+                if (el.isBox()) {
+                    i.next();
+                    i.add(new KnuthPenalty(0, KnuthPenalty.INFINITE, false, 
+                            /*new Position(getTableLM())*/null, false));
+                }
+            }
+        }
+    }
+    
+    /**
+     * Removes all legal breaks in an element list. A constraint can be specified to limit the
+     * range in which the breaks are removed. Legal breaks occuring before at least 
+     * constraint.opt space is filled will be removed.
+     * @param elements the element list
+     * @param constraint min/opt/max value to restrict the range in which the breaks are removed.
+     * @return true if the opt constraint is bigger than the list contents
+     */
+    public static boolean removeLegalBreaks(LinkedList elements, MinOptMax constraint) {
+        int len = 0;
+        ListIterator i = elements.listIterator();
+        while (i.hasNext()) {
+            KnuthElement el = (KnuthElement)i.next();
+            if (el.isPenalty()) {
+                KnuthPenalty penalty = (KnuthPenalty)el;
+                //Convert all penalties no break inhibitors
+                if (penalty.getP() < KnuthPenalty.INFINITE) {
+                    i.set(new KnuthPenalty(penalty.getW(), KnuthPenalty.INFINITE, 
+                            penalty.isFlagged(), penalty.getPosition(), penalty.isAuxiliary()));
+                }
+            } else if (el.isGlue()) {
+                len += el.getW();
+                i.previous();
+                if (el.isBox()) {
+                    i.next();
+                    i.add(new KnuthPenalty(0, KnuthPenalty.INFINITE, false, 
+                            /*new Position(getTableLM())*/null, false));
+                }
+            } else {
+                len += el.getW();
+            }
+            if (len > constraint.opt) {
+                return false;
+            }
+        }
+        return true;
+    }
+    
+    /**
+     * Calculates the content length of the given element list. Warning: It doesn't take any
+     * stretch and shrink possibilities into account.
+     * @param elems the element list
+     * @param start element at which to start
+     * @param end element at which to stop
+     * @return the content length
+     */
+    public static int calcContentLength(List elems, int start, int end) {
+        ListIterator iter = elems.listIterator(start);
+        int count = end - start + 1;
+        int len = 0;
+        while (iter.hasNext()) {
+            KnuthElement el = (KnuthElement)iter.next();
+            if (el.isBox()) {
+                len += el.getW();
+            } else if (el.isGlue()) {
+                len += el.getW();
+            } else {
+                //log.debug("Ignoring penalty: " + el);
+                //ignore penalties
+            }
+            count--;
+            if (count == 0) {
+                break;
+            }
+        }
+        return len;
+    }
+    
+    /**
+     * Calculates the content length of the given element list. Warning: It doesn't take any
+     * stretch and shrink possibilities into account.
+     * @param elems the element list
+     * @return the content length
+     */
+    public static int calcContentLength(List elems) {
+        return calcContentLength(elems, 0, elems.size() - 1);
+    }
+    
+}
index bad48b8a1a691a5fc85bbcbb23f4acf05ada1e26..18c3640ebc642e92990bac081458479bf44f4c00 100644 (file)
@@ -208,7 +208,7 @@ public class ExternalGraphicLayoutManager extends LeafNodeLayoutManager {
      }
      
      protected void addId() {
-         addID(fobj.getId());
+         getPSLM().addIDToPage(fobj.getId());
      }
 }
 
index f249941c056ad7c5a6c45084bb42cfc154310e34..7a8386dd2800412dcdc513cfaea5aa399ac52faa 100644 (file)
 package org.apache.fop.layoutmgr;
 
 import org.apache.fop.datatypes.PercentBase;
-import org.apache.fop.fo.flow.Marker;
 import org.apache.fop.fo.pagination.Flow;
 import org.apache.fop.area.Area;
 import org.apache.fop.area.BlockParent;
 
+import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
-import org.apache.fop.traits.MinOptMax;
+import java.util.ListIterator;
 
 /**
  * LayoutManager for an fo:flow object.
@@ -33,8 +34,8 @@ import org.apache.fop.traits.MinOptMax;
  * This LM is responsible for getting columns of the appropriate size
  * and filling them with block-level areas generated by its children.
  */
-public class FlowLayoutManager extends BlockStackingLayoutManager {
-    
+public class FlowLayoutManager extends BlockStackingLayoutManager
+                               implements BlockLevelLayoutManager {
     private Flow fobj;
     
     /** List of break possibilities */
@@ -51,6 +52,22 @@ public class FlowLayoutManager extends BlockStackingLayoutManager {
      */
     private int numSubsequentOverflows = 0;
     
+/*LF*/
+    private static class StackingIter extends PositionIterator {
+        StackingIter(Iterator parentIter) {
+            super(parentIter);
+        }
+
+        protected LayoutManager getLM(Object nextObj) {
+            return ((Position) nextObj).getLM();
+        }
+
+        protected Position getPos(Object nextObj) {
+            return ((Position) nextObj);
+        }
+    }
+/*LF*/
+
     /**
      * This is the top level layout manager.
      * It is created by the PageSequence FO.
@@ -61,9 +78,7 @@ public class FlowLayoutManager extends BlockStackingLayoutManager {
         fobj = node;
     }
 
-    /**
-     * @see org.apache.fop.layoutmgr.LayoutManager#getNextBreakPoss(LayoutContext)
-     */
+    /*
     public BreakPoss getNextBreakPoss(LayoutContext context) {
 
         // currently active LM
@@ -131,26 +146,266 @@ public class FlowLayoutManager extends BlockStackingLayoutManager {
                              new LeafPosition(this, blockBreaks.size() - 1));
         }
         return null;
+    }*/
+
+
+    /**
+     * "wrap" the Position inside each element moving the elements from 
+     * SourceList to targetList
+     * @param sourceList source list
+     * @param targetList target list receiving the wrapped position elements
+     */
+    protected void wrapPositionElements(List sourceList, List targetList) {
+        ListIterator listIter = sourceList.listIterator();
+        while (listIter.hasNext()) {
+            KnuthElement tempElement;
+            tempElement = (KnuthElement) listIter.next();
+            //if (tempElement.getLayoutManager() != this) {
+            tempElement.setPosition(new NonLeafPosition(this,
+                    tempElement.getPosition()));
+            //}
+            targetList.add(tempElement);
+        }
+    }
+
+    
+//TODO Reintroduce emergency counter (generate error to avoid endless loop)
+    public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+        // set layout dimensions
+        fobj.setLayoutDimension(PercentBase.BLOCK_IPD, context.getRefIPD());
+        fobj.setLayoutDimension(PercentBase.BLOCK_BPD, context.getStackLimit().opt);
+
+        // currently active LM
+        BlockLevelLayoutManager curLM;
+        BlockLevelLayoutManager prevLM = null;
+        //MinOptMax stackSize = new MinOptMax();
+        LinkedList returnedList;
+        LinkedList returnList = new LinkedList();
+
+        while ((curLM = ((BlockLevelLayoutManager) getChildLM())) != null) {
+            if (curLM.generatesInlineAreas()) {
+                log.error("inline area not allowed under flow - ignoring");
+                curLM.setFinished(true);
+                continue;
+            }
+
+            // Set up a LayoutContext
+            //MinOptMax bpd = context.getStackLimit();
+
+            LayoutContext childLC = new LayoutContext(0);
+            childLC.setStackLimit(context.getStackLimit());
+            childLC.setRefIPD(context.getRefIPD());
+
+            // get elements from curLM
+            returnedList = curLM.getNextKnuthElements(childLC, alignment);
+            //log.debug("FLM.getNextKnuthElements> returnedList.size() = " + returnedList.size());
+
+            // "wrap" the Position inside each element
+            LinkedList tempList = returnedList;
+            returnedList = new LinkedList();
+            wrapPositionElements(tempList, returnedList);
+
+            if (returnedList.size() == 1
+                && ((KnuthElement)returnedList.getFirst()).isPenalty()
+                && ((KnuthPenalty)returnedList.getFirst()).getP() == -KnuthElement.INFINITE) {
+                // a descendant of this flow has break-before
+                returnList.addAll(returnedList);
+                return returnList;
+            } else {
+                if (returnList.size() > 0) {
+                    // there is a block before this one
+                    if (prevLM.mustKeepWithNext()
+                        || curLM.mustKeepWithPrevious()) {
+                        // add an infinite penalty to forbid a break between blocks
+                        returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, new Position(this), false));
+                    } else if (!((KnuthElement) returnList.getLast()).isGlue()) {
+                        // add a null penalty to allow a break between blocks
+                        returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
+                    }
+                }
+/*LF*/          if (returnedList.size() > 0) { // controllare!
+                    returnList.addAll(returnedList);
+                    if (((KnuthElement)returnedList.getLast()).isPenalty()
+                        && ((KnuthPenalty)returnedList.getLast()).getP() == -KnuthElement.INFINITE) {
+                        // a descendant of this flow has break-after
+/*LF*/                  //System.out.println("FLM - break after!!");
+                        return returnList;
+                    }
+/*LF*/          }
+            }
+            prevLM = curLM;
+        }
+
+        setFinished(true);
+
+        if (returnList.size() > 0) {
+            return returnList;
+        } else {
+            return null;
+        }
+    }
+
+    public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
+        log.debug(" FLM.negotiateBPDAdjustment> " + adj);
+
+        if (lastElement.getPosition() instanceof NonLeafPosition) {
+            // this element was not created by this FlowLM
+            NonLeafPosition savedPos = (NonLeafPosition)lastElement.getPosition();
+            lastElement.setPosition(savedPos.getPosition());
+            int returnValue = ((BlockLevelLayoutManager) lastElement.getLayoutManager()).negotiateBPDAdjustment(adj, lastElement);
+            lastElement.setPosition(savedPos);
+            log.debug(" FLM.negotiateBPDAdjustment> result " + returnValue);
+            return returnValue;
+        } else {
+            return 0;
+        }
+    }
+
+    public void discardSpace(KnuthGlue spaceGlue) {
+        log.debug(" FLM.discardSpace> ");
+
+        if (spaceGlue.getPosition() instanceof NonLeafPosition) {
+            // this element was not created by this FlowLM
+            NonLeafPosition savedPos = (NonLeafPosition)spaceGlue.getPosition();
+            spaceGlue.setPosition(savedPos.getPosition());
+            ((BlockLevelLayoutManager) spaceGlue.getLayoutManager()).discardSpace(spaceGlue);
+            spaceGlue.setPosition(savedPos);
+        }
+    }
+
+    public boolean mustKeepTogether() {
+        return false;
+    }
+
+    public boolean mustKeepWithPrevious() {
+        return false;
+    }
+
+    public boolean mustKeepWithNext() {
+        return false;
+    }
+
+    public LinkedList getChangedKnuthElements(List oldList, /*int flaggedPenalty,*/ int alignment) {
+        ListIterator oldListIterator = oldList.listIterator();
+        KnuthElement returnedElement;
+        LinkedList returnedList = new LinkedList();
+        LinkedList returnList = new LinkedList();
+        KnuthElement prevElement = null;
+        KnuthElement currElement = null;
+        int fromIndex = 0;
+
+/*LF*/  //System.out.println("");
+/*LF*/  //System.out.println("FLM.getChangedKnuthElements> prima dell'unwrap, oldList.size() = " + oldList.size() + " da 0 a " + (oldList.size() - 1));
+        // "unwrap" the Positions stored in the elements
+        KnuthElement oldElement;
+        while (oldListIterator.hasNext()) {
+            oldElement = (KnuthElement)oldListIterator.next();
+            if (oldElement.getPosition() instanceof NonLeafPosition) {
+                // oldElement was created by a descendant of this FlowLM
+                oldElement.setPosition(((NonLeafPosition)oldElement.getPosition()).getPosition());
+            } else {
+                // thisElement was created by this FlowLM, remove it
+                oldListIterator.remove();
+            }
+        }
+        // reset the iterator
+        oldListIterator = oldList.listIterator();
+
+/*LF*/  //System.out.println("FLM.getChangedKnuthElements> dopo l'unwrap, oldList.size() = " + oldList.size() + " da 0 a " + (oldList.size() - 1));
+
+        while (oldListIterator.hasNext()) {
+            currElement = (KnuthElement) oldListIterator.next();
+/*LF*/      //System.out.println("elemento n. " + oldListIterator.previousIndex() + " nella oldList");
+            if (prevElement != null
+                && prevElement.getLayoutManager() != currElement.getLayoutManager()) {
+                // prevElement is the last element generated by the same LM
+                BlockLevelLayoutManager prevLM = (BlockLevelLayoutManager)
+                                                 prevElement.getLayoutManager();
+                BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
+                                                 currElement.getLayoutManager();
+/*LF*/          //System.out.println("FLM.getChangedKnuthElements> chiamata da " + fromIndex + " a " + oldListIterator.previousIndex());
+                returnedList.addAll(prevLM.getChangedKnuthElements(oldList.subList(fromIndex, oldListIterator.previousIndex()),
+                                                                   /*flaggedPenalty,*/ alignment));
+                fromIndex = oldListIterator.previousIndex();
+
+                // there is another block after this one
+                if (prevLM.mustKeepWithNext()
+                    || currLM.mustKeepWithPrevious()) {
+                    // add an infinite penalty to forbid a break between blocks
+                    returnedList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, new Position(this), false));
+                } else if (!((KnuthElement) returnedList.getLast()).isGlue()) {
+                    // add a null penalty to allow a break between blocks
+                    returnedList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
+                }
+            }
+            prevElement = currElement;
+        }
+        if (currElement != null) {
+            BlockLevelLayoutManager currLM = (BlockLevelLayoutManager)
+                                             currElement.getLayoutManager();
+/*LF*/      //System.out.println("FLM.getChangedKnuthElements> chiamata da " + fromIndex + " a " + oldList.size());
+            returnedList.addAll(currLM.getChangedKnuthElements(oldList.subList(fromIndex, oldList.size()),
+                                                               /*flaggedPenalty,*/ alignment));
+        }
+
+        // "wrap" the Position stored in each element of returnedList
+        // and add elements to returnList
+        ListIterator listIter = returnedList.listIterator();
+        while (listIter.hasNext()) {
+            returnedElement = (KnuthElement)listIter.next();
+            if (returnedElement.getLayoutManager() != this) {
+                returnedElement.setPosition(new NonLeafPosition(this, returnedElement.getPosition()));
+            }
+            returnList.add(returnedElement);
+        }
+
+        return returnList;
     }
 
     /**
      * @see org.apache.fop.layoutmgr.LayoutManager#addAreas(PositionIterator, LayoutContext)
      */
     public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
-
-        LayoutManager childLM;
+        AreaAdditionUtil.addAreas(parentIter, layoutContext);
+        /*
+        LayoutManager childLM = null;
         LayoutContext lc = new LayoutContext(0);
+        LayoutManager firstLM = null;
+        LayoutManager lastLM = null;
+
+        // "unwrap" the NonLeafPositions stored in parentIter
+        // and put them in a new list; 
+        LinkedList positionList = new LinkedList();
+        Position pos;
         while (parentIter.hasNext()) {
-            LeafPosition lfp = (LeafPosition) parentIter.next();
-            // Add the block areas to Area
-            PositionIterator breakPosIter =  new BreakPossPosIter(
-                    blockBreaks, iStartPos, lfp.getLeafPos() + 1);
-            iStartPos = lfp.getLeafPos() + 1;
-            while ((childLM = breakPosIter.getNextChildLM()) != null) {
-                childLM.addAreas(breakPosIter, lc);
+            pos = (Position)parentIter.next();
+            if (pos instanceof NonLeafPosition) {
+                // pos was created by a child of this FlowLM
+                positionList.add(((NonLeafPosition) pos).getPosition());
+                lastLM = ((NonLeafPosition) pos).getPosition().getLM();
+                if (firstLM == null) {
+                    firstLM = lastLM;
+                }
+            } else {
+                // pos was created by this FlowLM, so it must be ignored
             }
         }
 
+        StackingIter childPosIter = new StackingIter(positionList.listIterator());
+        while ((childLM = childPosIter.getNextChildLM()) != null) {
+            // Add the block areas to Area
+            lc.setFlags(LayoutContext.FIRST_AREA, childLM == firstLM);
+            lc.setFlags(LayoutContext.LAST_AREA, childLM == lastLM);
+            // set space before for the first LM, in order to implement
+            // display-align = center or after
+            lc.setSpaceBefore((childLM == firstLM ? layoutContext.getSpaceBefore() : 0));
+            // set space after for each LM, in order to implement
+            // display-align = distribute
+            lc.setSpaceAfter(layoutContext.getSpaceAfter());
+            lc.setStackLimit(layoutContext.getStackLimit());
+            childLM.addAreas(childPosIter, lc);
+        }*/
+
         flush();
     }
 
@@ -184,17 +439,5 @@ public class FlowLayoutManager extends BlockStackingLayoutManager {
             reset(null);
         }
     }
-
-    /**
-     * Retrieve marker is not allowed in the flow so this reports an
-     * error and returns null.
-     *
-     * @see org.apache.fop.layoutmgr.LayoutManager
-     */
-    public Marker retrieveMarker(String name, int pos, int boundary) {
-        // error cannot retrieve markers in flow
-        log.error("Cannot retrieve a marker from the flow");
-        return null;
-    }
 }
 
index f6e2a4cd8c71f5ea3ccc61627970ded5e0a50067..e03947d2e558b46370224b3032be4aaa64fcf3f3 100644 (file)
@@ -44,6 +44,6 @@ public class ICLayoutManager extends LeafNodeLayoutManager {
     }
 
     protected void addId() {
-        addID(fobj.getId());
+        getPSLM().addIDToPage(fobj.getId());
     }
 }
index c2027067057b72451ac89496774773112676607b..b02e96c98275db6281901c9b1474954e99fe2ca8 100755 (executable)
@@ -179,6 +179,7 @@ public class InlineLayoutManager extends InlineStackingLayoutManager
         return null;
     }
 
+    /*
     public KnuthElement addALetterSpaceTo(KnuthElement element) {
         NonLeafPosition savedPos = (NonLeafPosition) element.getPosition();
         element.setPosition(savedPos.getPosition());
@@ -325,6 +326,6 @@ public class InlineLayoutManager extends InlineStackingLayoutManager
             returnList.add(returnedElement);
         }
         return returnList;
-    }
+    }*/
 }
 
index 767e901b6d37eadbcecde77161d0fe724a516eef..98d7c35e5f8f62a3c1b43192e047f6406e76af4e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2004 The Apache Software Foundation.
+ * Copyright 2004-2005 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,7 +18,6 @@
  
 package org.apache.fop.layoutmgr;
 
-import java.util.LinkedList;
 import java.util.List;
 
 /**
@@ -26,25 +25,15 @@ import java.util.List;
  */
 public interface InlineLevelLayoutManager extends LayoutManager {
 
-    /**
-     * Get a sequence of KnuthElements representing the content 
-     * of the node assigned to the LM
-     * 
-     * @param context   the LayoutContext used to store layout information
-     * @param alignment the desired text alignement
-     * @return          the list of KnuthElements
-     */
-    LinkedList getNextKnuthElements(LayoutContext context, int alignment);
-
     /**
      * Tell the LM to modify its data, adding a letter space 
-     * to the word fragment represented by the given element,
-     * and returning a corrected element
+     * to the word fragment represented by the given elements,
+     * and returning the corrected elements
      *
-     * @param element the element which must be given one more letter space
-     * @return        the new element replacing the old one
+     * @param oldList the elements which must be given one more letter space
+     * @return        the new elements replacing the old ones
      */
-    KnuthElement addALetterSpaceTo(KnuthElement element);
+    List addALetterSpaceTo(List oldList);
 
     /**
      * Get the word chars corresponding to the given position
@@ -70,15 +59,4 @@ public interface InlineLevelLayoutManager extends LayoutManager {
      */
     boolean applyChanges(List oldList);
 
-    /**
-     * Get a sequence of KnuthElements representing the content 
-     * of the node assigned to the LM, after changes have been applied
-     *
-     * @param oldList        the elements to replace
-     * @param flaggedPenalty the penalty value for hyphenated lines
-     * @param alignment      the desired text alignment
-     * @return               the updated list of KnuthElements
-     */
-    LinkedList getChangedKnuthElements(List oldList, int flaggedPenalty,
-                                       int alignment);
 }
index 92fd5d7d6ff259d66f2f49dfd0bf925870c65234..1993145769e427e04f1015157811019ba49774dd 100644 (file)
@@ -20,11 +20,14 @@ package org.apache.fop.layoutmgr;
 
 import java.util.LinkedList;
 import java.util.Iterator;
+import java.util.List;
 import java.util.ListIterator;
 import java.util.HashMap;
 
 import org.apache.fop.fo.FObj;
+import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
 import org.apache.fop.fo.properties.SpaceProperty;
+import org.apache.fop.traits.InlineProps;
 import org.apache.fop.traits.SpaceVal;
 import org.apache.fop.area.Area;
 import org.apache.fop.area.inline.InlineArea;
@@ -37,7 +40,8 @@ import org.apache.fop.traits.MinOptMax;
  * which stack children in the inline direction, such as Inline or
  * Line. It should not be instantiated directly.
  */
-public class InlineStackingLayoutManager extends AbstractLayoutManager {
+public class InlineStackingLayoutManager extends AbstractLayoutManager 
+                                         implements InlineLevelLayoutManager {
 
 
     private static class StackingIter extends PositionIterator {
@@ -69,7 +73,7 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager {
     private Area currentArea; // LineArea or InlineParent
 
     private BreakPoss prevBP;
-    protected LayoutContext childLC ;
+    protected LayoutContext childLC;
 
     private LayoutManager lastChildLM = null; // Set when return last breakposs
     private boolean bAreaCreated = false;
@@ -534,7 +538,7 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager {
 
     // Current child layout context
     protected LayoutContext getContext() {
-        return childLC ;
+        return childLC;
     }
 
     protected void addSpace(Area parentArea, MinOptMax spaceRange,
@@ -558,5 +562,214 @@ public class InlineStackingLayoutManager extends AbstractLayoutManager {
             }
         }
     }
+
+    public LinkedList getNextKnuthElements(LayoutContext lc, int alignment) {
+        InlineLevelLayoutManager curLM;
+
+        // the list returned by child LM
+        LinkedList returnedList;
+        KnuthElement returnedElement;
+
+        // the list which will be returned to the parent LM
+        LinkedList returnList = new LinkedList();
+
+        SpaceSpecifier leadingSpace = lc.getLeadingSpace();
+
+        if (lc.startsNewArea()) {
+            // First call to this LM in new parent "area", but this may
+            // not be the first area created by this inline
+            childLC = new LayoutContext(lc);
+            lc.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart()));
+
+            // Check for "fence"
+            if (hasLeadingFence(!lc.isFirstArea())) {
+                // Reset leading space sequence for child areas
+                leadingSpace = new SpaceSpecifier(false);
+            }
+            // Reset state variables
+            clearPrevIPD(); // Clear stored prev content dimensions
+        }
+
+        while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) {
+            // get KnuthElements from curLM
+            returnedList = curLM.getNextKnuthElements(lc, alignment);
+            if (returnedList != null) {
+                // "wrap" the Position stored in each element of returnedList
+                ListIterator listIter = returnedList.listIterator();
+                while (listIter.hasNext()) {
+                    returnedElement = (KnuthElement) listIter.next();
+                    returnedElement.setPosition
+                        (new NonLeafPosition(this,
+                                             returnedElement.getPosition()));
+                    returnList.add(returnedElement);
+                }
+                return returnList;
+            } else {
+                // curLM returned null because it finished;
+                // just iterate once more to see if there is another child
+            }
+        }
+        setFinished(true);
+        return null;
+    }
+
+    public List addALetterSpaceTo(List oldList) {
+        // old list contains only a box, or the sequence: box penalty glue box
+
+        ListIterator oldListIterator = oldList.listIterator();
+        KnuthElement element = null;
+        // "unwrap" the Position stored in each element of oldList
+        while (oldListIterator.hasNext()) {
+            element = (KnuthElement) oldListIterator.next();
+            element.setPosition(((NonLeafPosition)element.getPosition()).getPosition());
+        }
+
+        oldList = ((InlineLevelLayoutManager)
+                   element.getLayoutManager()).addALetterSpaceTo(oldList);
+
+        // "wrap" againg the Position stored in each element of oldList
+        oldListIterator = oldList.listIterator();
+        while (oldListIterator.hasNext()) {
+            element = (KnuthElement) oldListIterator.next();
+            element.setPosition(new NonLeafPosition(this, element.getPosition()));
+        }
+
+        return oldList;
+    }
+
+    public void getWordChars(StringBuffer sbChars, Position pos) {
+        Position newPos = ((NonLeafPosition) pos).getPosition();
+        ((InlineLevelLayoutManager)
+         newPos.getLM()).getWordChars(sbChars, newPos);
+    }
+
+    public void hyphenate(Position pos, HyphContext hc) {
+        Position newPos = ((NonLeafPosition) pos).getPosition();
+        ((InlineLevelLayoutManager)
+         newPos.getLM()).hyphenate(newPos, hc);
+    }
+
+    public boolean applyChanges(List oldList) {
+        // "unwrap" the Positions stored in the elements
+        ListIterator oldListIterator = oldList.listIterator();
+        KnuthElement oldElement;
+        while (oldListIterator.hasNext()) {
+            oldElement = (KnuthElement) oldListIterator.next();
+            oldElement.setPosition
+                (((NonLeafPosition) oldElement.getPosition()).getPosition());
+        }
+        // reset the iterator
+        oldListIterator = oldList.listIterator();
+
+        InlineLevelLayoutManager prevLM = null;
+        InlineLevelLayoutManager currLM;
+        int fromIndex = 0;
+
+        boolean bSomethingChanged = false;
+        while(oldListIterator.hasNext()) {
+            oldElement = (KnuthElement) oldListIterator.next();
+            currLM = (InlineLevelLayoutManager) oldElement.getLayoutManager();
+            // initialize prevLM
+            if (prevLM == null) {
+                prevLM = currLM;
+            }
+
+            if (currLM != prevLM || !oldListIterator.hasNext()) {
+                if (oldListIterator.hasNext()) {
+                    bSomethingChanged
+                        = prevLM.applyChanges(oldList.subList(fromIndex, oldListIterator.previousIndex()))
+                        || bSomethingChanged;
+                    prevLM = currLM;
+                    fromIndex = oldListIterator.previousIndex();
+                } else if (currLM == prevLM) {
+                    bSomethingChanged
+                        = prevLM.applyChanges(oldList.subList(fromIndex, oldList.size()))
+                        || bSomethingChanged;
+                } else {
+                    bSomethingChanged
+                        = prevLM.applyChanges(oldList.subList(fromIndex, oldListIterator.previousIndex()))
+                        || bSomethingChanged;
+                    bSomethingChanged
+                        = currLM.applyChanges(oldList.subList(oldListIterator.previousIndex(), oldList.size()))
+                        || bSomethingChanged;
+                }
+            }
+        }
+
+        // "wrap" again the Positions stored in the elements
+        oldListIterator = oldList.listIterator();
+        while (oldListIterator.hasNext()) {
+            oldElement = (KnuthElement) oldListIterator.next();
+            oldElement.setPosition
+                (new NonLeafPosition(this, oldElement.getPosition()));
+        }
+        return bSomethingChanged;
+    }
+
+    public LinkedList getChangedKnuthElements(List oldList, /*int flaggedPenalty,*/ int alignment) {
+        // "unwrap" the Positions stored in the elements
+        ListIterator oldListIterator = oldList.listIterator();
+        KnuthElement oldElement;
+        while (oldListIterator.hasNext()) {
+            oldElement = (KnuthElement) oldListIterator.next();
+            oldElement.setPosition
+                (((NonLeafPosition) oldElement.getPosition()).getPosition());
+        }
+        // reset the iterator
+        oldListIterator = oldList.listIterator();
+
+        KnuthElement returnedElement;
+        LinkedList returnedList = new LinkedList();
+        LinkedList returnList = new LinkedList();
+        InlineLevelLayoutManager prevLM = null;
+        InlineLevelLayoutManager currLM;
+        int fromIndex = 0;
+
+        while(oldListIterator.hasNext()) {
+            oldElement = (KnuthElement) oldListIterator.next();
+            currLM = (InlineLevelLayoutManager) oldElement.getLayoutManager();
+            if (prevLM == null) {
+                prevLM = currLM;
+            }
+
+            if (currLM != prevLM || !oldListIterator.hasNext()) {
+                if (oldListIterator.hasNext()) {
+                    returnedList.addAll
+                        (prevLM.getChangedKnuthElements
+                         (oldList.subList(fromIndex,
+                                          oldListIterator.previousIndex()),
+                          /*flaggedPenalty,*/ alignment));
+                    prevLM = currLM;
+                    fromIndex = oldListIterator.previousIndex();
+                } else if (currLM == prevLM) {
+                    returnedList.addAll
+                        (prevLM.getChangedKnuthElements
+                         (oldList.subList(fromIndex, oldList.size()),
+                          /*flaggedPenalty,*/ alignment));
+                } else {
+                    returnedList.addAll
+                        (prevLM.getChangedKnuthElements
+                         (oldList.subList(fromIndex,
+                                          oldListIterator.previousIndex()),
+                          /*flaggedPenalty,*/ alignment));
+                    returnedList.addAll
+                        (currLM.getChangedKnuthElements
+                         (oldList.subList(oldListIterator.previousIndex(),
+                                          oldList.size()),
+                          /*flaggedPenalty,*/ alignment));
+                }
+            }
+        }
+
+        // "wrap" the Position stored in each element of returnedList
+        ListIterator listIter = returnedList.listIterator();
+        while (listIter.hasNext()) {
+            returnedElement = (KnuthElement) listIter.next();
+            returnedElement.setPosition
+                (new NonLeafPosition(this, returnedElement.getPosition()));
+            returnList.add(returnedElement);
+        }
+        return returnList;
+    }
 }
 
index dce3fa4616b8c62b9135db26d2abd37aaf48a7ac..2d50c2270931e87feceb154bdca652fb787d9f1c 100644 (file)
@@ -19,7 +19,6 @@
 package org.apache.fop.layoutmgr;
 
 // Java
-import java.awt.geom.Point2D;
 import java.awt.geom.Rectangle2D;
 
 // FOP
@@ -194,7 +193,7 @@ public class InstreamForeignObjectLM extends LeafNodeLayoutManager {
      * @see org.apache.fop.layoutmgr.LeafNodeLayoutManager#addId()
      */
     protected void addId() {
-        addID(fobj.getId());
+        getPSLM().addIDToPage(fobj.getId());
     }
 }
 
diff --git a/src/java/org/apache/fop/layoutmgr/KnuthBlockBox.java b/src/java/org/apache/fop/layoutmgr/KnuthBlockBox.java
new file mode 100644 (file)
index 0000000..896532e
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import org.apache.fop.traits.MinOptMax;
+
+public class KnuthBlockBox extends KnuthBox {
+    
+    private MinOptMax ipdRange;
+    private int bpd;
+
+    public KnuthBlockBox(int w, MinOptMax range, int bpdim, Position pos, boolean bAux) {
+        super(w, pos, bAux);
+        ipdRange = (MinOptMax) range.clone();
+        bpd = bpdim;
+    }
+
+    public MinOptMax getIPDRange() {
+        return (MinOptMax) ipdRange.clone();
+    }
+
+    public int getBPD() {
+        return bpd;
+    }
+}
\ No newline at end of file
index 8479f77d6f5b404bd207d02b17f6daac4a0ae276..77f463068f754a2bcc5844d9048601f564b445c8 100644 (file)
@@ -32,49 +32,33 @@ package org.apache.fop.layoutmgr;
  * positioning, and the methods used to get them.
  */
 public class KnuthBox extends KnuthElement {
-    private int lead;
-    private int total;
-    private int middle;
 
     /**
      * Create a new KnuthBox.
      *
      * @param w    the width of this box
-     * @param l    the height of this box above the main baseline
-     * @param t    the total height of this box
-     * @param m    the height of this box above and below the middle baseline
      * @param pos  the Position stored in this box
      * @param bAux is this box auxiliary?
      */
-    public KnuthBox(int w, int l, int t, int m, Position pos, boolean bAux) {
+    public KnuthBox(int w, Position pos, boolean bAux) {
         super(w, pos, bAux);
-        lead = l;
-        total = t;
-        middle = m;
     }
 
+    /** @see org.apache.fop.layoutmgr.KnuthElement#isBox() */
     public boolean isBox() {
         return true;
     }
 
-    /**
-     * Return the height of this box above the main baseline.
-     */
-    public int getLead() {
-        return lead;
-    }
-
-    /**
-     * Return the total height of this box.
-     */
-    public int getTotal() {
-        return total;
-    }
-
-    /**
-     * Return the height of this box above and below the middle baseline.
-     */
-    public int getMiddle() {
-        return middle;
+    /** @see java.lang.Object#toString() */
+    public String toString() {
+        StringBuffer sb = new StringBuffer(64);
+        if (isAuxiliary()) {
+            sb.append("aux. ");
+        }
+        sb.append("box");
+        sb.append(" w=");
+        sb.append(getW());
+        return sb.toString();
     }
+    
 }
index e377ce351478c3c8d0f19c901d5915d400b9f7e9..299266ed8caffe0cb7624447c6a6c0395f96e4c0 100644 (file)
@@ -45,8 +45,10 @@ package org.apache.fop.layoutmgr;
  * to get these values.
  */
 public class KnuthGlue extends KnuthElement {
+    
     private int stretchability;
     private int shrinkability;
+    private int adjustmentClass = -1;
 
     /**
      * Create a new KnuthGlue.
@@ -63,21 +65,48 @@ public class KnuthGlue extends KnuthElement {
         shrinkability = z;
     }
 
+    public KnuthGlue(int w, int y, int z,
+            int iAdjClass, Position pos, boolean bAux) {
+        super(w, pos, bAux);
+        stretchability = y;
+        shrinkability = z;
+        adjustmentClass = iAdjClass;
+    }
+
+    /** @see org.apache.fop.layoutmgr.KnuthElement#isGlue() */
     public boolean isGlue() {
         return true;
     }
 
-    /**
-     * Return the stretchability of this glue.
-     */
+    /** @return the stretchability of this glue. */
     public int getY() {
         return stretchability;
     }
 
-    /**
-     * Return the shrinkability of this glue.
-     */
+    /** @return the shrinkability of this glue. */
     public int getZ() {
         return shrinkability;
     }
+    
+    /** @return the adjustment class (or role) of this glue. */
+    public int getAdjustmentClass() {
+        return adjustmentClass;
+    }
+    
+    /** @see java.lang.Object#toString() */
+    public String toString() {
+        StringBuffer sb = new StringBuffer(64);
+        if (isAuxiliary()) {
+            sb.append("aux. ");
+        }
+        sb.append("glue");
+        sb.append(" w=").append(getW());
+        sb.append(" stretch=").append(getY());
+        sb.append(" shrink=").append(getZ());
+        if (getAdjustmentClass() >= 0) {
+            sb.append(" adj-class=").append(getAdjustmentClass());
+        }
+        return sb.toString();
+    }
+    
 }
diff --git a/src/java/org/apache/fop/layoutmgr/KnuthInlineBox.java b/src/java/org/apache/fop/layoutmgr/KnuthInlineBox.java
new file mode 100644 (file)
index 0000000..cca2bbb
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+public class KnuthInlineBox extends KnuthBox {
+    
+    private int lead;
+    private int total;
+    private int middle;
+
+    /**
+     * Create a new KnuthBox.
+     *
+     * @param w    the width of this box
+     * @param l    the height of this box above the main baseline
+     * @param t    the total height of this box
+     * @param m    the height of this box above and below the middle baseline
+     * @param pos  the Position stored in this box
+     * @param bAux is this box auxiliary?
+     */
+    public KnuthInlineBox(int w, int l, int t, int m, Position pos, boolean bAux) {
+        super(w, pos, bAux);
+        lead = l;
+        total = t;
+        middle = m;
+    }
+
+    /**
+     * @return the height of this box above the main baseline.
+     */
+    public int getLead() {
+        return lead;
+    }
+
+    /**
+     * @return the total height of this box.
+     */
+    public int getTotal() {
+        return total;
+    }
+
+    /**
+     * @return the height of this box above and below the middle baseline.
+     */
+    public int getMiddle() {
+        return middle;
+    }
+}
\ No newline at end of file
index baf3492d67e8c720ceb971ef670427d83c8e4a77..cf08b631553109195acd5535ae60586e1a38502c 100644 (file)
  */
 
 /* $Id$ */
+
 package org.apache.fop.layoutmgr;
 
-import java.util.ArrayList;
+import java.util.List;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -60,7 +61,7 @@ public class KnuthParagraph {
     /**
      * The paragraph of KnuthElements.
      */
-    private ArrayList par;
+    private List par;
     
     /**
      * The width of a line.
@@ -114,7 +115,7 @@ public class KnuthParagraph {
     
     protected static Log log = LogFactory.getLog(KnuthParagraph.class);
     
-    public KnuthParagraph(ArrayList par) {
+    public KnuthParagraph(List par) {
         this.best = new BestRecords();
         this.par = par;
     }
index 83b01313205973f08ba22e753a810c219e13d94a..e0b528867685212683b53a1b540697c0ec8807db 100644 (file)
@@ -37,8 +37,12 @@ package org.apache.fop.layoutmgr;
  * be chosen as breaking points for consecutive lines.
  */
 public class KnuthPenalty extends KnuthElement {
+
+    public static final int FLAGGED_PENALTY = 50;
+
     private int penalty;
     private boolean bFlagged; 
+    private int breakClass = -1;
 
     /**
      * Create a new KnuthPenalty.
@@ -55,6 +59,14 @@ public class KnuthPenalty extends KnuthElement {
         bFlagged = f;
     }
 
+    public KnuthPenalty(int w, int p, boolean f,
+            int iBreakClass, Position pos, boolean bAux) {
+        super(w, pos, bAux);
+        penalty = p;
+        bFlagged = f;
+        breakClass = iBreakClass;
+    }
+
     public boolean isPenalty() {
         return true;
     }
@@ -76,4 +88,36 @@ public class KnuthPenalty extends KnuthElement {
     public boolean isForcedBreak() {
         return penalty == -KnuthElement.INFINITE;
     }
+    
+    public int getBreakClass() {
+        return breakClass;
+    }
+    
+    /** @see java.lang.Object#toString() */
+    public String toString() {
+        StringBuffer sb = new StringBuffer(64);
+        if (isAuxiliary()) {
+            sb.append("aux. ");
+        }
+        sb.append("penalty");
+        sb.append(" p=");
+        if (getP() < 0) {
+            sb.append("-");
+        }
+        if (Math.abs(getP()) == INFINITE) {
+            sb.append("INFINITE");
+        } else {
+            sb.append(getP());
+        }
+        if (isFlagged()) {
+            sb.append(" [flagged]");
+        }
+        sb.append(" w=");
+        sb.append(getW());
+        if (isForcedBreak()) {
+            sb.append(" (forced break)");
+        }
+        return sb.toString();
+    }
+    
 }
index 1b6a03cf13d987e4d9b230f992e2160b7bd75db7..0b157c0504af7bcc80428b1eefd534f6165b4285 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2004 The Apache Software Foundation.
+ * Copyright 2004-2005 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -26,14 +26,22 @@ public class KnuthPossPosIter extends PositionIterator {
 
     /**
      * Main constructor
-     * @param bpList List of break possibilities
+     * @param elementList List of Knuth elements
      * @param startPos starting position
      * @param endPos ending position
      */
-    public KnuthPossPosIter(List bpList, int startPos, int endPos) {
-        super(bpList.listIterator(startPos));
+    public KnuthPossPosIter(List elementList, int startPos, int endPos) {
+        super(elementList.listIterator(startPos));
         iterCount = endPos - startPos;
     }
+    
+    /**
+     * Auxiliary constructor
+     * @param elementList List of Knuth elements
+     */
+    public KnuthPossPosIter(List elementList) {
+        this(elementList, 0, elementList.size());
+    }
 
     // Check position < endPos
     
diff --git a/src/java/org/apache/fop/layoutmgr/KnuthSequence.java b/src/java/org/apache/fop/layoutmgr/KnuthSequence.java
new file mode 100644 (file)
index 0000000..55465f8
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import java.util.ArrayList;
+
+/**
+ * Represents a list of Knuth elements.
+ */
+public class KnuthSequence extends ArrayList {
+    /** Number of elements to ignore at the beginning of the list. */ 
+    public int ignoreAtStart = 0;
+    /** Number of elements to ignore at the end of the list. */
+    public int ignoreAtEnd = 0;
+
+    /**
+     * Creates a new and empty list.
+     */
+    public KnuthSequence() {
+        super();
+    }
+
+    /**
+     * Marks the start of the sequence.
+     */
+    public void startSequence() {
+    }
+
+    /**
+     * @return a finalized sequence.
+     */
+    public KnuthSequence endSequence() {
+        // remove glue and penalty item at the end of the paragraph
+        while (this.size() > ignoreAtStart
+               && !((KnuthElement)this.get(this.size() - 1)).isBox()) {
+            this.remove(this.size() - 1);
+        }
+        if (this.size() > ignoreAtStart) {
+            // add the elements representing the space at the end of the last line
+            // and the forced break
+            this.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, null, false));
+            this.add(new KnuthGlue(0, 10000000, 0, null, false));
+            this.add(new KnuthPenalty(0, -KnuthElement.INFINITE, false, null, false));
+            ignoreAtEnd = 3;
+            return this;
+        } else {
+            this.clear();
+            return null;
+        }
+    }
+
+    public KnuthElement getLast() {
+        int idx = size();
+        if (idx == 0) {
+            return null; 
+        }
+        return (KnuthElement) get(idx - 1);
+    }
+
+    public KnuthElement removeLast() {
+        int idx = size();
+        if (idx == 0) {
+            return null; 
+        }
+        return (KnuthElement) remove(idx - 1);
+    }
+}
index a6f41d5c3dcbf527aaa7b9e798e720cedfadfbaf..6452f75bcb8dd288e9e55cdb061582102d8e0508 100644 (file)
@@ -87,6 +87,10 @@ public class LayoutContext {
     private int iLineHeight;
     private int iBaseline;
     private int iMiddleShift;
+    private int iTopShift; /*LF*/
+    private int iBottomShift; /*LF*/
+    private int iSpaceBefore; /*LF*/
+    private int iSpaceAfter; /*LF*/
 
     public LayoutContext(LayoutContext parentLC) {
         this.flags = parentLC.flags;
@@ -100,6 +104,10 @@ public class LayoutContext {
         this.iLineHeight = parentLC.iLineHeight;
         this.iBaseline = parentLC.iBaseline;
         this.iMiddleShift = parentLC.iMiddleShift;
+/*LF*/  this.iTopShift = parentLC.iTopShift;
+/*LF*/  this.iBottomShift = parentLC.iBottomShift;
+/*LF*/  this.iSpaceBefore = parentLC.iSpaceBefore;
+/*LF*/  this.iSpaceAfter = parentLC.iSpaceAfter;
         // Copy other fields as necessary. Use clone???
     }
 
@@ -235,6 +243,38 @@ public class LayoutContext {
         return iBaseline + iMiddleShift;
     }
     
+    public void setTopShift(int ts) {
+        iTopShift = ts;
+    }
+
+    public int getTopBaseline() {
+        return iBaseline + iTopShift;
+    }
+
+    public void setBottomShift(int bs) {
+        iBottomShift = bs;
+    }
+
+    public int getBottomBaseline() {
+        return iBaseline + iBottomShift;
+    }
+
+    public int getSpaceBefore() {
+        return iSpaceBefore;
+    }
+    
+    public void setSpaceBefore(int sp) {
+        iSpaceBefore = sp;
+    }
+
+    public int getSpaceAfter() {
+        return iSpaceAfter;
+    }
+
+    public void setSpaceAfter(int sp) {
+        iSpaceAfter = sp;
+    }
+    
     public String toString() {
         return "Layout Context:" +
         "\nStack Limit: \t" + (getStackLimit() == null ? "null" : getStackLimit().toString()) +
index 93fd36df572e4db3f4509728ccd0de023abadf8b..6c3187117b36c0d41aaa96452460f9dc319186ec 100644 (file)
  
 package org.apache.fop.layoutmgr;
 
+import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
-
-import org.apache.fop.fo.flow.Marker;
 
 import org.apache.fop.area.Area;
-import org.apache.fop.area.Resolvable;
-import org.apache.fop.area.PageViewport;
-import org.apache.fop.area.AreaTreeHandler;
-import org.apache.fop.fo.FObj;
 
 /**
  * The interface for all LayoutManagers.
  */
 public interface LayoutManager {
 
-    /**
-     * Set the FO object for this layout manager.
-     * For layout managers that are created without an FO
-     * this may not be called.
-     *
-     * @param obj the FO object for this layout manager
-     */
-    void setFObj(FObj obj);
-
     /**
      * Set the parent layout manager.
      * The parent layout manager is required for adding areas.
@@ -62,6 +47,12 @@ public interface LayoutManager {
      */
     void initialize();
 
+    /**
+     * Get the active PageSequenceLayoutManager instance for this
+     * layout process.
+     */
+    PageSequenceLayoutManager getPSLM();
+
     /**
      * Generates inline areas.
      * This is used to check if the layout manager generates inline
@@ -167,68 +158,6 @@ public interface LayoutManager {
      */
     void addAreas(PositionIterator posIter, LayoutContext context);
 
-    /**
-     * Get the string of the current page number.
-     *
-     * @return the string for the current page number
-     */
-    String getCurrentPageNumberString();
-
-    /**
-     * Resolve the id reference.
-     * This is called by an area looking for an id reference.
-     * If the id reference is not found then it should add a resolvable object.
-     *
-     * @param ref the id reference
-     * @return the page containing the id reference or null if not found
-     */
-    PageViewport resolveRefID(String ref);
-
-    /**
-     * Add an id to the page.
-     * (todo) add the location of the area on the page
-     *
-     * @param id the id reference to add.
-     */
-    void addIDToPage(String id);
-
-    /**
-     * Add an unresolved area.
-     * The is used to add a resolvable object to the page for a given id.
-     *
-     * @param id the id reference this object needs for resolving
-     * @param res the resolvable object
-     */
-    void addUnresolvedArea(String id, Resolvable res);
-
-    /**
-     * Add the marker.
-     * A number of formatting objects may contain markers. This
-     * method is used to add those markers to the page.
-     *
-     * @param name the marker class name
-     * @param starting if the area being added is starting or ending
-     * @param isfirst if the area being added has is-first trait
-     * @param islast if the area being added has is-last trait
-     */
-    void addMarkerMap(Map marks, boolean starting, boolean isfirst, boolean islast);
-
-    /**
-     * Retrieve a marker.
-     * This method is used when retrieve a marker.
-     *
-     * @param name the class name of the marker
-     * @param pos the retrieve position
-     * @param boundary the boundary for retrieving the marker
-     * @return the layout manaager of the retrieved marker if any
-     */
-    Marker retrieveMarker(String name, int pos, int boundary);
-
-    /**
-     * @return the AreaTreeHandler object.
-     */
-    AreaTreeHandler getAreaTreeHandler();
-
     /**
      * Load next child LMs, up to child LM index pos
      * @param pos index up to which child LMs are requested
@@ -254,4 +183,27 @@ public interface LayoutManager {
      * @param newLMs the list of LMs to be added
      */
     void addChildLMs(List newLMs);
+
+    /**
+     * Get a sequence of KnuthElements representing the content 
+     * of the node assigned to the LM
+     * 
+     * @param context   the LayoutContext used to store layout information
+     * @param alignment the desired text alignement
+     * @return          the list of KnuthElements
+     */
+    LinkedList getNextKnuthElements(LayoutContext context, int alignment);
+
+    /**
+     * Get a sequence of KnuthElements representing the content 
+     * of the node assigned to the LM, after changes have been applied
+     *
+     * @param oldList        the elements to replace
+     * @param flaggedPenalty the penalty value for hyphenated lines
+     * @param alignment      the desired text alignment
+     * @return               the updated list of KnuthElements
+     */
+    LinkedList getChangedKnuthElements(List oldList, /*int flaggedPenalty,*/
+                                       int alignment);
+   
 }
index 43b62d6c6468037ebbe94eaa0bd09fc1521fe822..b0d72344461c7eeceba733e988e2916d8b03c5ab 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2004 The Apache Software Foundation.
+ * Copyright 2004-2005 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -19,7 +19,9 @@ package org.apache.fop.layoutmgr;
 
 import java.util.List;
 import org.apache.fop.fo.FONode;
-import org.apache.fop.apps.FOPException;
+import org.apache.fop.fo.pagination.PageSequence;
+import org.apache.fop.area.AreaTreeHandler;
+
 
 /**
  * The interface for all LayoutManager makers
@@ -34,14 +36,24 @@ public interface LayoutManagerMaker {
     public void makeLayoutManagers(FONode node, List lms);
 
     /**
-     * Make the LayoutManager for the node.
-     * If not exactly one LayoutManagers is made,
-     * a FOPException is thrown.
+     * Make a specific LayoutManager for the node.
+     * If not exactly one LayoutManagers is available,
+     * an IllegalStateException is thrown.
      * @param node the FO node for which the LayoutManagers are made
      * @return The created LayoutManager
+     * @throws IllegalStateException if not exactly one
+     *    LayoutManager is available for the requested node
+     */
+    public LayoutManager makeLayoutManager(FONode node);
+
+    /**
+     * Make a PageSequenceLayoutManager object.
+     * @param ath the AreaTreeHandler object the PSLM interacts with
+     * @param ps the fo:page-sequence object this PSLM will process
+     * @return The created PageSequenceLayoutManager object
      */
-    public LayoutManager makeLayoutManager(FONode node)
-        throws FOPException;
+    public PageSequenceLayoutManager makePageSequenceLayoutManager(
+        AreaTreeHandler ath, PageSequence ps);
 
 }
 
index 85bc59c9646999a5d1ddc5b59d7dd9ba49d9cae9..4b235c09d969f805daad0a86a4792dae4f4242fe 100644 (file)
@@ -21,14 +21,11 @@ import java.util.ArrayList;
 import java.util.Map;
 import java.util.HashMap;
 import java.util.List;
-import java.util.ListIterator;
 import java.util.Iterator;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
-import org.apache.fop.apps.FOPException;
-
 import org.apache.fop.fo.FONode;
 import org.apache.fop.fo.FOText;
 import org.apache.fop.fo.FObjMixed;
@@ -61,13 +58,14 @@ import org.apache.fop.fo.pagination.Flow;
 import org.apache.fop.fo.pagination.PageSequence;
 import org.apache.fop.fo.pagination.StaticContent;
 import org.apache.fop.fo.pagination.Title;
+import org.apache.fop.area.AreaTreeHandler;
 
 import org.apache.fop.layoutmgr.list.ListBlockLayoutManager;
 import org.apache.fop.layoutmgr.list.ListItemLayoutManager;
-import org.apache.fop.layoutmgr.table.Body;
+/*import org.apache.fop.layoutmgr.table.Body;
 import org.apache.fop.layoutmgr.table.Cell;
 import org.apache.fop.layoutmgr.table.Column;
-import org.apache.fop.layoutmgr.table.Row;
+import org.apache.fop.layoutmgr.table.Row;*/
 import org.apache.fop.layoutmgr.table.TableLayoutManager;
 
 /**
@@ -88,7 +86,7 @@ public class LayoutManagerMapping implements LayoutManagerMaker {
     /**
      * Initializes the set of maker objects associated with this LayoutManagerMapping
      */
-    private void initialize() {
+    protected void initialize() {
         makers.put(FOText.class, new FOTextLayoutManagerMaker());
         makers.put(FObjMixed.class, new Maker());
         makers.put(BidiOverride.class, new BidiOverrideLayoutManagerMaker());
@@ -112,14 +110,13 @@ public class LayoutManagerMapping implements LayoutManagerMaker {
         makers.put(PageNumber.class, new PageNumberLayoutManagerMaker());
         makers.put(PageNumberCitation.class,
                    new PageNumberCitationLayoutManagerMaker());
-        makers.put(PageSequence.class, new PageSequenceLayoutManagerMaker());
         makers.put(Table.class, new TableLayoutManagerMaker());
-        makers.put(TableBody.class, new TableBodyLayoutManagerMaker());
-        makers.put(TableColumn.class, new TableColumnLayoutManagerMaker());
-        makers.put(TableRow.class, new TableRowLayoutManagerMaker());
-        makers.put(TableCell.class, new TableCellLayoutManagerMaker());
-        makers.put(TableFooter.class, new TableBodyLayoutManagerMaker());
-        makers.put(TableHeader.class, new TableBodyLayoutManagerMaker());
+        makers.put(TableBody.class, new /*TableBodyLayoutManager*/Maker());
+        makers.put(TableColumn.class, new /*TableColumnLayoutManager*/Maker());
+        makers.put(TableRow.class, new /*TableRowLayoutManager*/Maker());
+        makers.put(TableCell.class, new /*TableCellLayoutManager*/Maker());
+        makers.put(TableFooter.class, new /*TableBodyLayoutManager*/Maker());
+        makers.put(TableHeader.class, new /*TableBodyLayoutManager*/Maker());
         makers.put(Flow.class, new FlowLayoutManagerMaker());
         makers.put(StaticContent.class, new StaticContentLayoutManagerMaker());
         makers.put(Wrapper.class, new WrapperLayoutManagerMaker());
@@ -141,22 +138,26 @@ public class LayoutManagerMapping implements LayoutManagerMaker {
     /**
      * @see org.apache.fop.layoutmgr.LayoutManagerMaker#makeLayoutManager(FONode)
      */
-    public LayoutManager makeLayoutManager(FONode node)
-        throws FOPException {
+    public LayoutManager makeLayoutManager(FONode node) {
         List lms = new ArrayList();
         makeLayoutManagers(node, lms);
         if (lms.size() == 0) {
-            throw new FOPException("No LayoutManager for class "
+            throw new IllegalStateException("LayoutManager for class "
                                    + node.getClass()
-                                   + "; 1 was required");
+                                   + " is missing.");
         } else if (lms.size() > 1) {
-            throw new FOPException("More than 1 LayoutManager for class "
+            throw new IllegalStateException("Duplicate LayoutManagers for class "
                                    + node.getClass()
-                                   + "; 1 was required"); 
+                                   + " found, only one may be declared."); 
         }
         return (LayoutManager) lms.get(0);
     }
 
+    public PageSequenceLayoutManager makePageSequenceLayoutManager(
+        AreaTreeHandler ath, PageSequence ps) {
+        return new PageSequenceLayoutManager(ath, ps);
+    }
+
     public static class Maker {
         public void make(FONode node, List lms) {
             // no layout manager
@@ -305,14 +306,9 @@ public class LayoutManagerMapping implements LayoutManagerMaker {
          }
     }
 
-    public static class PageSequenceLayoutManagerMaker extends Maker {
-         public void make(FONode node, List lms) {
-             lms.add(new PageSequenceLayoutManager((PageSequence) node));
-         }
-    }
-
     public static class TableLayoutManagerMaker extends Maker {
         
+        /*
         private List getColumnLayoutManagerList(Table table, TableLayoutManager tlm) {
             List columnLMs = null;
             List columns = table.getColumns();
@@ -347,19 +343,21 @@ public class LayoutManagerMapping implements LayoutManagerMaker {
                 }
             }
             return columnLMs;
-        }
+        }*/
         
         public void make(FONode node, List lms) {
             Table table = (Table) node;
             TableLayoutManager tlm = new TableLayoutManager(table);
+            /*
             List columnLMs = getColumnLayoutManagerList(table, tlm);
             if (columnLMs != null) {
                 tlm.setColumns(columnLMs);
-            }
+            }*/
             lms.add(tlm);
         }
     }
-        
+     
+    /*
     public static class TableBodyLayoutManagerMaker extends Maker {
          public void make(FONode node, List lms) {
              lms.add(new Body((TableBody) node));
@@ -383,7 +381,7 @@ public class LayoutManagerMapping implements LayoutManagerMaker {
         public void make(FONode node, List lms) {
             lms.add(new Cell((TableCell) node));
         }
-    }
+    }*/
 
     public static class FlowLayoutManagerMaker extends Maker {
          public void make(FONode node, List lms) {
index 4cb87a41955df8e33d957f10445e9f1799d69a59..32859279454ddd88dd59b938141e0154715ed3ce 100644 (file)
@@ -263,7 +263,7 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
                                 lead, total, middle);
 
         // node is a fo:Leader
-        returnList.add(new KnuthBox(0, areaInfo.lead, areaInfo.total,
+        returnList.add(new KnuthInlineBox(0, areaInfo.lead, areaInfo.total,
                                     areaInfo.middle,
                                     new LeafPosition(this, -1), true));
         returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
@@ -273,7 +273,7 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
                            areaInfo.ipdArea.max - areaInfo.ipdArea.opt,
                            areaInfo.ipdArea.opt - areaInfo.ipdArea.min, 
                            new LeafPosition(this, 0), false));
-        returnList.add(new KnuthBox(0, areaInfo.lead, areaInfo.total,
+        returnList.add(new KnuthInlineBox(0, areaInfo.lead, areaInfo.total,
                                     areaInfo.middle,
                                     new LeafPosition(this, -1), true));
 
@@ -308,7 +308,7 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
 
         LinkedList returnList = new LinkedList();
 
-        returnList.add(new KnuthBox(0, areaInfo.lead, areaInfo.total,
+        returnList.add(new KnuthInlineBox(0, areaInfo.lead, areaInfo.total,
                                     areaInfo.middle,
                                     new LeafPosition(this, -1), true));
         returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false,
@@ -318,7 +318,7 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
                            areaInfo.ipdArea.max - areaInfo.ipdArea.opt,
                            areaInfo.ipdArea.opt - areaInfo.ipdArea.min, 
                            new LeafPosition(this, 0), false));
-        returnList.add(new KnuthBox(0, areaInfo.lead, areaInfo.total,
+        returnList.add(new KnuthInlineBox(0, areaInfo.lead, areaInfo.total,
                                     areaInfo.middle,
                                     new LeafPosition(this, -1), true));
 
@@ -327,6 +327,6 @@ public class LeaderLayoutManager extends LeafNodeLayoutManager {
     }
 
     protected void addId() {
-        addID(fobj.getId());
+        getPSLM().addIDToPage(fobj.getId());
     }
 }
index b81adc6861e2dc6bc824eb207d116abc19b3f59b..a3dcba1114faafbea9c0fd2d828dce416ca73e80 100644 (file)
@@ -77,6 +77,12 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager
         super(node);
     }
 
+    /**
+     * Create a Leaf node layout mananger.
+     */
+    public LeafNodeLayoutManager() {
+    }
+
     /**
      * get the inline area.
      * @param context the context used to create the area
@@ -194,10 +200,10 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager
                 curArea.setOffset(context.getMiddleBaseline() - bpd / 2);
             break;
             case EN_TOP:
-                //curArea.setOffset(0);
+                curArea.setOffset(context.getTopBaseline());
             break;
             case EN_BOTTOM:
-                curArea.setOffset(context.getLineHeight() - bpd);
+                curArea.setOffset(context.getBottomBaseline() - bpd);
             break;
             case EN_BASELINE:
             default:
@@ -269,21 +275,19 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager
 
         // node is a fo:ExternalGraphic, fo:InstreamForeignObject,
         // fo:PageNumber or fo:PageNumberCitation
-        returnList.add(new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
+        returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
                                     areaInfo.total, areaInfo.middle,
                                     new LeafPosition(this, 0), false));
         setFinished(true);
         return returnList;
     }
 
-    public void getWordChars(StringBuffer sbChars, Position bp) {
+    public List addALetterSpaceTo(List oldList) {
+        // return the unchanged elements
+        return oldList;
     }
 
-    public KnuthElement addALetterSpaceTo(KnuthElement element) {
-        // return the unchanged box object
-        return new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
-                            areaInfo.total, areaInfo.middle,
-                            new LeafPosition(this, 0), false);
+    public void getWordChars(StringBuffer sbChars, Position pos) {
     }
 
     public void hyphenate(Position pos, HyphContext hc) {
@@ -295,7 +299,7 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager
     }
 
     public LinkedList getChangedKnuthElements(List oldList,
-                                              int flaggedPenalty,
+                                              /*int flaggedPenalty,*/
                                               int alignment) {
         if (isFinished()) {
             return null;
@@ -305,9 +309,9 @@ public class LeafNodeLayoutManager extends AbstractLayoutManager
 
         // fobj is a fo:ExternalGraphic, fo:InstreamForeignObject,
         // fo:PageNumber or fo:PageNumberCitation
-        returnList.add(new KnuthBox(areaInfo.ipdArea.opt, areaInfo.lead,
-                                    areaInfo.total, areaInfo.middle,
-                                    new LeafPosition(this, 0), true));
+        returnList.add(new KnuthInlineBox(areaInfo.ipdArea.opt, areaInfo.lead,
+                                          areaInfo.total, areaInfo.middle,
+                                          new LeafPosition(this, 0), true));
 
         setFinished(true);
         return returnList;
index e21d2e5b81f3358efe2a2fb3b235aeaca8fbac98..0f72ef124c964cd585cab072936be41c0394c04f 100644 (file)
@@ -25,7 +25,6 @@ import org.apache.fop.fo.properties.CommonHyphenation;
 import org.apache.fop.hyphenation.Hyphenation;
 import org.apache.fop.hyphenation.Hyphenator;
 import org.apache.fop.area.LineArea;
-import org.apache.fop.area.Resolvable;
 
 import java.util.ListIterator;
 import java.util.Iterator;
@@ -46,31 +45,11 @@ import org.apache.fop.traits.MinOptMax;
  * creates a line area to contain the inline areas added by the
  * child layout managers.
  */
-public class LineLayoutManager extends InlineStackingLayoutManager {
+public class LineLayoutManager extends InlineStackingLayoutManager 
+                               implements BlockLevelLayoutManager {
+
     private Block fobj; 
     
-    /**
-     * Create a new Line Layout Manager.
-     * This is used by the block layout manager to create
-     * line managers for handling inline areas flowing into line areas.
-     *
-     * @param lh the default line height
-     * @param l the default lead, from top to baseline
-     * @param f the default follow, from baseline to bottom
-     */
-    public LineLayoutManager(Block node, int lh, int l, int f, int ms) {
-        super(node);
-        fobj = node;
-        // the child FObj are owned by the parent BlockLM
-        // this LM has all its childLMs preloaded
-        fobjIter = null;
-        lineHeight = lh;
-        lead = l;
-        follow = f;
-        middleShift = ms;
-        initialize(); // Normally done when started by parent!
-    }
-
     /**
      * @see org.apache.fop.layoutmgr.AbstractLayoutManager#initProperties()
      */
@@ -96,36 +75,51 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
     private static class LineBreakPosition extends LeafPosition {
         // int iPos;
         int iParIndex; // index of the Paragraph this Position refers to
+        int availableShrink;
+        int availableStretch;
+        int difference;
         double dAdjust; // Percentage to adjust (stretch or shrink)
         double ipdAdjust; // Percentage to adjust (stretch or shrink)
         int startIndent;
         int lineHeight;
+        int lineWidth;
         int baseline;
+        int topShift;
+        int bottomShift;
 
         LineBreakPosition(LayoutManager lm, int index, int iBreakIndex,
-                          double ipdA, double adjust, int ind, int lh, int bl) {
+                          int shrink, int stretch, int diff,
+                          double ipdA, double adjust, int ind,
+                          int lh, int lw, int bl, int ts, int bs) {
             super(lm, iBreakIndex);
-            // iPos = iBreakIndex;
+            availableShrink = shrink;
+            availableStretch = stretch;
+            difference = diff;
             iParIndex = index;
             ipdAdjust = ipdA;
             dAdjust = adjust;
             startIndent = ind;
             lineHeight = lh;
+            lineWidth = lw;
             baseline = bl;
+            topShift = ts;
+            bottomShift = bs;
         }
         
     }
 
 
     /** Break positions returned by inline content. */
-    private List vecInlineBreaks = new ArrayList();
+    private List vecInlineBreaks = new java.util.ArrayList();
 
     private BreakPoss prevBP = null; // Last confirmed break position
     private int bTextAlignment = EN_JUSTIFY;
     private int bTextAlignmentLast;
     private int effectiveAlignment;
     private Length textIndent;
+    private int iIndents = 0;
     private CommonHyphenation hyphProps;
+    //private LayoutProps layoutProps;
 
     private int lineHeight;
     private int lead;
@@ -133,8 +127,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
     // offset of the middle baseline with respect to the main baseline
     private int middleShift;
 
-    private ArrayList knuthParagraphs = null;
-    private ArrayList breakpoints = null;
+    private List knuthParagraphs = null;
+    private List breakpoints = null;
     private int iReturnedLBP = 0;
     private int iStartElement = 0;
     private int iEndElement = 0;
@@ -143,6 +137,10 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
     // penalty value for flagged penalties
     private int flaggedPenalty = 50;
 
+    private LineLayoutPossibilities lineLayouts;
+    private List lineLayoutsList;
+    private int iLineWidth = 0;
+
     // this constant is used to create elements when text-align is center:
     // every TextLM descendant of LineLM must use the same value, 
     // otherwise the line breaking algorithm does not find the right
@@ -164,20 +162,37 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
     }
 
     // this class represents a paragraph
-    public class Paragraph extends ArrayList {
-        // number of KnuthElements added by the LineLayoutManager
-        public int ignoreAtStart = 0;
-        public int ignoreAtEnd = 0;
-        // minimum space at the end of the last line (in millipoints)
-        public int lineFillerWidth;
-
-        public void startParagraph(int lineWidth) {
+    private class Paragraph extends KnuthSequence {
+        // space at the end of the last line (in millipoints)
+        private MinOptMax lineFiller;
+        private int textAlignment;
+        private int textAlignmentLast;
+        private int textIndent;
+        private int lineWidth;
+        // the LM which created the paragraph
+        private LineLayoutManager layoutManager;
+
+        public Paragraph(LineLayoutManager llm, int alignment, int alignmentLast,
+                         int indent) {
+            super();
+            layoutManager = llm;
+            textAlignment = alignment;
+            textAlignmentLast = alignmentLast;
+            textIndent = indent;
+        }
+
+        public void startParagraph(int lw) {
+            lineWidth = lw;
+            startSequence();
+        }
+
+        public void startSequence() {
             // set the minimum amount of empty space at the end of the
             // last line
             if (bTextAlignment == EN_CENTER) {
-                lineFillerWidth = 0
+                lineFiller = new MinOptMax(0)
             } else {
-                lineFillerWidth = (int)(lineWidth / 12); 
+                lineFiller = new MinOptMax(0, (int)(lineWidth / 12), lineWidth); 
             }
 
             // add auxiliary elements at the beginning of the paragraph
@@ -190,17 +205,24 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
             // add the element representing text indentation
             // at the beginning of the first paragraph
             if (knuthParagraphs.size() == 0
-                && textIndent.getValue() != 0) {
-                this.add(new KnuthBox(textIndent.getValue(), 0, 0, 0,
+                        && fobj.getTextIndent().getValue() != 0) {
+                this.add(new KnuthInlineBox(fobj.getTextIndent().getValue(), 0, 0, 0,
                                       null, false));
                 ignoreAtStart ++;
             }
         }
 
         public void endParagraph() {
+            KnuthSequence finishedPar = this.endSequence();
+            if (finishedPar != null) {
+                knuthParagraphs.add(finishedPar);
+            }
+        }
+
+        public KnuthSequence endSequence() {
             // remove glue and penalty item at the end of the paragraph
             while (this.size() > ignoreAtStart
-                   && !((KnuthElement) this.get(this.size() - 1)).isBox()) {
+                   && !((KnuthElement)this.get(this.size() - 1)).isBox()) {
                 this.remove(this.size() - 1);
             }
             if (this.size() > ignoreAtStart) {
@@ -215,10 +237,11 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
                     // add the elements representing the space
                     // at the end of the last line
                     // and the forced break
-                    this.add(new KnuthPenalty(0, KnuthElement.INFINITE,
+                    this.add(new KnuthPenalty(0, KnuthElement.INFINITE, 
                                               false, null, false));
-                    this.add(new KnuthGlue(lineFillerWidth, 10000000, 0,
-                                           null, false));
+                    this.add(new KnuthGlue(lineFiller.opt, 
+                            lineFiller.max - lineFiller.opt, 
+                            lineFiller.opt - lineFiller.min, null, false));
                     this.add(new KnuthPenalty(0, -KnuthElement.INFINITE,
                                               false, null, false));
                     ignoreAtEnd = 3;
@@ -228,27 +251,237 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
                                               false, null, false));
                     ignoreAtEnd = 1;
                 }
-                knuthParagraphs.add(this);
+                return this;
+            } else {
+                this.clear();
+                return null;
             }
         }
 
-        public KnuthElement getLast() {
-            int idx = size();
-            if (idx == 0) {
-                return null; 
+    }
+
+    private class LineBreakingAlgorithm extends BreakingAlgorithm {
+        private LineLayoutManager thisLLM;
+        private int pageAlignment;
+        private int activePossibility;
+        private int addedPositions;
+        private int textIndent;
+        private int fillerMinWidth;
+        private int lineHeight;
+        private int lead;
+        private int follow;
+        private int middleshift;
+        private int maxDiff;
+        private static final double MAX_DEMERITS = 10e6;
+
+        public LineBreakingAlgorithm (int pageAlign,
+                                      int textAlign, int textAlignLast,
+                                      int indent, int fillerWidth,
+                                      int lh, int ld, int fl, int ms, boolean first,
+                                      LineLayoutManager llm) {
+            super(textAlign, textAlignLast, first);
+            pageAlignment = pageAlign;
+            textIndent = indent;
+            fillerMinWidth = fillerWidth;
+            lineHeight = lh;
+            lead = ld;
+            follow = fl;
+            middleshift = ms;
+            thisLLM = llm;
+            activePossibility = -1;
+            maxDiff = fobj.getWidows() >= fobj.getOrphans() 
+                    ? fobj.getWidows()
+                    : fobj.getOrphans();
+        }
+
+        public void updateData1(int 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.line < total) ? bestActiveNode.difference : bestActiveNode.difference + fillerMinWidth;
+            int textAlign = (bestActiveNode.line < total) ? alignment : alignmentLast;
+            indent += (textAlign == Constants.EN_CENTER) ?
+                          difference / 2 :
+                          (textAlign == Constants.EN_END) ? difference : 0;
+            indent += (bestActiveNode.line == 1 && bFirst) ? 
+                          textIndent : 0;
+            double ratio = (textAlign == Constants.EN_JUSTIFY
+                            || bestActiveNode.adjustRatio < 0) ? bestActiveNode.adjustRatio : 0;
+
+            // add nodes at the beginning of the list, as they are found
+            // backwards, from the last one to the first one
+
+            // the first time this method is called, initialize activePossibility
+            if (activePossibility == -1) {
+                activePossibility = 0;
+                addedPositions = 0;
+            }
+
+            if (addedPositions == lineLayouts.getLineCount(activePossibility)) {
+                activePossibility ++;
+                addedPositions = 0;
+                //System.out.println(" ");
+            }
+
+            //System.out.println("LLM> (" + (lineLayouts.getLineNumber(activePossibility) - addedPositions) + ") difference = " + difference + " ratio = " + ratio);
+            lineLayouts.addBreakPosition(makeLineBreakPosition(par,
+                                                               (bestActiveNode.line > 1 ? bestActiveNode.previous.position + 1: 0),
+                                                               bestActiveNode.position,
+                                                               bestActiveNode.availableShrink - (addedPositions > 0 ? 0 : ((Paragraph)par).lineFiller.opt - ((Paragraph)par).lineFiller.min), bestActiveNode.availableStretch, difference, ratio, indent),
+                                         activePossibility);
+            addedPositions ++;
+        }
+
+        /* 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
+
+            int halfLeading = (lineHeight - lead - follow) / 2;
+            // height before the main baseline
+            int lineLead = lead;
+            // maximum size of top and bottom alignment
+            int maxtb = follow;
+            // max size of middle alignment before and after the middle baseline
+            int middlefollow = maxtb;
+
+            // 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);
+                for (int j = firstElementIndex;
+                     j <= lastElementIndex;
+                     j++) {
+                    KnuthElement element = (KnuthElement) inlineIterator.next();
+                    if (element.isBox()) {
+                        if (((KnuthInlineBox) element).getLead() > lineLead) {
+                            lineLead = ((KnuthInlineBox) element).getLead();
+                        }
+                        if (((KnuthInlineBox) element).getTotal() > maxtb) {
+                            maxtb = ((KnuthInlineBox) element).getTotal();
+                        }
+                        if (((KnuthInlineBox) element).getMiddle() > lineLead + middleShift) {
+                            lineLead += ((KnuthInlineBox) element).getMiddle()
+                                        - lineLead - middleShift;
+                        }
+                        if (((KnuthInlineBox) element).getMiddle() > middlefollow - middleShift) {
+                            middlefollow += ((KnuthInlineBox) element).getMiddle()
+                                            - middlefollow + middleShift;
+                        }
+                    }
+                }
+
+                if (maxtb - lineLead > middlefollow) {
+                    middlefollow = maxtb - lineLead;
+                }
             }
-            return (KnuthElement) get(idx - 1);
+
+            //lineLead += halfLeading;
+            //middlefollow += lineHeight - lead - follow - halfLeading;
+
+            constantLineHeight = lineLead + middlefollow + (lineHeight - lead - follow);
+            //System.out.println("desired height: " + lineHeight + " actual height: " + (lineLead + middlefollow + (lineHeight - lead - follow)) + " halfleading = " + halfLeading + " and " + (lineHeight - lead - follow - halfLeading));
+
+            return new LineBreakPosition(thisLLM,
+                                         knuthParagraphs.indexOf(par),
+                                         lastElementIndex,
+                                         availableShrink, availableStretch, difference, ratio, 0, indent,
+                                         lineLead + middlefollow + (lineHeight - lead - follow), iLineWidth,
+                                         lineLead + halfLeading,
+                                         - lineLead, middlefollow);
+        }
+
+        public int findBreakingPoints(Paragraph par, int lineWidth,
+                                      double threshold, boolean force,
+                                      boolean hyphenationAllowed) {
+            return super.findBreakingPoints(par, lineWidth, threshold, force, hyphenationAllowed);
         }
 
-        public KnuthElement removeLast() {
-            int idx = size();
-            if (idx == 0) {
-                return null; 
+        protected int filterActiveNodes() {
+            KnuthNode bestActiveNode = null;
+
+            if (pageAlignment == EN_JUSTIFY) {
+                // leave all active nodes and find the optimum line number
+                //System.out.println("LBA.filterActiveNodes> " + activeNodeCount + " layouts");
+                for (int i = startLine; i < endLine; i++) {
+                    for (KnuthNode node = getNode(i); node != null; node = node.next) {
+                        //System.out.println("                       + lines = " + node.line + " demerits = " + node.totalDemerits);
+                        bestActiveNode = compareNodes(bestActiveNode, node);
+                    }
+                }
+
+                // scan the node set once again and remove some nodes
+                //System.out.println("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) {
+                            //System.out.println("                     XXX lines = " + node.line + " demerits = " + node.totalDemerits);
+                            removeNode(i, node);
+                        } else {
+                            //System.out.println("                      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 (KnuthElement) remove(idx - 1);
+            return bestActiveNode.line;
         }
     }
 
+      
+    private int constantLineHeight = 12000;
+      
+
+    /**
+     * Create a new Line Layout Manager.
+     * This is used by the block layout manager to create
+     * line managers for handling inline areas flowing into line areas.
+     *
+     * @param lh the default line height
+     * @param l the default lead, from top to baseline
+     * @param f the default follow, from baseline to bottom
+     */
+    public LineLayoutManager(Block block, int lh, int l, int f, int ms) {
+        super(block);
+        fobj = block;
+        // the child FObj are owned by the parent BlockLM
+        // this LM has all its childLMs preloaded
+        fobjIter = null;
+        lineHeight = lh;
+        lead = l;
+        follow = f;
+        middleShift = ms;
+        initialize(); // Normally done when started by parent!
+    }
 
     /**
      * Call child layout managers to generate content.
@@ -257,7 +490,13 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
      * @param context the layout context for finding breaks
      * @return the next break position
      */
+    // this method is no longer used
     public BreakPoss getNextBreakPoss(LayoutContext context) {
+        setFinished(true);
+        return null;
+    }
+
+    public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
         // Get a break from currently active child LM
         // Set up constraints for inline level managers
         InlineLevelLayoutManager curLM ; // currently active LM
@@ -265,8 +504,6 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
         // IPD remaining in line
         MinOptMax availIPD = context.getStackLimit();
 
-        LayoutContext inlineLC = new LayoutContext(context);
-
         clearPrevIPD();
         int iPrevLineEnd = vecInlineBreaks.size();
 
@@ -276,101 +513,14 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
         prevBP = null;
 
         //PHASE 1: Create Knuth elements
-        
         if (knuthParagraphs == null) {
             // it's the first time this method is called
             knuthParagraphs = new ArrayList();
 
             // here starts Knuth's algorithm
-            KnuthElement thisElement = null;
-            LinkedList returnedList = null;
-
-            // convert all the text in a sequence of paragraphs made
-            // of KnuthBox, KnuthGlue and KnuthPenalty objects
-            boolean bPrevWasKnuthBox = false;
-            KnuthBox prevBox = null;
-
-            Paragraph knuthPar = new Paragraph();
-            knuthPar.startParagraph(availIPD.opt);
-            while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) {
-                if ((returnedList
-                     = curLM.getNextKnuthElements(inlineLC,
-                                                  effectiveAlignment))
-                    != null) {
-                    // look at the first element
-                    thisElement = (KnuthElement) returnedList.getFirst();
-                    if (thisElement.isBox() && !thisElement.isAuxiliary()
-                        && bPrevWasKnuthBox) {
-                        prevBox = (KnuthBox) knuthPar.removeLast();
-                        // if there are two consecutive KnuthBoxes the
-                        // first one does not represent a whole word,
-                        // so it must be given one more letter space
-                        if (!prevBox.isAuxiliary()) {
-                            // if letter spacing is constant,
-                            // only prevBox needs to be replaced;
-                            knuthPar.add(((InlineLevelLayoutManager)
-                                              prevBox.getLayoutManager())
-                                             .addALetterSpaceTo(prevBox));
-                        } else {
-                            // prevBox is the last element
-                            // in the sub-sequence
-                            //   <box> <aux penalty> <aux glue> <aux box>
-                            // the letter space is added to <aux glue>,
-                            // while the other elements are not changed
-                            KnuthBox auxBox = prevBox;
-                            KnuthGlue auxGlue
-                                = (KnuthGlue) knuthPar.removeLast();
-                            KnuthPenalty auxPenalty
-                                = (KnuthPenalty) knuthPar.removeLast();
-                            prevBox = (KnuthBox) knuthPar.getLast();
-                            knuthPar.add(auxPenalty);
-                            knuthPar.add(((InlineLevelLayoutManager)
-                                              prevBox.getLayoutManager())
-                                             .addALetterSpaceTo(prevBox));
-                            knuthPar.add(auxBox);
-                        }
-                    }
-
-                    // look at the last element
-                    KnuthElement lastElement = (KnuthElement) returnedList.getLast();
-                    boolean bForceLinefeed = false;
-                    if (lastElement.isBox()) {
-                        bPrevWasKnuthBox = true;
-                    } else {
-                        bPrevWasKnuthBox = false;
-                        if (lastElement.isPenalty()
-                            && ((KnuthPenalty) lastElement).getP()
-                                == -KnuthPenalty.INFINITE) {
-                            // a penalty item whose value is -inf
-                            // represents a preserved linefeed,
-                            // wich forces a line break
-                            bForceLinefeed = true;
-                            returnedList.removeLast();
-                        }
-                    }
-
-                    // add the new elements to the paragraph
-                    knuthPar.addAll(returnedList);
-                    if (bForceLinefeed) {
-                        if (knuthPar.size() == 0) {
-                            //only a forced linefeed on this line 
-                            //-> compensate with a zero width box
-                            knuthPar.add(new KnuthBox(0, 0, 0, 0,
-                                    null, false));
-                        }
-                        knuthPar.endParagraph();
-                        knuthPar = new Paragraph();
-                        knuthPar.startParagraph(availIPD.opt);
-                        bPrevWasKnuthBox = false;
-                    }
-                } else {
-                    // curLM returned null; this can happen
-                    // if it has nothing more to layout,
-                    // so just iterate once more to see
-                    // if there are other children
-                }
-            }
-            knuthPar.endParagraph();
+            //TODO availIPD should not really be used here, so we can later support custom line
+            //widths for for each line (side-floats, differing available IPD after page break)
+            collectInlineKnuthElements(context, availIPD);
         } else {
             // this method has been called before
             // all line breaks are already calculated
@@ -383,7 +533,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
         }
 
         //PHASE 2: Create line breaks
-
+        return findOptimalLineBreakingPoints(alignment);
+        /*
         LineBreakPosition lbp = null;
         if (breakpoints == null) {
             // find the optimal line breaking points for each paragraph
@@ -395,10 +546,11 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
                 currPar = (Paragraph) paragraphsIterator.previous();
                 findBreakingPoints(currPar, context.getStackLimit().opt);
             }
-        }
+        }*/
 
         //PHASE 3: Return lines
 
+        /*
         // get a break point from the list
         lbp = (LineBreakPosition) breakpoints.get(iReturnedLBP ++);
         if (iReturnedLBP == breakpoints.size()) {
@@ -409,6 +561,109 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
         curLineBP.setFlag(BreakPoss.ISLAST, isFinished());
         curLineBP.setStackingSize(new MinOptMax(lbp.lineHeight));
         return curLineBP;
+        */
+    }
+
+    /**
+     * Phase 1 of Knuth algorithm: Collect all inline Knuth elements before determining line breaks.
+     * @param context the LayoutContext
+     * @param availIPD available IPD for line (should be removed!) 
+     */
+    private void collectInlineKnuthElements(LayoutContext context, MinOptMax availIPD) {
+        LayoutContext inlineLC = new LayoutContext(context);
+
+        InlineLevelLayoutManager curLM;
+        KnuthElement thisElement = null;
+        LinkedList returnedList = null;
+        iLineWidth = context.getStackLimit().opt;
+
+        // convert all the text in a sequence of paragraphs made
+        // of KnuthBox, KnuthGlue and KnuthPenalty objects
+        boolean bPrevWasKnuthBox = false;
+        KnuthBox prevBox = null;
+
+        Paragraph knuthPar = new Paragraph(this, 
+                                           bTextAlignment, bTextAlignmentLast, 
+                                           textIndent.getValue());
+        knuthPar.startParagraph(availIPD.opt);
+        while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) {
+            if ((returnedList
+                 = curLM.getNextKnuthElements(inlineLC,
+                                              effectiveAlignment))
+                != null) {
+                // look at the first element
+                thisElement = (KnuthElement) returnedList.getFirst();
+                if (thisElement.isBox() && !thisElement.isAuxiliary()
+                    && bPrevWasKnuthBox) {
+                    prevBox = (KnuthBox) knuthPar.removeLast();
+                    LinkedList oldList = new LinkedList();
+                    // if there are two consecutive KnuthBoxes the
+                    // first one does not represent a whole word,
+                    // so it must be given one more letter space
+                    if (!prevBox.isAuxiliary()) {
+                        // if letter spacing is constant,
+                        // only prevBox needs to be replaced;
+                        oldList.add(prevBox);
+                    } else {
+                        // prevBox is the last element
+                        // in the sub-sequence
+                        //   <box> <aux penalty> <aux glue> <aux box>
+                        // the letter space is added to <aux glue>,
+                        // while the other elements are not changed
+                        oldList.add(prevBox);
+                        oldList.addFirst((KnuthGlue) knuthPar.removeLast());
+                        oldList.addFirst((KnuthPenalty) knuthPar.removeLast());
+                    }
+                    // adding a letter space could involve, according to the text
+                    // represented by oldList, replacing a glue element or adding
+                    // new elements
+                    knuthPar.addAll(((InlineLevelLayoutManager)
+                                     prevBox.getLayoutManager())
+                                    .addALetterSpaceTo(oldList));
+                }
+
+                // look at the last element
+                KnuthElement lastElement = (KnuthElement) returnedList.getLast();
+                boolean bForceLinefeed = false;
+                if (lastElement.isBox()) {
+                    bPrevWasKnuthBox = true;
+                } else {
+                    bPrevWasKnuthBox = false;
+                    if (lastElement.isPenalty()
+                        && ((KnuthPenalty) lastElement).getP()
+                            == -KnuthPenalty.INFINITE) {
+                        // a penalty item whose value is -inf
+                        // represents a preserved linefeed,
+                        // wich forces a line break
+                        bForceLinefeed = true;
+                        returnedList.removeLast();
+                    }
+                }
+
+                // add the new elements to the paragraph
+                knuthPar.addAll(returnedList);
+                if (bForceLinefeed) {
+                    if (knuthPar.size() == 0) {
+                        //only a forced linefeed on this line 
+                        //-> compensate with a zero width box
+                        knuthPar.add(new KnuthInlineBox(0, 0, 0, 0,
+                                null, false));
+                    }
+                    knuthPar.endParagraph();
+                    knuthPar = new Paragraph(this, 
+                                             bTextAlignment, bTextAlignmentLast, 
+                                             textIndent.getValue());
+                    knuthPar.startParagraph(availIPD.opt);
+                    bPrevWasKnuthBox = false;
+                }
+            } else {
+                // curLM returned null; this can happen
+                // if it has nothing more to layout,
+                // so just iterate once more to see
+                // if there are other children
+            }
+        }
+        knuthPar.endParagraph();
     }
 
     /**
@@ -421,6 +676,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
      *                  into lines
      * @param lineWidth the desired length ot the lines
      */
+    /*
     private void findBreakingPoints(Paragraph par, int lineWidth) {
         // maximum adjustment ratio permitted
         float maxAdjustment = 1;
@@ -508,18 +764,19 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
              j++) {
             KnuthElement element = (KnuthElement) inlineIterator.next();
             if (element.isBox()) {
-                if (((KnuthBox) element).getLead() > lineLead) {
-                    lineLead = ((KnuthBox) element).getLead();
+                KnuthInlineBox box = (KnuthInlineBox)element;
+                if (box.getLead() > lineLead) {
+                    lineLead = box.getLead();
                 }
-                if (((KnuthBox) element).getTotal() > maxtb) {
-                    maxtb = ((KnuthBox) element).getTotal();
+                if (box.getTotal() > maxtb) {
+                    maxtb = box.getTotal();
                 }
-                if (((KnuthBox) element).getMiddle() > lineLead + middleShift) {
-                    lineLead += ((KnuthBox) element).getMiddle()
+                if (box.getMiddle() > lineLead + middleShift) {
+                    lineLead += box.getMiddle()
                                 - lineLead - middleShift;
                 }
-                if (((KnuthBox) element).getMiddle() > middlefollow - middleShift) {
-                    middlefollow += ((KnuthBox) element).getMiddle()
+                if (box.getMiddle() > middlefollow - middleShift) {
+                    middlefollow += box.getMiddle()
                                     - middlefollow + middleShift;
                 }
             }
@@ -536,9 +793,368 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
                                               ratio, 0, indent,
                                               lineLead + middlefollow,
                                               lineLead));
+    }*/
+
+    
+    /**
+     * Phase 2 of Knuth algorithm: find optimal break points.
+     * @param alignment alignmenr of the paragraph
+     * @return a list of Knuth elements representing broken lines
+     */
+    private LinkedList findOptimalLineBreakingPoints(int alignment) {
+
+        // find the optimal line breaking points for each paragraph
+        ListIterator paragraphsIterator
+            = knuthParagraphs.listIterator(knuthParagraphs.size());
+        Paragraph currPar = null;
+        LineBreakingAlgorithm alg;
+        lineLayoutsList = new ArrayList(knuthParagraphs.size());
+        while (paragraphsIterator.hasPrevious()) {
+            lineLayouts = new LineLayoutPossibilities();
+            currPar = (Paragraph) paragraphsIterator.previous();
+            double maxAdjustment = 1;
+            int iBPcount = 0;
+            alg = new LineBreakingAlgorithm(alignment,
+                                            bTextAlignment, bTextAlignmentLast,
+                                            textIndent.getValue(), currPar.lineFiller.opt,
+                                            lineHeight, lead, follow, middleShift,
+                                            (knuthParagraphs.indexOf(currPar) == 0),
+                                            this);
+    
+            if (hyphProps.hyphenate == EN_TRUE) {
+                findHyphenationPoints(currPar);
+            }
+    
+            // first try
+            boolean bHyphenationAllowed = false;
+            iBPcount = alg.findBreakingPoints(currPar,
+                                              iLineWidth,
+                                              maxAdjustment, false, bHyphenationAllowed);
+            if (iBPcount == 0 || alignment == EN_JUSTIFY) {
+                // if the first try found a set of breaking points, save them
+                if (iBPcount > 0) {
+                    alg.resetAlgorithm();
+                    lineLayouts.savePossibilities(false);
+                } else {
+                    // the first try failed
+                    log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment);
+                }
+    
+                // now try something different
+                log.debug("Hyphenation possible? " + (hyphProps.hyphenate == EN_TRUE));
+                if (hyphProps.hyphenate == EN_TRUE) {
+                    // consider every hyphenation point as a legal break
+                    bHyphenationAllowed = true;
+                } else {
+                    // try with a higher threshold
+                    maxAdjustment = 5;
+                }
+    
+                if ((iBPcount
+                     = alg.findBreakingPoints(currPar,
+                                              iLineWidth,
+                                              maxAdjustment, false, bHyphenationAllowed)) == 0) {
+                    // the second try failed too, try with a huge threshold
+                    // and force the algorithm to find
+                    // a set of breaking points
+                    log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment
+                                     + (hyphProps.hyphenate == EN_TRUE ? " and hyphenation" : ""));
+                    maxAdjustment = 20;
+                    iBPcount
+                        = alg.findBreakingPoints(currPar,
+                                                 iLineWidth,
+                                                 maxAdjustment, true, bHyphenationAllowed);
+                }
+    
+                // use non-hyphenated breaks, when possible
+                lineLayouts.restorePossibilities();
+    
+                /* extension (not in the XSL FO recommendation): if vertical alignment
+                   is justify and the paragraph has only one layout, try using 
+                   shorter or longer lines */
+                //TODO This code snippet is disabled. Reenable?
+                if (false && alignment == EN_JUSTIFY && bTextAlignment == EN_JUSTIFY) {
+                    //System.out.println("LLM.getNextKnuthElements> layouts with more lines? " + lineLayouts.canUseMoreLines());
+                    //System.out.println("                          layouts with fewer lines? " + lineLayouts.canUseLessLines());
+                    if (!lineLayouts.canUseMoreLines()) {
+                        alg.resetAlgorithm();
+                        lineLayouts.savePossibilities(true);
+                        // try with shorter lines
+                        int savedLineWidth = iLineWidth;
+                        iLineWidth = (int) (iLineWidth * 0.95);
+                        iBPcount
+                            = alg.findBreakingPoints(currPar,
+                                                     iLineWidth,
+                                                     maxAdjustment, true, bHyphenationAllowed);
+                         // use normal lines, when possible
+                         lineLayouts.restorePossibilities();
+                         iLineWidth = savedLineWidth;
+                    }
+                    if (!lineLayouts.canUseLessLines()) {
+                        alg.resetAlgorithm();
+                        lineLayouts.savePossibilities(true);
+                        // try with longer lines
+                        int savedLineWidth = iLineWidth;
+                        iLineWidth = (int) (iLineWidth * 1.05);
+                        iBPcount
+                            = alg.findBreakingPoints(currPar,
+                                                     iLineWidth,
+                                                     maxAdjustment, true, bHyphenationAllowed);
+                         // use normal lines, when possible
+                         lineLayouts.restorePossibilities();
+                         iLineWidth = savedLineWidth;
+                    }
+                    //System.out.println("LLM.getNextKnuthElements> now, layouts with more lines? " + lineLayouts.canUseMoreLines());
+                    //System.out.println("                          now, layouts with fewer lines? " + lineLayouts.canUseLessLines());
+                }
+            }
+            lineLayoutsList.add(0, lineLayouts);
+        }
+        
+        
+        setFinished(true);
+    
+        //Post-process the line breaks found
+        return postProcessLineBreaks(alignment);
+    }
+
+    private LinkedList postProcessLineBreaks(int alignment) {
+    
+        LinkedList returnList = new LinkedList();
+        
+        for (int p = 0; p < knuthParagraphs.size(); p ++) {
+            // null penalty between paragraphs
+            if (p > 0
+                && !((BlockLevelLayoutManager) parentLM).mustKeepTogether()) {
+                returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
+            }
+        
+            lineLayouts = (LineLayoutPossibilities)lineLayoutsList.get(p);
+        
+            if (alignment == EN_JUSTIFY) {
+                /* justified vertical alignment (not in the XSL FO recommendation):
+                   create a multi-layout sequence whose elements will contain 
+                   a conventional Position */
+                Position returnPosition = new LeafPosition(this, p);
+                createElements(returnList, lineLayouts, returnPosition);
+            } else {
+                /* "normal" vertical alignment: create a sequence whose boxes
+                   represent effective lines, and contain LineBreakPositions */
+                Position returnPosition = new LeafPosition(this, p);
+                for (int i = 0;
+                        i < lineLayouts.getChosenLineCount();
+                        i++) {
+                    if (!((BlockLevelLayoutManager) parentLM).mustKeepTogether()
+                        && i >= fobj.getOrphans()
+                        && i <= lineLayouts.getChosenLineCount() - fobj.getWidows()
+                        && returnList.size() > 0) {
+                        // null penalty allowing a page break between lines
+                        returnList.add(new KnuthPenalty(0, 0, false, returnPosition, false));
+                    }
+                    returnList.add(new KnuthBox(((LineBreakPosition) lineLayouts.getChosenPosition(i)).lineHeight,
+                                                lineLayouts.getChosenPosition(i), false));
+                }
+            }
+        }
+        
+        return returnList;
     }
 
 
+    private void createElements(List list, LineLayoutPossibilities lineLayouts,
+                                Position elementPosition) {
+        /* number of normal, inner lines */
+        int nInnerLines = 0;
+        /* number of lines that can be used in order to fill more space */
+        int nOptionalLines = 0;
+        /* number of lines that can be used in order to fill more space
+           only if the paragraph is not parted */
+        int nConditionalOptionalLines = 0;
+        /* number of lines that can be omitted in order to fill less space */
+        int nEliminableLines = 0;
+        /* number of lines that can be omitted in order to fill less space
+           only if the paragraph is not parted */
+        int nConditionalEliminableLines = 0;
+        /* number of the first unbreakable lines */
+        int nFirstLines = fobj.getOrphans();
+        /* number of the last unbreakable lines */
+        int nLastLines = fobj.getWidows();
+        /* sub-sequence used to separate the elements representing different lines */
+        List breaker = new LinkedList();
+
+        /* comment out the next lines in order to test particular situations */
+        if (fobj.getOrphans() + fobj.getWidows() <= lineLayouts.getMinLineCount()) {
+            nInnerLines = lineLayouts.getMinLineCount() - (fobj.getOrphans() + fobj.getWidows());
+            nOptionalLines = lineLayouts.getMaxLineCount() - lineLayouts.getOptLineCount();
+            nEliminableLines = lineLayouts.getOptLineCount() - lineLayouts.getMinLineCount();
+        } else if (fobj.getOrphans() + fobj.getWidows() <= lineLayouts.getOptLineCount()) {
+            nOptionalLines = lineLayouts.getMaxLineCount() - lineLayouts.getOptLineCount();
+            nEliminableLines = lineLayouts.getOptLineCount() - (fobj.getOrphans() + fobj.getWidows());
+            nConditionalEliminableLines = (fobj.getOrphans() + fobj.getWidows()) - lineLayouts.getMinLineCount();
+        } else if (fobj.getOrphans() + fobj.getWidows() <= lineLayouts.getMaxLineCount()) {
+            nOptionalLines = lineLayouts.getMaxLineCount() - (fobj.getOrphans() + fobj.getWidows());
+            nConditionalOptionalLines = (fobj.getOrphans() + fobj.getWidows()) - lineLayouts.getOptLineCount();
+            nConditionalEliminableLines = lineLayouts.getOptLineCount() - lineLayouts.getMinLineCount();
+            nFirstLines -= nConditionalOptionalLines;
+        } else {
+            nConditionalOptionalLines = lineLayouts.getMaxLineCount() - lineLayouts.getOptLineCount();
+            nConditionalEliminableLines = lineLayouts.getOptLineCount() - lineLayouts.getMinLineCount();
+            nFirstLines = lineLayouts.getOptLineCount();
+            nLastLines = 0;
+        }
+        /* comment out the previous lines in order to test particular situations */
+
+        /* use these lines to test particular situations
+        nInnerLines = 0;
+        nOptionalLines = 1;
+        nConditionalOptionalLines = 2;
+        nEliminableLines = 0;
+        nConditionalEliminableLines = 0;
+        nFirstLines = 1;
+        nLastLines = 3;
+        */
+
+        if (nLastLines != 0
+            && (nConditionalOptionalLines > 0 || nConditionalEliminableLines > 0)) {
+            breaker.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
+            breaker.add(new KnuthGlue(0, -nConditionalOptionalLines * constantLineHeight,
+                                        -nConditionalEliminableLines * constantLineHeight,
+                                        LINE_NUMBER_ADJUSTMENT, elementPosition, false));
+            breaker.add(new KnuthPenalty(nConditionalOptionalLines * constantLineHeight,
+                                           0, false, elementPosition, false));
+            breaker.add(new KnuthGlue(0, nConditionalOptionalLines * constantLineHeight,
+                                        nConditionalEliminableLines * constantLineHeight,
+                                        LINE_NUMBER_ADJUSTMENT, elementPosition, false));
+        } else if (nLastLines != 0) {
+            breaker.add(new KnuthPenalty(0, 0, false, elementPosition, false));
+        }
+
+        //System.out.println("first=" + nFirstLines + " inner=" + nInnerLines
+        //                   + " optional=" + nOptionalLines + " eliminable=" + nEliminableLines
+        //                   + " last=" + nLastLines
+        //                   + " (condOpt=" + nConditionalOptionalLines + " condEl=" + nConditionalEliminableLines + ")");
+
+        // creation of the elements:
+        // first group of lines
+        list.add(new KnuthBox(nFirstLines * constantLineHeight, elementPosition,
+                              (nLastLines == 0
+                               && nConditionalOptionalLines == 0
+                               && nConditionalEliminableLines == 0 ? true : false)));
+        if (nConditionalOptionalLines > 0
+            || nConditionalEliminableLines > 0) {
+            list.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
+            list.add(new KnuthGlue(0, nConditionalOptionalLines * constantLineHeight,
+                                   nConditionalEliminableLines * constantLineHeight,
+                                   LINE_NUMBER_ADJUSTMENT, elementPosition, false));
+            list.add(new KnuthBox(0, elementPosition,
+                                  (nLastLines == 0 ? true : false)));
+        }
+
+        // optional lines
+        for (int i = 0; i < nOptionalLines; i++) {
+            list.addAll(breaker);
+            list.add(new KnuthBox(0, elementPosition, false));
+            list.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
+            list.add(new KnuthGlue(0, 1 * constantLineHeight, 0,
+                                   LINE_NUMBER_ADJUSTMENT, elementPosition, false));
+            list.add(new KnuthBox(0, elementPosition, false));
+        }
+
+        // eliminable lines
+        for (int i = 0; i < nEliminableLines; i++) {
+            list.addAll(breaker);
+            list.add(new KnuthBox(1 * constantLineHeight, elementPosition, false));
+            list.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
+            list.add(new KnuthGlue(0, 0, 1 * constantLineHeight,
+                                   LINE_NUMBER_ADJUSTMENT, elementPosition, false));
+            list.add(new KnuthBox(0, elementPosition, false));
+        }
+
+        // inner lines
+        for (int i = 0; i < nInnerLines; i++) {
+            list.addAll(breaker);
+            list.add(new KnuthBox(1 * constantLineHeight, elementPosition, false));
+        }
+
+        // last group of lines
+        if (nLastLines > 0) {
+            list.addAll(breaker);
+            list.add(new KnuthBox(nLastLines * constantLineHeight,
+                                  elementPosition, true));
+        }
+    }
+
+    public boolean mustKeepTogether() {
+        return false;
+    }
+
+    public boolean mustKeepWithPrevious() {
+        return false;
+    }
+
+    public boolean mustKeepWithNext() {
+        return false;
+    }
+
+    public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
+        LeafPosition pos = (LeafPosition)lastElement.getPosition();
+        int totalAdj = adj;
+        //if (lastElement.isPenalty()) {
+        //    totalAdj += lastElement.getW();
+        //}
+        //int lineNumberDifference = (int)((double) totalAdj / constantLineHeight);
+        int lineNumberDifference = (int) Math.round((double) totalAdj / constantLineHeight + (adj > 0 ? - 0.4 : 0.4));
+        //System.out.println("   LLM> variazione calcolata = " + ((double) totalAdj / constantLineHeight) + " variazione applicata = " + lineNumberDifference);
+        lineLayouts = (LineLayoutPossibilities)lineLayoutsList.get(pos.getLeafPos());
+        lineNumberDifference = lineLayouts.applyLineCountAdjustment(lineNumberDifference);
+        return lineNumberDifference * constantLineHeight;
+    }
+
+    public void discardSpace(KnuthGlue spaceGlue) {
+    }
+
+    public LinkedList getChangedKnuthElements(List oldList, int alignment) {
+        LinkedList returnList = new LinkedList();
+        for (int p = 0;
+             p < knuthParagraphs.size();
+             p ++) {
+            lineLayouts = (LineLayoutPossibilities)lineLayoutsList.get(p);
+            //System.out.println("demerits of the chosen layout: " + lineLayouts.getChosenDemerits());
+            for (int i = 0;
+                 i < lineLayouts.getChosenLineCount();
+                 i ++) {
+                if (!((BlockLevelLayoutManager) parentLM).mustKeepTogether()
+                    && i >= fobj.getOrphans()
+                    && i <= lineLayouts.getChosenLineCount() - fobj.getWidows()) {
+                    // null penalty allowing a page break between lines
+                    returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
+                }
+                LineBreakPosition lbp = (LineBreakPosition) lineLayouts.getChosenPosition(i);
+                //System.out.println("LLM.getChangedKnuthElements> lineWidth= " + lbp.lineWidth + " difference= " + lbp.difference);
+                //System.out.println("                             shrink= " + lbp.availableShrink + " stretch= " + lbp.availableStretch);
+
+                //System.out.println("linewidth= " + lbp.lineWidth + " difference= " + lbp.difference + " indent= " + lbp.startIndent);
+                MinOptMax contentIPD;
+                if (alignment == EN_JUSTIFY) {
+                    contentIPD = new MinOptMax(
+                        lbp.lineWidth - lbp.difference - lbp.availableShrink, 
+                        lbp.lineWidth - lbp.difference, 
+                        lbp.lineWidth - lbp.difference + lbp.availableStretch);
+                } else if (alignment == EN_CENTER) {
+                    contentIPD = new MinOptMax(lbp.lineWidth - 2 * lbp.startIndent);
+                } else if (alignment == EN_END) {
+                    contentIPD = new MinOptMax(lbp.lineWidth - lbp.startIndent);
+                } else {
+                    contentIPD = new MinOptMax(lbp.lineWidth - lbp.difference + lbp.startIndent);
+                }
+                returnList.add(new KnuthBlockBox(lbp.lineHeight,
+                                                 contentIPD,
+                                                 (lbp.ipdAdjust != 0 ? lbp.lineWidth - lbp.difference : 0),
+                                                 lbp, false));
+            }
+        }
+        return returnList;
+    }
 
     /**
      * find hyphenation points for every word int the current paragraph
@@ -658,7 +1274,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
                     = currUpdate.inlineLM.getChangedKnuthElements
                     (currPar.subList(fromIndex + iAddedElements,
                                      toIndex + iAddedElements),
-                     flaggedPenalty, effectiveAlignment);
+                     /*flaggedPenalty,*/ effectiveAlignment);
                 // remove the old elements
                 currPar.subList(fromIndex + iAddedElements,
                                 toIndex + iAddedElements).clear();
@@ -823,7 +1439,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
                 setFinished(false);
                 iReturnedLBP--;
             }
-            while ((LineBreakPosition) breakpoints.get(iReturnedLBP)
+            while ((LineBreakPosition) lineLayouts.getChosenPosition(iReturnedLBP)
                    != (LineBreakPosition) resetPos) {
                 iReturnedLBP--;
             }
@@ -839,123 +1455,121 @@ public class LineLayoutManager extends InlineStackingLayoutManager {
      */
     public void addAreas(PositionIterator parentIter,
                          LayoutContext context) {
-        addAreas(parentIter, 0.0);
-
-        //vecInlineBreaks.clear();
-        prevBP = null;
-    }
-
-    // Generate and add areas to parent area
-    // Set size etc
-    // dSpaceAdjust should reference extra space in the BPD
-    /**
-     * Add the areas with the associated space adjustment.
-     *
-     * @param parentIter the iterator of breaks positions
-     * @param dSpaceAdjust the space adjustment
-     */
-    public void addAreas(PositionIterator parentIter, double dSpaceAdjust) {
         LayoutManager childLM;
         LayoutContext lc = new LayoutContext(0);
         int iCurrParIndex;
         while (parentIter.hasNext()) {
-            ListIterator paragraphIterator = null;
-            KnuthElement tempElement = null;
-            // the TLM which created the last KnuthElement in this line
-            LayoutManager lastLM = null;
-
-            LineBreakPosition lbp = (LineBreakPosition) parentIter.next();
-            LineArea lineArea = new LineArea();
-            lineArea.setStartIndent(lbp.startIndent);
-            lineArea.setBPD(lbp.lineHeight);
-            lc.setBaseline(lbp.baseline);
-            lc.setLineHeight(lbp.lineHeight);
-            lc.setMiddleShift(middleShift);
-            setCurrentArea(lineArea);
-
-            iCurrParIndex = lbp.iParIndex;
-            Paragraph currPar = (Paragraph) knuthParagraphs.get(iCurrParIndex);
-            iEndElement = lbp.getLeafPos();
-
-            // ignore the first elements added by the LineLayoutManager
-            iStartElement += (iStartElement == 0) ? currPar.ignoreAtStart : 0;
-
-            // ignore the last elements added by the LineLayoutManager
-            iEndElement -= (iEndElement == (currPar.size() - 1))
-                ? currPar.ignoreAtEnd : 0;
-
-            // ignore the last element in the line if it is a KnuthGlue object
-            paragraphIterator = currPar.listIterator(iEndElement);
-            tempElement = (KnuthElement) paragraphIterator.next();
-            if (tempElement.isGlue()) {
-                iEndElement --;
-                // this returns the same KnuthElement
-                paragraphIterator.previous();
-                tempElement = (KnuthElement) paragraphIterator.previous();
-            }
-            lastLM = tempElement.getLayoutManager();
-
-            // ignore KnuthGlue and KnuthPenalty objects
-            // at the beginning of the line
-            paragraphIterator = currPar.listIterator(iStartElement);
-            tempElement = (KnuthElement) paragraphIterator.next();
-            while (!tempElement.isBox() && paragraphIterator.hasNext()) {
+            Position pos = (Position) parentIter.next();
+            if (pos instanceof LineBreakPosition) {
+                ListIterator paragraphIterator = null;
+                KnuthElement tempElement = null;
+                // the TLM which created the last KnuthElement in this line
+                LayoutManager lastLM = null;
+    
+                LineBreakPosition lbp = (LineBreakPosition) pos;
+                LineArea lineArea = new LineArea();
+                lineArea.setStartIndent(lbp.startIndent);
+                lineArea.setBPD(lbp.lineHeight);
+                lc.setBaseline(lbp.baseline);
+                lc.setLineHeight(lbp.lineHeight);
+                lc.setMiddleShift(middleShift);
+                lc.setTopShift(lbp.topShift);
+                lc.setBottomShift(lbp.bottomShift);
+
+                iCurrParIndex = lbp.iParIndex;
+                Paragraph currPar = (Paragraph) knuthParagraphs.get(iCurrParIndex);
+                iEndElement = lbp.getLeafPos();
+    
+                // ignore the first elements added by the LineLayoutManager
+                iStartElement += (iStartElement == 0) ? currPar.ignoreAtStart : 0;
+    
+                // ignore the last elements added by the LineLayoutManager
+                iEndElement -= (iEndElement == (currPar.size() - 1))
+                    ? currPar.ignoreAtEnd : 0;
+    
+                // ignore the last element in the line if it is a KnuthGlue object
+                paragraphIterator = currPar.listIterator(iEndElement);
                 tempElement = (KnuthElement) paragraphIterator.next();
-                iStartElement ++;
-            }
-
-            // Add the inline areas to lineArea
-            PositionIterator inlinePosIter
-                = new KnuthPossPosIter(currPar, iStartElement,
-                                       iEndElement + 1);
-
-            iStartElement = lbp.getLeafPos() + 1;
-            if (iStartElement == currPar.size()) {
-                // advance to next paragraph
-                iStartElement = 0;
-            }
-
-            lc.setSpaceAdjust(lbp.dAdjust);
-            lc.setIPDAdjust(lbp.ipdAdjust);
-            lc.setLeadingSpace(new SpaceSpecifier(true));
-            lc.setTrailingSpace(new SpaceSpecifier(false));
-            lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
-            setChildContext(lc);
-            while ((childLM = inlinePosIter.getNextChildLM()) != null) {
-                lc.setFlags(LayoutContext.LAST_AREA, (childLM == lastLM));
-                childLM.addAreas(inlinePosIter, lc);
-                lc.setLeadingSpace(lc.getTrailingSpace());
+                if (tempElement.isGlue()) {
+                    iEndElement --;
+                    // this returns the same KnuthElement
+                    paragraphIterator.previous();
+                    tempElement = (KnuthElement) paragraphIterator.previous();
+                }
+                lastLM = tempElement.getLayoutManager();
+    
+                // ignore KnuthGlue and KnuthPenalty objects
+                // at the beginning of the line
+                paragraphIterator = currPar.listIterator(iStartElement);
+                tempElement = (KnuthElement) paragraphIterator.next();
+                while (!tempElement.isBox() && paragraphIterator.hasNext()) {
+                    tempElement = (KnuthElement) paragraphIterator.next();
+                    iStartElement ++;
+                }
+    
+                // Add the inline areas to lineArea
+                PositionIterator inlinePosIter
+                    = new KnuthPossPosIter(currPar, iStartElement,
+                                           iEndElement + 1);
+    
+                iStartElement = lbp.getLeafPos() + 1;
+                if (iStartElement == currPar.size()) {
+                    // advance to next paragraph
+                    iStartElement = 0;
+                }
+    
+                lc.setSpaceAdjust(lbp.dAdjust);
+                lc.setIPDAdjust(lbp.ipdAdjust);
+                lc.setLeadingSpace(new SpaceSpecifier(true));
                 lc.setTrailingSpace(new SpaceSpecifier(false));
+                lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
+
+                /* extension (not in the XSL FO recommendation): if the left and right margins
+                   have been optimized, recompute indents and / or adjust ratio, according
+                   to the paragraph horizontal alignment */
+                if (false && bTextAlignment == EN_JUSTIFY) {
+                    // re-compute space adjust ratio
+                    int updatedDifference = context.getStackLimit().opt - lbp.lineWidth + lbp.difference;
+                    double updatedRatio = 0.0;
+                    if (updatedDifference > 0) {
+                        updatedRatio = (float) updatedDifference / lbp.availableStretch;
+                    } else if (updatedDifference < 0) {
+                        updatedRatio = (float) updatedDifference / lbp.availableShrink;
+                    }
+                    lc.setIPDAdjust(updatedRatio);
+                    //System.out.println("LLM.addAreas> old difference = " + lbp.difference + " new difference = " + updatedDifference);
+                    //System.out.println("              old ratio = " + lbp.ipdAdjust + " new ratio = " + updatedRatio);
+                } else if (false && bTextAlignment == EN_CENTER) {
+                    // re-compute indent
+                    int updatedIndent = lbp.startIndent + (context.getStackLimit().opt - lbp.lineWidth) / 2;
+                    lineArea.setStartIndent(updatedIndent);
+                } else if (false && bTextAlignment == EN_END) {
+                    // re-compute indent
+                    int updatedIndent = lbp.startIndent + (context.getStackLimit().opt - lbp.lineWidth);
+                    lineArea.setStartIndent(updatedIndent);
+                }
+
+                setCurrentArea(lineArea);
+                setChildContext(lc);
+                while ((childLM = inlinePosIter.getNextChildLM()) != null) {
+                    lc.setFlags(LayoutContext.LAST_AREA, (childLM == lastLM));
+                    childLM.addAreas(inlinePosIter, lc);
+                    lc.setLeadingSpace(lc.getTrailingSpace());
+                    lc.setTrailingSpace(new SpaceSpecifier(false));
+                }
+                
+                // when can this be null?
+                // if display-align is distribute, add space after 
+                if (context.getSpaceAfter() > 0
+                    && (!context.isLastArea() || parentIter.hasNext())) {
+                    lineArea.setBPD(lineArea.getBPD() + context.getSpaceAfter());
+                }
+                parentLM.addChildArea(lineArea);
+            } else {
+                // pos was the Position inside a penalty item, nothing to do
             }
-            // when can this be null?
-            if (lc.getTrailingSpace() != null) {
-                addSpace(lineArea, lc.getTrailingSpace().resolve(true),
-                         lc.getSpaceAdjust());
-            }
-            parentLM.addChildArea(lineArea);
         }
         setCurrentArea(null); // ?? necessary
     }
-
-    /**
-     * Add an unresolved area.
-     * If a child layout manager needs to add an unresolved area
-     * for page reference or linking then this intercepts it for
-     * line area handling.
-     * A line area may need to have the inline areas adjusted
-     * to properly fill the line area. This adds a resolver that
-     * resolves the inline area and can do the necessary
-     * adjustments to the line and inline areas.
-     *
-     * @param id the id reference of the resolvable
-     * @param res the resolvable object
-     */
-    public void addUnresolvedArea(String id, Resolvable res) {
-        // create a resolvable class that handles ipd
-        // adjustment for the current line
-
-        parentLM.addUnresolvedArea(id, res);
-    }
-
 }
 
diff --git a/src/java/org/apache/fop/layoutmgr/LineLayoutPossibilities.java b/src/java/org/apache/fop/layoutmgr/LineLayoutPossibilities.java
new file mode 100644 (file)
index 0000000..865121f
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class LineLayoutPossibilities {
+
+    /** logger instance */
+    protected static Log log = LogFactory.getLog(LineLayoutPossibilities.class);
+    
+    private class Possibility {
+        private int lineCount;
+        private double demerits;
+        private List breakPositions;
+
+        private Possibility(int lc, double dem) {
+            lineCount = lc;
+            demerits = dem;
+            breakPositions = new java.util.ArrayList(lc);
+        }
+
+        private int getLineCount() {
+            return lineCount;
+        }
+
+        private double getDemerits() {
+            return demerits;
+        }
+
+        private void addBreakPosition(Position pos) {
+            // Positions are always added with index 0 because 
+            // they are created backward, from the last one to 
+            // the first one
+            breakPositions.add(0, pos);
+        }
+
+        private Position getBreakPosition(int i) {
+            return (Position)breakPositions.get(i);
+        }
+    }
+
+    private List possibilitiesList;
+    private List savedPossibilities;
+    private int minimumIndex;
+    private int optimumIndex;
+    private int maximumIndex;
+    private int chosenIndex;
+    private int savedOptLineCount;
+
+    public LineLayoutPossibilities() {
+        possibilitiesList = new java.util.ArrayList();
+        savedPossibilities = new java.util.ArrayList();
+        optimumIndex = -1;
+    }
+    public void addPossibility(int ln, double dem) {
+        possibilitiesList.add(new Possibility(ln, dem));
+        if (possibilitiesList.size() == 1) {
+            // first Possibility added
+            minimumIndex = 0;
+            optimumIndex = 0;
+            maximumIndex = 0;
+            chosenIndex = 0;
+        } else {
+            if (dem < ((Possibility)possibilitiesList.get(optimumIndex)).getDemerits()) {
+                optimumIndex = possibilitiesList.size() - 1;
+                chosenIndex = optimumIndex;
+            }
+            if (ln < ((Possibility)possibilitiesList.get(minimumIndex)).getLineCount()) {
+                minimumIndex = possibilitiesList.size() - 1;
+            }
+            if (ln > ((Possibility)possibilitiesList.get(maximumIndex)).getLineCount()) {
+                maximumIndex = possibilitiesList.size() - 1;
+            }
+        }
+    }
+
+    /* save in a different array the computed Possibilities,
+     * so possibilitiesList is ready to store different Possibilities
+     */
+    public void savePossibilities(boolean bSaveOptLineCount) {
+        if (bSaveOptLineCount) {
+            savedOptLineCount = getOptLineCount();
+        } else {
+            savedOptLineCount = 0;
+        }
+        savedPossibilities = possibilitiesList;
+        possibilitiesList = new java.util.ArrayList();
+    }
+
+    /* replace the Possibilities stored in possibilitiesList with
+     * the ones stored in savedPossibilities and having the same line number
+     */
+    public void restorePossibilities() {
+        int index = 0;
+        while (savedPossibilities.size() > 0) {
+            Possibility restoredPossibility = (Possibility) savedPossibilities.remove(0);
+            if (restoredPossibility.getLineCount() < getMinLineCount()) {
+                // if the line number of restoredPossibility is less than the minimum one,
+                // add restoredPossibility at the beginning of the list
+                possibilitiesList.add(0, restoredPossibility);
+                // update minimumIndex
+                minimumIndex = 0;
+                // shift the other indexes;
+                optimumIndex ++;
+                maximumIndex ++;
+                chosenIndex ++;
+            } else if (restoredPossibility.getLineCount() > getMaxLineCount()) {
+                // if the line number of restoredPossibility is greater than the maximum one,
+                // add restoredPossibility at the end of the list
+                possibilitiesList.add(possibilitiesList.size(), restoredPossibility);
+                // update maximumIndex
+                maximumIndex = possibilitiesList.size() - 1;
+                index = maximumIndex;
+            } else {
+                // find the index of the Possibility that will be replaced
+                while (index < maximumIndex
+                       && getLineCount(index) < restoredPossibility.getLineCount()) {
+                    index ++;
+                }
+                if (getLineCount(index) == restoredPossibility.getLineCount()) {
+                    possibilitiesList.set(index, restoredPossibility);
+                } else {
+                    // this should not happen
+                    log.error("LineLayoutPossibilities restorePossibilities(),"
+                        + " min= " + getMinLineCount() 
+                        + " max= " + getMaxLineCount() 
+                        + " restored= " + restoredPossibility.getLineCount());
+                    return;
+                }
+            }
+            // update optimumIndex and chosenIndex
+            if (savedOptLineCount == 0 && getDemerits(optimumIndex) > restoredPossibility.getDemerits()
+                || savedOptLineCount != 0 && restoredPossibility.getLineCount() == savedOptLineCount) {
+                optimumIndex = index;
+                chosenIndex = optimumIndex;
+            }
+        }
+/*LF*/  //System.out.println(">> minLineCount = " + getMinLineCount() + " optLineCount = " + getOptLineCount() + " maxLineCount() = " + getMaxLineCount());
+    }
+
+    public void addBreakPosition(Position pos, int i) {
+        ((Possibility)possibilitiesList.get(i)).addBreakPosition(pos);
+    }
+
+    public boolean canUseMoreLines() {
+        return (getOptLineCount() < getMaxLineCount());
+    }
+
+    public boolean canUseLessLines() {
+        return (getMinLineCount() < getOptLineCount());
+    }
+
+    public int getMinLineCount() {
+        return getLineCount(minimumIndex);
+    }
+
+    public int getOptLineCount() {
+        return getLineCount(optimumIndex);
+    }
+
+    public int getMaxLineCount() {
+        return getLineCount(maximumIndex);
+    }
+
+    public int getChosenLineCount() {
+        return getLineCount(chosenIndex);
+    }
+
+    public int getLineCount(int i) {
+        return ((Possibility)possibilitiesList.get(i)).getLineCount();
+    }
+
+    public double getChosenDemerits() {
+        return getDemerits(chosenIndex);
+    }
+
+    public double getDemerits(int i) {
+        return ((Possibility)possibilitiesList.get(i)).getDemerits();
+    }
+
+    public int getPossibilitiesNumber() {
+        return possibilitiesList.size();
+    }
+
+    public Position getChosenPosition(int i) {
+        return ((Possibility)possibilitiesList.get(chosenIndex)).getBreakPosition(i);
+    }
+
+    public int applyLineCountAdjustment(int adj) {
+        if (adj >= (getMinLineCount() - getChosenLineCount())
+            && adj <= (getMaxLineCount() - getChosenLineCount())
+            && getLineCount(chosenIndex + adj) == getChosenLineCount() + adj) {
+            chosenIndex += adj;
+            log.debug("chosenLineCount= " + (getChosenLineCount() - adj) + " adjustment= " + adj
+                               + " => chosenLineCount= " + getLineCount(chosenIndex));
+            return adj;
+        } else {
+            // this should not happen!
+            log.warn("Cannot apply the desired line count adjustment.");
+            return 0;
+        }
+    }
+
+    public void printAll() {
+        System.out.println("++++++++++");
+        System.out.println(" " + possibilitiesList.size() + " possibility':");
+        for (int i = 0; i < possibilitiesList.size(); i ++) {
+            System.out.println("   " + ((Possibility)possibilitiesList.get(i)).getLineCount()
+                               + (i == optimumIndex ? " *" : "")
+                               + (i == minimumIndex ? " -" : "")
+                               + (i == maximumIndex ? " +" : ""));
+        }
+        System.out.println("++++++++++");
+    }
+}
index 63cec1a7e561bb950f2e38d210d4f6188216f476..a17748e24d1be0b02a6432a06a573f41c85925b4 100644 (file)
@@ -63,6 +63,17 @@ public class MinOptMaxUtil {
         }
     }
 
+    public static void extendMinimum(MinOptMax mom, int len, boolean optToLen) {
+        if (mom.min < len) {
+            mom.min = len;
+            mom.opt = Math.max(mom.min, mom.opt);
+            if (optToLen) {
+                mom.opt = Math.min(mom.min, len);
+            }
+            mom.max = Math.max(mom.opt, mom.max);
+        }
+    }
+    
     /**
      * After a calculation on a MinOptMax, this can be called to set opt to
      * a new effective value.
@@ -77,4 +88,21 @@ public class MinOptMaxUtil {
         }
     }
     
+    /**
+     * Converts a LengthRangeProperty to a MinOptMax.
+     * @param prop LengthRangeProperty
+     * @return the requested MinOptMax instance
+     */
+    public static MinOptMax toMinOptMax(LengthRangeProperty prop) {
+        MinOptMax mom = new MinOptMax(
+                (prop.getMinimum().isAuto() 
+                        ? 0 : prop.getMinimum().getLength().getValue()),
+                (prop.getOptimum().isAuto() 
+                        ? 0 : prop.getOptimum().getLength().getValue()),
+                (prop.getMinimum().isAuto() 
+                        ? Integer.MAX_VALUE 
+                        : prop.getMaximum().getLength().getValue()));
+        return mom;
+    }
+    
 }
index 3b828a80cbdfffd0714ea5565c870a81cea3bdfe..7f21a54d31ffeb02f5bf590e68a9c109671b487a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -30,5 +30,18 @@ public class NonLeafPosition extends Position {
     public Position getPosition() {
         return subPos;
     }
+    
+    /** @see java.lang.Object#toString() */
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("NonLeafPos(");
+        if (getPosition() != null) {
+            sb.append(getPosition().toString());
+        } else {
+            sb.append("null");
+        }
+        sb.append(")");
+        return sb.toString();
+    }
 }
 
diff --git a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
new file mode 100644 (file)
index 0000000..766ca99
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr;
+
+import java.util.LinkedList;
+
+import org.apache.fop.layoutmgr.AbstractBreaker.PageBreakPosition;
+
+class PageBreakingAlgorithm extends BreakingAlgorithm {
+    private LayoutManager topLevelLM;
+    private LinkedList pageBreaks = null;
+
+    public PageBreakingAlgorithm(LayoutManager topLevelLM,
+                                  int alignment, int alignmentLast) {
+        super(alignment, alignmentLast, true);
+        this.topLevelLM = topLevelLM;
+    }
+    
+    public LinkedList getPageBreaks() {
+        return pageBreaks;
+    }
+
+    public void insertPageBreakAsFirst(PageBreakPosition pageBreak) {
+        if (pageBreaks == null) {
+            pageBreaks = new LinkedList();
+        }
+        pageBreaks.addFirst(pageBreak);
+    }
+    
+    public void updateData1(int total, double demerits) {
+    }
+
+    public void updateData2(KnuthNode bestActiveNode,
+                            KnuthSequence sequence,
+                            int total) {
+        //int difference = (bestActiveNode.line < total) ? bestActiveNode.difference : bestActiveNode.difference + fillerMinWidth;
+        int difference = bestActiveNode.difference;
+        int blockAlignment = (bestActiveNode.line < total) ? alignment : alignmentLast;
+        double ratio = (blockAlignment == org.apache.fop.fo.Constants.EN_JUSTIFY
+                        || bestActiveNode.adjustRatio < 0) ? bestActiveNode.adjustRatio : 0;
+
+
+        // add nodes at the beginning of the list, as they are found
+        // backwards, from the last one to the first one
+        System.out.println("BBA> difference= " + difference + " ratio= " + ratio 
+                           + " posizione= " + bestActiveNode.position);
+        insertPageBreakAsFirst(new PageBreakPosition(this.topLevelLM, 
+                bestActiveNode.position, ratio, difference));
+    }
+
+    protected int filterActiveNodes() {
+        // leave only the active node with fewest total demerits
+        KnuthNode bestActiveNode = null;
+        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;
+    }
+
+}
\ No newline at end of file
index d2755fea38b53ca8baaef057f0fd12f881e4657e..f03973694cccb76f5e7c02cc96fd9fcbf4deee0a 100644 (file)
@@ -58,7 +58,7 @@ public class PageNumberCitationLayoutManager extends LeafNodeLayoutManager {
     public void addAreas(PositionIterator posIter, LayoutContext context) {
         super.addAreas(posIter, context);
         if (!resolved) {
-            parentLM.addUnresolvedArea(fobj.getRefId(), (Resolvable) curArea);
+            getPSLM().addUnresolvedArea(fobj.getRefId(), (Resolvable) curArea);
         }
     }
     
@@ -71,7 +71,7 @@ public class PageNumberCitationLayoutManager extends LeafNodeLayoutManager {
      * return a resolvable area
      */
     private InlineArea getPageNumberCitationInlineArea(LayoutManager parentLM) {
-        PageViewport page = parentLM.resolveRefID(fobj.getRefId());
+        PageViewport page = getPSLM().getFirstPVWithID(fobj.getRefId());
         InlineArea inline = null;
         if (page != null) {
             String str = page.getPageNumberString();
@@ -118,7 +118,7 @@ public class PageNumberCitationLayoutManager extends LeafNodeLayoutManager {
     }
     
     protected void addId() {
-        addID(fobj.getId());
+        getPSLM().addIDToPage(fobj.getId());
     }
 }
 
index 89298bb699e3975bd21422f5e5025d1fe0165ce1..382ff9dfd5047fd4cb27bd6055da3b0dcaf3b808 100644 (file)
@@ -46,7 +46,7 @@ public class PageNumberLayoutManager extends LeafNodeLayoutManager {
     public InlineArea get(LayoutContext context) {
         // get page string from parent, build area
         TextArea inline = new TextArea();
-        String str = parentLM.getCurrentPageNumberString();
+        String str = getCurrentPV().getPageNumberString();
         int width = 0;
         for (int count = 0; count < str.length(); count++) {
             width += font.getCharWidth(str.charAt(count));
@@ -69,7 +69,7 @@ public class PageNumberLayoutManager extends LeafNodeLayoutManager {
     }
     
     protected void addId() {
-        addID(fobj.getId());
+        getPSLM().addIDToPage(fobj.getId());
     }
 }
 
index d0cc95e46ffc264378f9d52eb45c2325a7fb93c7..bd7cac98a2e2b582c7f2f7e8859f35c4fd8d937a 100644 (file)
@@ -20,119 +20,93 @@ package org.apache.fop.layoutmgr;
 
 import org.apache.fop.apps.FOPException;
 
-import org.apache.fop.area.CTM;
 import org.apache.fop.area.AreaTreeHandler;
 import org.apache.fop.area.AreaTreeModel;
 import org.apache.fop.area.Area;
 import org.apache.fop.area.PageViewport;
-import org.apache.fop.area.NormalFlow;
 import org.apache.fop.area.LineArea;
-import org.apache.fop.area.Page;
 import org.apache.fop.area.RegionViewport;
-import org.apache.fop.area.RegionReference;
-import org.apache.fop.area.BodyRegion;
-import org.apache.fop.area.Span;
-import org.apache.fop.area.BeforeFloat;
-import org.apache.fop.area.Footnote;
 import org.apache.fop.area.Resolvable;
-import org.apache.fop.area.Trait;
 
 import org.apache.fop.datatypes.PercentBase;
-import org.apache.fop.datatypes.FODimension;
 
-import org.apache.fop.fo.FObj;
 import org.apache.fop.fo.Constants;
 import org.apache.fop.fo.flow.Marker;
+import org.apache.fop.fo.flow.RetrieveMarker;
 import org.apache.fop.fo.pagination.PageSequence;
 import org.apache.fop.fo.pagination.Region;
-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.fo.properties.CommonMarginBlock;
 
+import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
-import java.awt.Rectangle;
-import java.util.Iterator;
-import java.awt.geom.Rectangle2D;
-import org.apache.fop.traits.MinOptMax;
 
 /**
- * LayoutManager for a PageSequence.
+ * LayoutManager for a PageSequence.  This class is instantiated by
+ * area.AreaTreeHandler for each fo:page-sequence found in the
+ * input document.
  */
 public class PageSequenceLayoutManager extends AbstractLayoutManager {
-    private PageSequence pageSeq;
-
-    private static class BlockBreakPosition extends LeafPosition {
-        protected BreakPoss breakps;
-
-        protected BlockBreakPosition(LayoutManager lm, BreakPoss bp) {
-            super(lm, 0);
-            breakps = bp;
-        }
-    }
-
-    private int startPageNum = 0;
-    private int currentPageNum = 0;
-    private String pageNumberString;
-    private boolean isFirstPage = true;
-
-    /** Current page being worked on. */
-    private PageViewport curPage;
-
-    /** Current span being filled */
-    private Span curSpan;
-
-    /** Current normal-flow-reference-area being filled. */
-    private NormalFlow curFlow;
-
-    private int flowBPD = 0;
-    private int flowIPD = 0;
 
     /** 
-     * AreaTreeHandler which activates this PSLM.
+     * AreaTreeHandler which activates the PSLM and controls
+     * the rendering of its pages.
      */
     private AreaTreeHandler areaTreeHandler;
 
     /** 
-     * AreaTreeModel that this PSLM sends pages to.
+     * fo:page-sequence formatting object being
+     * processed by this class
      */
-    private AreaTreeModel areaTreeModel;
+    private PageSequence pageSeq;
+
+    /** 
+     * Current page-viewport-area being filled by
+     * the PSLM.
+     */
+    private PageViewport curPV = null;
 
     /**
-     * This is the SimplePageMaster that should be used to create the page. It
-     * will be equal to the PageSequence's simplePageMaster, if it exists, or
-     * to the correct member of the PageSequence's pageSequenceMaster, if that
-     * is in effect instead.
+     * Zero-based index of column (Normal Flow) in span (of the PV) 
+     * being filled.  See XSL Rec description of fo:region-body 
+     * and fop.Area package classes for more information. 
      */
-    private SimplePageMaster currentSimplePageMaster;
+    private int curFlowIdx = -1;
 
     /**
-     * Constructor - activated by AreaTreeHandler for each
-     * fo:page-sequence in the input FO stream
-     *
-     * @param pageseq the page-sequence formatting object
+     * The FlowLayoutManager object, which processes
+     * the single fo:flow of the fo:page-sequence
      */
-    public PageSequenceLayoutManager(PageSequence pageSeq) {
-        super(pageSeq);
-        this.pageSeq = pageSeq;
-    }
+    private FlowLayoutManager childFLM = null;
 
     /**
-     * Set the AreaTreeHandler
-     * @param areaTreeHandler the area tree handler object
+     * The collection of StaticContentLayoutManager objects that
+     * are associated with this Page Sequence, keyed by flow-name.
      */
-    public void setAreaTreeHandler(AreaTreeHandler areaTreeHandler) {
-        this.areaTreeHandler = areaTreeHandler;
-        areaTreeModel = areaTreeHandler.getAreaTreeModel();
+    //private HashMap staticContentLMs = new HashMap(4);
+
+    private int startPageNum = 0;
+    private int currentPageNum = 0;
+
+    /**
+     * Constructor
+     *
+     * @param ath the area tree handler object
+     * @param pseq fo:page-sequence to process
+     */
+    public PageSequenceLayoutManager(AreaTreeHandler ath, PageSequence pseq) {
+        super(pseq);
+        this.areaTreeHandler = ath;
+        this.pageSeq = pseq;
     }
 
     /**
      * @see org.apache.fop.layoutmgr.LayoutManager
-     * @return the AreaTreeHandler object
+     * @return the LayoutManagerMaker object
      */
-    public AreaTreeHandler getAreaTreeHandler() {
-        return areaTreeHandler;
+    public LayoutManagerMaker getLayoutManagerMaker() {
+        return areaTreeHandler.getLayoutManagerMaker();
     }
 
     /**
@@ -142,42 +116,106 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
      */
     public void activateLayout() {
         startPageNum = pageSeq.getStartingPageNumber();
-        currentPageNum = startPageNum;
-        pageNumberString = pageSeq.makeFormattedPageNumber(currentPageNum);
+        currentPageNum = startPageNum - 1;
 
         LineArea title = null;
 
         if (pageSeq.getTitleFO() != null) {
-            ContentLayoutManager clm = 
-                new ContentLayoutManager(pageSeq.getTitleFO(), this);
-            title = (LineArea) clm.getParentArea(null); // can improve
+            ContentLayoutManager clm = new ContentLayoutManager(pageSeq
+                    .getTitleFO(), this);
+            title = (LineArea) clm.getParentArea(null);
         }
 
-        areaTreeModel.startPageSequence(title);
+        areaTreeHandler.getAreaTreeModel().startPageSequence(title);
         log.debug("Starting layout");
 
-        makeNewPage(false, false);
-        flowIPD = curFlow.getIPD();
-
-        BreakPoss bp;
-        LayoutContext childLC = new LayoutContext(0);
-        while (!isFinished()) {
-            if ((bp = getNextBreakPoss(childLC)) != null) {
-                addAreas((BlockBreakPosition)bp.getPosition());
-                // add static areas and resolve any new id areas
-                // finish page and add to area tree
-                finishPage();
-                currentPageNum++;
-                pageNumberString = pageSeq.makeFormattedPageNumber(currentPageNum);
-            }
-        }
-        // TODO: Don't decrement currentPageNum when no pages are generated
-        currentPageNum--;
+        curPV = makeNewPage(false, true, false);
+
+        PageBreaker breaker = new PageBreaker(this);
+        int flowBPD = (int) curPV.getBodyRegion().getBPD();
+        breaker.doLayout(flowBPD);
+        
         finishPage();
-        pageSeq.getRoot().notifyPageSequenceFinished(currentPageNum, (currentPageNum - startPageNum) + 1);
+        pageSeq.getRoot().notifyPageSequenceFinished(currentPageNum,
+                (currentPageNum - startPageNum) + 1);
         log.debug("Ending layout");
     }
 
+    private class PageBreaker extends AbstractBreaker {
+        
+        private PageSequenceLayoutManager pslm;
+        private boolean firstPart = true;
+        
+        public PageBreaker(PageSequenceLayoutManager pslm) {
+            this.pslm = pslm;
+        }
+        
+        protected LayoutContext createLayoutContext() {
+            LayoutContext lc = new LayoutContext(0);
+            int flowIPD = curPV.getCurrentSpan().getColumnWidth();
+            lc.setRefIPD(flowIPD);
+            return lc;
+        }
+        
+        protected LayoutManager getTopLevelLM() {
+            return pslm;
+        }
+        
+        protected LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+            return pslm.getNextKnuthElements(context, alignment);
+        }
+        
+        protected int getCurrentDisplayAlign() {
+            return curPV.getSPM().getRegion(Constants.FO_REGION_BODY).getDisplayAlign();
+        }
+        
+        protected boolean hasMoreContent() {
+            return !isFinished();
+        }
+        
+        protected void addAreas(PositionIterator posIter, LayoutContext context) {
+            getCurrentChildLM().addAreas(posIter, context);    
+        }
+        
+        protected void doPhase3(PageBreakingAlgorithm alg, int partCount, 
+                BlockSequence originalList, BlockSequence effectiveList) {
+            //Directly add areas after finding the breaks
+            addAreas(alg, partCount, originalList, effectiveList);
+        }
+        
+        protected void startPart(BlockSequence list, boolean bIsFirstPage) {
+            if (curPV == null) {
+                throw new IllegalStateException("curPV must not be null");
+            } else {
+                //firstPart is necessary because we need the first page before we start the 
+                //algorithm so we have a BPD and IPD. This may subject to change later when we
+                //start handling more complex cases.
+                if (!firstPart) {
+                    if (curFlowIdx < curPV.getCurrentSpan().getColumnCount()-1) {
+                        curFlowIdx++;
+                    } else  {
+                        // if this is the first page that will be created by
+                        // the current BlockSequence, it could have a break
+                        // condition that must be satisfied;
+                        // otherwise, we may simply need a new page
+                        handleBreakTrait(bIsFirstPage ? list.getStartOn() : Constants.EN_PAGE);
+                    }
+                }
+            }
+            // add static areas and resolve any new id areas
+            // finish page and add to area tree
+            firstPart = false;
+        }
+        
+        protected void finishPart() {
+        }
+        
+        protected LayoutManager getCurrentChildLM() {
+            return childFLM;
+        }
+        
+    }
+    
     /** @see org.apache.fop.layoutmgr.LayoutManager#isBogus() */
     public boolean isBogus() {
         return false;
@@ -191,79 +229,70 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
      * @param context the layout context for finding breaks
      * @return the break for the page
      */
-    public BreakPoss getNextBreakPoss(LayoutContext context) {
+    public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
 
         LayoutManager curLM; // currently active LM
 
         while ((curLM = getChildLM()) != null) {
-            BreakPoss bp = null;
+/*LF*/      LinkedList returnedList = null;
+/*LF*/      if (childFLM == null && (curLM instanceof FlowLayoutManager)) {
+/*LF*/          childFLM = (FlowLayoutManager)curLM;
+/*LF*/      } else {
+/*LF*/          if (curLM != childFLM) {
+/*LF*/              System.out.println("PLM> figlio sconosciuto (invalid child LM)");
+/*LF*/          }
+/*LF*/      }
 
             LayoutContext childLC = new LayoutContext(0);
-            childLC.setStackLimit(new MinOptMax(flowBPD));
-            childLC.setRefIPD(flowIPD);
+            childLC.setStackLimit(context.getStackLimit());
+            childLC.setRefIPD(context.getRefIPD());
 
             if (!curLM.isFinished()) {
+                int flowIPD = curPV.getCurrentSpan().getColumnWidth();
+                int flowBPD = (int) curPV.getBodyRegion().getBPD();
                 pageSeq.setLayoutDimension(PercentBase.REFERENCE_AREA_IPD, flowIPD);
                 pageSeq.setLayoutDimension(PercentBase.REFERENCE_AREA_BPD, flowBPD);
-                bp = curLM.getNextBreakPoss(childLC);
+/*LF*/          returnedList = curLM.getNextKnuthElements(childLC, alignment);
             }
-            if (bp != null) {
-                return new BreakPoss(
-                         new BlockBreakPosition(curLM, bp));
+            if (returnedList != null) {
+                return returnedList;
             }
         }
         setFinished(true);
         return null;
     }
 
-    /**
-     * Get the current page number string.
-     * This returns the formatted string for the current page.
-     *
-     * @return the formatted page number string
-     */
-    public String getCurrentPageNumberString() {
-        return pageNumberString;
-    }
-    
     /**
      * Provides access to the current page.
      * @return the current PageViewport
      */
-    public PageViewport getCurrentPageViewport() {
-        return this.curPage;
+    public PageViewport getCurrentPV() {
+        return curPV;
     }
 
     /**
-     * Resolve a reference ID.
-     * This resolves a reference ID and returns the first PageViewport
-     * that contains the reference ID or null if reference not found.
+     * Provides access to this object
+     * @return this PageSequenceLayoutManager instance
+     */
+    public PageSequenceLayoutManager getPSLM() {
+        return this;
+    }
+    
+    /**
+     * This returns the first PageViewport that contains an id trait
+     * matching the idref argument, or null if no such PV exists.
      *
-     * @param id the reference ID to lookup
-     * @return the first page viewport that contains the reference
+     * @param idref the idref trait needing to be resolved 
+     * @return the first PageViewport that contains the ID trait
      */
-    public PageViewport resolveRefID(String id) {
-        List list = areaTreeHandler.getPageViewportsContainingID(id);
+    public PageViewport getFirstPVWithID(String idref) {
+        List list = areaTreeHandler.getPageViewportsContainingID(idref);
         if (list != null && list.size() > 0) {
             return (PageViewport) list.get(0);
         }
         return null;
     }
 
-    /**
-     * Add the areas to the current page.
-     * Given the page break position this adds the areas to the current
-     * page.
-     *
-     * @param bbp the block break position
-     */
-    public void addAreas(BlockBreakPosition bbp) {
-        List list = new java.util.ArrayList();
-        list.add(bbp.breakps);
-        bbp.getLM().addAreas(new BreakPossPosIter(list, 0,
-                              1), null);
-    }
-
     /**
      * Add an ID reference to the current page.
      * When adding areas the area adds its ID reference.
@@ -273,40 +302,34 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
      * @param id the ID reference to add
      */
     public void addIDToPage(String id) {
-        areaTreeHandler.associateIDWithPageViewport(id, curPage);
+        if (id != null && id.length() > 0) {
+            areaTreeHandler.associateIDWithPageViewport(id, curPV);
+        }
     }
 
     /**
-     * Add an unresolved area to the layout manager.
-     * The Page layout manager handles the unresolved ID
-     * reference by adding to the current page and then adding
-     * the page as a resolvable to the area tree.
-     * This is so that the area tree can resolve the reference
-     * and the page can serialize the resolvers if required.
+     * Identify an unresolved area (one needing an idref to be 
+     * resolved, e.g. the internal-destination of an fo:basic-link)
+     * for both the AreaTreeHandler and PageViewport object.
+     * 
+     * The AreaTreeHandler keeps a document-wide list of idref's
+     * and the PV's needing them to be resolved.  It uses this to  
+     * send notifications to the PV's when an id has been resolved.
+     * 
+     * The PageViewport keeps lists of id's needing resolving, along
+     * with the child areas (page-number-citation, basic-link, etc.)
+     * of the PV needing their resolution.
      *
      * @param id the ID reference to add
      * @param res the resolvable object that needs resolving
      */
     public void addUnresolvedArea(String id, Resolvable res) {
-        // add to the page viewport so it can serialize
-        curPage.addUnresolvedIDRef(id, res);
-        // add unresolved to tree
-        areaTreeHandler.addUnresolvedIDRef(id, curPage);
-    }
-
-    /**
-     * Add the marker to the page layout manager.
-     *
-     * @see org.apache.fop.layoutmgr.LayoutManager
-     */
-    public void addMarkerMap(Map marks, boolean starting, boolean isfirst, boolean islast) {
-        //getLogger().debug("adding markers: " + marks + ":" + start);
-        // add markers to page on area tree
-        curPage.addMarkers(marks, starting, isfirst, islast);
+        curPV.addUnresolvedIDRef(id, res);
+        areaTreeHandler.addUnresolvedIDRef(id, curPV);
     }
 
     /**
-     * Retrieve a marker from this layout manager.
+     * Bind the RetrieveMarker to the corresponding Marker subtree.
      * If the boundary is page then it will only check the
      * current page. For page-sequence and document it will
      * lookup preceding pages from the area tree and try to find
@@ -317,14 +340,19 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
      * Therefore we use last-ending-within-page (Constants.EN_LEWP)
      * as the position. 
      *
-     * @param name the marker class name to lookup
-     * @param pos the position to locate the marker
-     * @param boundary the boundary for locating the marker
-     * @return the layout manager for the marker contents
+     * @param rm the RetrieveMarker instance whose properties are to
+     * used to find the matching Marker.
+     * @return a bound RetrieveMarker instance, or null if no Marker
+     * could be found.
      */
-    public Marker retrieveMarker(String name, int pos, int boundary) {
+    public RetrieveMarker resolveRetrieveMarker(RetrieveMarker rm) {
+        AreaTreeModel areaTreeModel = areaTreeHandler.getAreaTreeModel();
+        String name = rm.getRetrieveClassName();
+        int pos = rm.getRetrievePosition();
+        int boundary = rm.getRetrieveBoundary();               
+        
         // get marker from the current markers on area tree
-        Marker mark = (Marker)curPage.getMarker(name, pos);
+        Marker mark = (Marker)curPV.getMarker(name, pos);
         if (mark == null && boundary != EN_PAGE) {
             // go back over pages until mark found
             // if document boundary then keep going
@@ -339,7 +367,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
                 PageViewport pv = areaTreeModel.getPage(seq, page);
                 mark = (Marker)pv.getMarker(name, Constants.EN_LEWP);
                 if (mark != null) {
-                    return mark;
+                    break;
                 }
                 page--;
                 if (page < 0 && doc && seq > 1) {
@@ -351,71 +379,55 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
 
         if (mark == null) {
             log.debug("found no marker with name: " + name);
+            return null;
+        } else {
+            rm.bindMarker(mark);
+            return rm;
         }
-
-        return mark;
     }
 
-    /**
-     * For now, only handle normal flow areas.
-     * @see org.apache.fop.layoutmgr.LayoutManager#addChildArea(org.apache.fop.area.Area)
-     */
-    public void addChildArea(Area childArea) {
-        if (childArea == null) {
-            return;
-        }
-        if (childArea.getAreaClass() == Area.CLASS_NORMAL) {
-            getParentArea(childArea);
-        } else {
-             // todo: all the others!
+    private PageViewport makeNewPage(boolean bIsBlank, boolean bIsFirst, boolean bIsLast) {
+        if (curPV != null) {
+            finishPage();
         }
-    }
 
-    private PageViewport makeNewPage(boolean bIsBlank, boolean bIsLast) {
-        finishPage();
+        currentPageNum++;
+        String pageNumberString = pageSeq.makeFormattedPageNumber(currentPageNum);
 
         try {
             // create a new page
-            currentSimplePageMaster = pageSeq.getSimplePageMasterToUse(
-                currentPageNum, isFirstPage, bIsBlank);
-            Region body = currentSimplePageMaster.getRegion(FO_REGION_BODY);
+            SimplePageMaster spm = pageSeq.getSimplePageMasterToUse(
+                currentPageNum, bIsFirst, bIsBlank);
+            
+            Region body = spm.getRegion(FO_REGION_BODY);
             if (!pageSeq.getMainFlow().getFlowName().equals(body.getRegionName())) {
+              // this is fine by the XSL Rec (fo:flow's flow-name can be mapped to
+              // any region), but we don't support it yet.
               throw new FOPException("Flow '" + pageSeq.getMainFlow().getFlowName()
                  + "' does not map to the region-body in page-master '"
-                 + currentSimplePageMaster.getMasterName() + "'");
+                 + spm.getMasterName() + "'.  FOP presently "
+                 + "does not support this.");
             }
-            curPage = createPageAreas(currentSimplePageMaster);
-            isFirstPage = false;
+            curPV = new PageViewport(spm);
         } catch (FOPException fopex) {
             throw new IllegalArgumentException("Cannot create page: " + fopex.getMessage());
         }
 
-        curPage.setPageNumberString(pageNumberString);
+        curPV.setPageNumberString(pageNumberString);
         if (log.isDebugEnabled()) {
-            log.debug("[" + curPage.getPageNumberString() + "]");
+            log.debug("[" + curPV.getPageNumberString() + (bIsBlank ? "*" : "") + "]");
         }
 
-        flowBPD = (int) curPage.getBodyRegion().getBPD();
-        createSpan(curPage.getBodyRegion().getColumnCount());
-        return curPage;
+        curPV.createSpan(false);
+        curFlowIdx = 0;
+        return curPV;
     }
 
-    private void createSpan(int numCols) {
-        // get Width or Height as IPD for span
-        RegionViewport rv = curPage.getPage().getRegionViewport(FO_REGION_BODY);
-        int ipdWidth = (int) rv.getRegion().getIPD() -
-            rv.getBorderAndPaddingWidthStart() - rv.getBorderAndPaddingWidthEnd();
-
-        // currently hardcoding to one column, replace with numCols when ready
-        curSpan = new Span(1 /* numCols */, ipdWidth);
-
-        //curSpan.setPosition(BPD, newpos);
-        curPage.getBodyRegion().getMainReference().addSpan(curSpan);
-        curFlow = curSpan.getNormalFlow(0);
-    }
-
-    private void layoutStaticContent(int regionID) {
-        Region reg = currentSimplePageMaster.getRegion(regionID);
+    /* TODO: See if can initialize the SCLM's just once for
+     * the page sequence, instead of after every page.
+     */
+    private void layoutSideRegion(int regionID) {
+        SideRegion reg = (SideRegion)curPV.getSPM().getRegion(regionID);
         if (reg == null) {
             return;
         }
@@ -423,59 +435,31 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
         if (sc == null) {
             return;
         }
-        
-        RegionViewport rv = curPage.getPage().getRegionViewport(regionID);
+
+        RegionViewport rv = curPV.getPage().getRegionViewport(regionID);
         StaticContentLayoutManager lm;
-        try {
-            lm = (StaticContentLayoutManager)
-                areaTreeHandler.getLayoutManagerMaker().makeLayoutManager(sc);
-        } catch (FOPException e) {
-            log.error
-                ("Failed to create a StaticContentLayoutManager for flow "
-                 + sc.getFlowName()
-                 + "; no static content will be laid out:");
-            log.error(e.getMessage());
-            return;
-        }
-        lm.initialize();
-        lm.setRegionReference(rv.getRegion());
-        lm.setParent(this);
-        LayoutContext childLC = new LayoutContext(0);
-        childLC.setStackLimit(new MinOptMax((int)curPage.getViewArea().getHeight()));
-        childLC.setRefIPD(rv.getRegion().getIPD());
-        while (!lm.isFinished()) {
-            BreakPoss bp = lm.getNextBreakPoss(childLC);
-            if (bp != null) {
-                List vecBreakPoss = new java.util.ArrayList();
-                vecBreakPoss.add(bp);
-                lm.addAreas(new BreakPossPosIter(vecBreakPoss, 0,
-                                                 vecBreakPoss.size()), null);
-            } else {
-                log.error("bp==null  cls=" + reg.getRegionName());
-            }
-        }
-        //lm.flush();
+        lm = (StaticContentLayoutManager)
+            areaTreeHandler.getLayoutManagerMaker().makeLayoutManager(sc);
+        lm.setTargetRegion(rv.getRegionReference());
+        lm.setParent(this);       
+        lm.doLayout(reg);
         lm.reset(null);
     }
 
     private void finishPage() {
-        if (curPage == null) {
-            curSpan = null;
-            curFlow = null;
-            return;
-        }
-        // Layout static content into the regions
-        layoutStaticContent(FO_REGION_BEFORE); 
-        layoutStaticContent(FO_REGION_AFTER);
-        layoutStaticContent(FO_REGION_START);
-        layoutStaticContent(FO_REGION_END);
+        // Layout side regions
+        layoutSideRegion(FO_REGION_BEFORE); 
+        layoutSideRegion(FO_REGION_AFTER);
+        layoutSideRegion(FO_REGION_START);
+        layoutSideRegion(FO_REGION_END);
         // Queue for ID resolution and rendering
-        areaTreeModel.addPage(curPage);
-        curPage = null;
-        curSpan = null;
-        curFlow = null;
+        areaTreeHandler.getAreaTreeModel().addPage(curPV);
+        log.debug("page finished: " + curPV.getPageNumberString() 
+                + ", current num: " + currentPageNum);
+        curPV = null;
+        curFlowIdx = -1;
     }
-
+    
     /**
      * This is called from FlowLayoutManager when it needs to start
      * a new flow container (while generating areas).
@@ -487,103 +471,41 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
      */
     public Area getParentArea(Area childArea) {
         int aclass = childArea.getAreaClass();
+
         if (aclass == Area.CLASS_NORMAL) {
-            // todo: how to get properties from the Area???
-            // Need span, break
-            int breakVal = Constants.EN_AUTO;
-            Integer breakBefore = (Integer)childArea.getTrait(Trait.BREAK_BEFORE);
-            if (breakBefore != null) {
-                breakVal = breakBefore.intValue();
-            }
-            if (breakVal != Constants.EN_AUTO) {
-                // We may be forced to make new page
-                handleBreak(breakVal);
-            } else if (curPage == null) {
-                log.debug("curPage is null. Making new page");
-                makeNewPage(false, false);
-            }
-            // Now we should be on the right kind of page
-            boolean bNeedNewSpan = false;
-            /* Determine if a new span is needed.  From the XSL
-             * fo:region-body definition, if an fo:block has a span="ALL"
-             * (i.e., span all columns defined for the region-body), it
-             * must be placed in a span-reference-area whose 
-             * column-count = 1.  If its span-value is "NONE", 
-             * place in a normal Span whose column-count is what
-             * is defined for the region-body. 
-             */  // temporarily hardcoded to EN_NONE.
-            int span = Constants.EN_NONE; // childArea.getSpan()
-            int numColsNeeded;
-            if (span == Constants.EN_ALL) {
-                numColsNeeded = 1;
-            } else { // EN_NONE
-                numColsNeeded = curPage.getBodyRegion().getColumnCount();
-            }
-            if (curSpan == null) {  // should never happen, remove?
-                bNeedNewSpan = true;
-            } else if (numColsNeeded != curSpan.getColumnCount()) {
-                // need a new Span, with numColsNeeded columns
-                if (curSpan.getColumnCount() > 1) {
-                    // finished with current span, so balance 
-                    // its columns to make them the same "height"
-                    // balanceColumns();  // TODO: implement
-                }
-                bNeedNewSpan = true;
-            }
-            if (bNeedNewSpan) {
-                createSpan(numColsNeeded);
-            } else if (curFlow == null) {  // should not happen
-                curFlow = curSpan.addAdditionalNormalFlow();
-            }
-            return curFlow;
-        } else {
-            if (curPage == null) {
-                makeNewPage(false, false);
-            }
-            // Now handle different kinds of areas
-            if (aclass == Area.CLASS_BEFORE_FLOAT) {
-                BeforeFloat bf = curPage.getBodyRegion().getBeforeFloat();
-                if (bf == null) {
-                    bf = new BeforeFloat();
-                    curPage.getBodyRegion().setBeforeFloat(bf);
-                }
-                return bf;
-            } else if (aclass == Area.CLASS_FOOTNOTE) {
-                Footnote fn = curPage.getBodyRegion().getFootnote();
-                if (fn == null) {
-                    fn = new Footnote();
-                    curPage.getBodyRegion().setFootnote(fn);
-                }
-                return fn;
-            }
-            // todo!!! other area classes (side-float, absolute, fixed)
-            return null;
+            return curPV.getCurrentSpan().getNormalFlow(curFlowIdx);
+        } else if (aclass == Area.CLASS_BEFORE_FLOAT) {
+            return curPV.getBodyRegion().getBeforeFloat();
+        } else if (aclass == Area.CLASS_FOOTNOTE) {
+            return curPV.getBodyRegion().getFootnote();
         }
+        // todo!!! other area classes (side-float, absolute, fixed)
+        return null;
     }
 
     /**
      * Depending on the kind of break condition, make new column
      * or page. May need to make an empty page if next page would
      * not have the desired "handedness".
-     *
-     * @param breakVal the break value to handle
+     * @param breakVal - value of break-before or break-after trait.
      */
-    private void handleBreak(int breakVal) {
+    private void handleBreakTrait(int breakVal) {
         if (breakVal == Constants.EN_COLUMN) {
-            if (curSpan != null
-                    && curSpan.getNormalFlowCount() < curSpan.getColumnCount()) {
+            if (curFlowIdx < curPV.getCurrentSpan().getColumnCount()) {
                 // Move to next column
-                curFlow = curSpan.addAdditionalNormalFlow();
-                return;
+                curFlowIdx++;
+            } else {
+                curPV = makeNewPage(false, false, false);
             }
-            // else need new page
-            breakVal = Constants.EN_PAGE;
+            return;
         }
-        if (needEmptyPage(breakVal)) {
-            curPage = makeNewPage(true, false);
+        log.debug("handling break-before after page " + currentPageNum 
+            + " breakVal=" + breakVal);
+        if (needBlankPageBeforeNew(breakVal)) {
+            curPV = makeNewPage(true, false, false);
         }
         if (needNewPage(breakVal)) {
-            curPage = makeNewPage(false, false);
+            curPV = makeNewPage(false, false, false);
         }
     }
 
@@ -594,130 +516,39 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
      * Note that if not all content is placed, we aren't sure whether
      * it will flow onto another page or not, so we'd probably better
      * block until the queue of layoutable stuff is empty!
+     * @param breakVal - value of break-before or break-after trait.
      */
-    private boolean needEmptyPage(int breakValue) {
-
-        if (breakValue == Constants.EN_PAGE || curPage.getPage().isEmpty()) {
+    private boolean needBlankPageBeforeNew(int breakVal) {
+        if (breakVal == Constants.EN_PAGE || (curPV.getPage().isEmpty())) {
             // any page is OK or we already have an empty page
             return false;
-        }
-        else {
+        } else {
             /* IF we are on the kind of page we need, we'll need a new page. */
-            if (currentPageNum%2 != 0) {
-                // Current page is odd
-                return (breakValue == Constants.EN_ODD_PAGE);
-            }
-            else {
-                return (breakValue == Constants.EN_EVEN_PAGE);
+            if (currentPageNum % 2 == 0) { // even page
+                return (breakVal == Constants.EN_EVEN_PAGE);
+            } else { // odd page
+                return (breakVal == Constants.EN_ODD_PAGE);
             }
         }
     }
 
     /**
-     * See if need to generate a new page for a forced break condition.
+     * See if need to generate a new page
+     * @param breakVal - value of break-before or break-after trait.
      */
-    private boolean needNewPage(int breakValue) {
-        if (curPage != null && curPage.getPage().isEmpty()) {
-            if (breakValue == Constants.EN_PAGE) {
+    private boolean needNewPage(int breakVal) {
+        if (curPV.getPage().isEmpty()) {
+            if (breakVal == Constants.EN_PAGE) {
                 return false;
             }
-            else if (currentPageNum%2 != 0) {
-                // Current page is odd
-                return (breakValue == Constants.EN_EVEN_PAGE);
+            else if (currentPageNum % 2 == 0) { // even page
+                return (breakVal == Constants.EN_ODD_PAGE);
             }
-            else {
-                return (breakValue == Constants.EN_ODD_PAGE);
+            else { // odd page
+                return (breakVal == Constants.EN_EVEN_PAGE);
             }
-        }
-        else {
+        } else {
             return true;
         }
     }
-
-    private PageViewport createPageAreas(SimplePageMaster spm) {
-        int pageWidth = spm.getPageWidth().getValue();
-        int pageHeight = spm.getPageHeight().getValue();
-
-        // Set the page dimension as the toplevel containing block for margin.
-        ((FObj) pageSeq.getParent()).setLayoutDimension(PercentBase.BLOCK_IPD, pageWidth);
-        ((FObj) pageSeq.getParent()).setLayoutDimension(PercentBase.BLOCK_BPD, pageHeight);
-
-        // Get absolute margin properties (top, left, bottom, right)
-        CommonMarginBlock mProps = spm.getCommonMarginBlock();
-
-      /* Create the page reference area rectangle (0,0 is at top left
-       * of the "page media" and y increases
-       * when moving towards the bottom of the page.
-       * The media rectangle itself is (0,0,pageWidth,pageHeight).
-       */
-       Rectangle pageRefRect =
-               new Rectangle(mProps.marginLeft.getValue(), mProps.marginTop.getValue(),
-                       pageWidth - mProps.marginLeft.getValue() - mProps.marginRight.getValue(),
-                       pageHeight - mProps.marginTop.getValue() - mProps.marginBottom.getValue());
-
-       Page page = new Page();  // page reference area
-
-       // Set up the CTM on the page reference area based on writing-mode
-       // and reference-orientation
-       FODimension reldims = new FODimension(0, 0);
-       CTM pageCTM = CTM.getCTMandRelDims(spm.getReferenceOrientation(),
-               spm.getWritingMode(), pageRefRect, reldims);
-
-       // Create a RegionViewport/ reference area pair for each page region
-       RegionReference rr = null;
-       for (Iterator regenum = spm.getRegions().values().iterator();
-            regenum.hasNext();) {
-           Region r = (Region)regenum.next();
-           RegionViewport rvp = makeRegionViewport(r, reldims, pageCTM);
-           r.setLayoutDimension(PercentBase.BLOCK_IPD, rvp.getIPD());
-           r.setLayoutDimension(PercentBase.BLOCK_BPD, rvp.getBPD());
-           if (r.getNameId() == FO_REGION_BODY) {
-               rr = new BodyRegion((RegionBody) r);
-           } else {
-               rr = new RegionReference(r.getNameId());
-           }
-           setRegionPosition(r, rr, rvp.getViewArea());
-           rvp.setRegion(rr);
-           page.setRegionViewport(r.getNameId(), rvp);
-       }
-
-       return new PageViewport(page, new Rectangle(0, 0, pageWidth, pageHeight));
-    }  
-    
-    /**
-     * Creates a RegionViewport Area object for this pagination Region.
-     * @param reldims relative dimensions
-     * @param pageCTM page coordinate transformation matrix
-     * @return the new region viewport
-     */
-    private RegionViewport makeRegionViewport(Region r, FODimension reldims, CTM pageCTM) {
-        Rectangle2D relRegionRect = r.getViewportRectangle(reldims);
-        Rectangle2D absRegionRect = pageCTM.transform(relRegionRect);
-        // Get the region viewport rectangle in absolute coords by
-        // transforming it using the page CTM
-        RegionViewport rv = new RegionViewport(absRegionRect);
-        rv.setBPD((int)relRegionRect.getHeight());
-        rv.setIPD((int)relRegionRect.getWidth());
-        TraitSetter.addBackground(rv, r.getCommonBorderPaddingBackground());
-        return rv;
-    }
-   
-    /**
-     * Set the region position inside the region viewport.
-     * This sets the transform that is used to place the contents of
-     * the region.
-     *
-     * @param r the region reference area
-     * @param absRegVPRect The region viewport rectangle in "absolute" coordinates
-     * where x=distance from left, y=distance from bottom, width=right-left
-     * height=top-bottom
-     */
-    private void setRegionPosition(Region r, RegionReference rr,
-                                  Rectangle2D absRegVPRect) {
-        FODimension reldims = new FODimension(0, 0);
-        rr.setCTM(CTM.getCTMandRelDims(r.getReferenceOrientation(),
-                r.getWritingMode(), absRegVPRect, reldims));
-        rr.setIPD(reldims.ipd);
-        rr.setBPD(reldims.bpd);
-    }
 }
index 8c03696375a62d2a966f6c27f36a4e8cd14e471e..40fc573a957c8c24c29c862f70ddea9217e71c4c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -37,5 +37,18 @@ public class Position {
     public Position getPosition() {
         return null;
     }
+    
+    
+    /** @see java.lang.Object#toString() */
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("Position");
+        if (getLM() != null) {
+            sb.append(" {");
+            sb.append(getLM());
+            sb.append("}");
+        }
+        return sb.toString();
+    }
 }
 
index 3ecf564862ceed40b8ce7f176d4ba361cea42615..ae38b96d5978114101db20c168132b5ea5df374d 100644 (file)
@@ -28,7 +28,7 @@ public abstract class PositionIterator implements Iterator {
     private LayoutManager childLM;
     private boolean bHasNext;
 
-    PositionIterator(Iterator pIter) {
+    protected PositionIterator(Iterator pIter) {
         parentIter = pIter;
         lookAhead();
         //checkNext();
index 5effaf429dd661c536171629a131ab0c412b4458..aaed403d4cb7f34a685018d590cfffa8abbeda46 100644 (file)
@@ -21,11 +21,16 @@ package org.apache.fop.layoutmgr;
 import org.apache.fop.area.RegionReference;
 import org.apache.fop.area.Area;
 import org.apache.fop.area.Block;
+import org.apache.fop.datatypes.PercentBase;
+import org.apache.fop.fo.pagination.Region;
+import org.apache.fop.fo.pagination.SideRegion;
 import org.apache.fop.fo.pagination.StaticContent;
+import org.apache.fop.traits.MinOptMax;
 
 import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
+import java.util.ListIterator;
 
 /**
  * LayoutManager for an fo:flow object.
@@ -34,8 +39,7 @@ import java.util.Map;
  * and filling them with block-level areas generated by its children.
  */
 public class StaticContentLayoutManager extends BlockStackingLayoutManager {
-
-    private RegionReference region;
+    private RegionReference targetRegion;
     private List blockBreaks = new ArrayList();
 
     public StaticContentLayoutManager(StaticContent node) {
@@ -46,10 +50,106 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager {
      * Sets the region reference
      * @param region region reference
      */
-    public void setRegionReference(RegionReference region) {
-        this.region = region;
+    public void setTargetRegion(RegionReference targetRegion) {
+        this.targetRegion = targetRegion;
     }
 
+    /** 
+     * @return the region-reference-area that this 
+     * static content is directed to.  
+     */
+    public RegionReference getTargetRegion() {
+        return targetRegion;
+    }
+    
+    /**
+     * @see org.apache.fop.layoutmgr.LayoutManager#getNextKnuthElements(org.apache.fop.layoutmgr.LayoutContext, int)
+     */
+    public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+        // set layout dimensions
+        fobj.setLayoutDimension(PercentBase.BLOCK_IPD, context.getRefIPD());
+        fobj.setLayoutDimension(PercentBase.BLOCK_BPD, context.getStackLimit().opt);
+
+        //TODO Copied from elsewhere. May be worthwhile to factor out the common parts. 
+        // currently active LM
+        BlockLevelLayoutManager curLM;
+        BlockLevelLayoutManager prevLM = null;
+        MinOptMax stackSize = new MinOptMax();
+        LinkedList returnedList;
+        LinkedList returnList = new LinkedList();
+
+        while ((curLM = ((BlockLevelLayoutManager) getChildLM())) != null) {
+            if (curLM.generatesInlineAreas()) {
+                log.error("inline area not allowed under flow - ignoring");
+                curLM.setFinished(true);
+                continue;
+            }
+
+            // Set up a LayoutContext
+            MinOptMax bpd = context.getStackLimit();
+            BreakPoss bp;
+            bp = null;
+
+            LayoutContext childLC = new LayoutContext(0);
+            boolean breakPage = false;
+            childLC.setStackLimit(MinOptMax.subtract(bpd, stackSize));
+            childLC.setRefIPD(context.getRefIPD());
+
+            // get elements from curLM
+            returnedList = curLM.getNextKnuthElements(childLC, alignment);
+/*LF*/      //System.out.println("FLM.getNextKnuthElements> returnedList.size() = " + returnedList.size());
+
+            // "wrap" the Position inside each element
+            LinkedList tempList = returnedList;
+            KnuthElement tempElement;
+            returnedList = new LinkedList();
+            ListIterator listIter = tempList.listIterator();
+            while (listIter.hasNext()) {
+                tempElement = (KnuthElement)listIter.next();
+                tempElement.setPosition(new NonLeafPosition(this, tempElement.getPosition()));
+                returnedList.add(tempElement);
+            }
+
+            if (returnedList.size() == 1
+                && ((KnuthElement)returnedList.getFirst()).isPenalty()
+                && ((KnuthPenalty)returnedList.getFirst()).getP() == -KnuthElement.INFINITE) {
+                // a descendant of this flow has break-before
+                returnList.addAll(returnedList);
+                return returnList;
+            } else {
+                if (returnList.size() > 0) {
+                    // there is a block before this one
+                    if (prevLM.mustKeepWithNext()
+                        || curLM.mustKeepWithPrevious()) {
+                        // add an infinite penalty to forbid a break between blocks
+                        returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, new Position(this), false));
+                    } else if (!((KnuthElement) returnList.getLast()).isGlue()) {
+                        // add a null penalty to allow a break between blocks
+                        returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
+                    }
+                }
+/*LF*/          if (returnedList.size() > 0) { // controllare!
+                    returnList.addAll(returnedList);
+                    if (((KnuthElement)returnedList.getLast()).isPenalty()
+                        && ((KnuthPenalty)returnedList.getLast()).getP() == -KnuthElement.INFINITE) {
+                        // a descendant of this flow has break-after
+/*LF*/                  //System.out.println("FLM - break after!!");
+                        return returnList;
+                    }
+/*LF*/          }
+            }
+            prevLM = curLM;
+        }
+
+        setFinished(true);
+
+        if (returnList.size() > 0) {
+            return returnList;
+        } else {
+            return null;
+        }
+    }
+    
     /**
      * @see org.apache.fop.layoutmgr.LayoutManager#getNextBreakPoss(LayoutContext)
      */
@@ -86,7 +186,9 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager {
      * @see org.apache.fop.layoutmgr.LayoutManager#addAreas(PositionIterator, LayoutContext)
      */
     public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
+        AreaAdditionUtil.addAreas(parentIter, layoutContext);
 
+        /*
         LayoutManager childLM;
         int iStartPos = 0;
         LayoutContext lc = new LayoutContext(0);
@@ -103,8 +205,9 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager {
         }
 
         blockBreaks.clear();
+        */
         flush();
-        region = null;
+        targetRegion = null;
     }
 
 
@@ -115,25 +218,106 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager {
      * @see org.apache.fop.layoutmgr.LayoutManager#addChildArea(Area)
      */
     public void addChildArea(Area childArea) {
-        region.addBlock((Block)childArea);
+        targetRegion.addBlock((Block)childArea);
     }
 
     /**
      * @see org.apache.fop.layoutmgr.LayoutManager#getParentArea(Area)
      */
     public Area getParentArea(Area childArea) {
-        return region;
+        return targetRegion;
     }
 
-    /**
-     * Markers are not allowed in static areas so this reports an
-     * error and does nothing.
-     *
-     * @see org.apache.fop.layoutmgr.LayoutManager
-     */
-    public void addMarker(Map marks, boolean start, boolean isfirst) {
-        // error markers not allowed in static
-        log.error("Cannot add marker to static areas");
+    public void doLayout(SideRegion region) {
+        MinOptMax range = new MinOptMax(targetRegion.getIPD());
+        StaticContentBreaker breaker = new StaticContentBreaker(region, this, range);
+        breaker.doLayout(targetRegion.getBPD());
+        if (breaker.isOverflow()) {
+            if (region.getOverflow() == EN_ERROR_IF_OVERFLOW) {
+                //TODO throw layout exception
+            }
+            log.warn("static-content overflows the available area.");
+        }
     }
+    
+    private class StaticContentBreaker extends AbstractBreaker {
+        
+        private Region region;
+        private StaticContentLayoutManager lm;
+        private MinOptMax ipd;
+        boolean overflow = false;
+        
+        public StaticContentBreaker(Region region, StaticContentLayoutManager lm, MinOptMax ipd) {
+            this.region = region;
+            this.lm = lm;
+            this.ipd = ipd;
+        }
+
+        public boolean isOverflow() {
+            return this.overflow;
+        }
+        
+        protected LayoutManager getTopLevelLM() {
+            return lm;
+        }
+
+        protected LayoutContext createLayoutContext() {
+            LayoutContext lc = super.createLayoutContext();
+            lc.setRefIPD(ipd.opt);
+            return lc;
+        }
+        
+        protected LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+            LayoutManager curLM; // currently active LM
+            LinkedList returnList = new LinkedList();
+
+            while ((curLM = getChildLM()) != null) {
+                LayoutContext childLC = new LayoutContext(0);
+                childLC.setStackLimit(context.getStackLimit());
+                childLC.setRefIPD(context.getRefIPD());
+
+                LinkedList returnedList = null;
+                if (!curLM.isFinished()) {
+                    returnedList = curLM.getNextKnuthElements(childLC, alignment);
+                }
+                if (returnedList != null) {
+                    lm.wrapPositionElements(returnedList, returnList);
+                    //returnList.addAll(returnedList);
+                }
+            }
+            setFinished(true);
+            return returnList;
+        }
+
+        protected int getCurrentDisplayAlign() {
+            return region.getDisplayAlign();
+        }
+        
+        protected boolean hasMoreContent() {
+            return !lm.isFinished();
+        }
+        
+        protected void addAreas(PositionIterator posIter, LayoutContext context) {
+            AreaAdditionUtil.addAreas(posIter, context);    
+        }
+        
+        protected void doPhase3(PageBreakingAlgorithm alg, int partCount, 
+                BlockSequence originalList, BlockSequence effectiveList) {
+            //Directly add areas after finding the breaks
+            addAreas(alg, partCount, originalList, effectiveList);
+            if (partCount > 1) {
+                overflow = true;
+            }
+        }
+        
+        protected void finishPart() {
+            //nop for static content
+        }
+        
+        protected LayoutManager getCurrentChildLM() {
+            return null; //TODO NYI
+        }
+        
+    }    
 }
 
index 4d5fe4130f2e2928acd7a5dd9d19b40d78cd01b0..13d6d89adb5843bd521f95c768e44c6a81f56afd 100644 (file)
@@ -37,8 +37,7 @@ import org.apache.fop.traits.MinOptMax;
  * LayoutManager for text (a sequence of characters) which generates one
  * or more inline areas.
  */
-public class TextLayoutManager extends AbstractLayoutManager
-                               implements InlineLevelLayoutManager {
+public class TextLayoutManager extends LeafNodeLayoutManager {
 
     /**
      * Store information about each potential text area.
@@ -131,6 +130,7 @@ public class TextLayoutManager extends AbstractLayoutManager
      * @param node The FOText object to be rendered
      */
     public TextLayoutManager(FOText node) {
+        super();
         foText = node;
         
         textArray = new char[node.endIndex - node.startIndex];
@@ -645,10 +645,10 @@ public class TextLayoutManager extends AbstractLayoutManager
                 textArea.setOffset(context.getMiddleBaseline() + fs.getXHeight() / 2);
             break;
             case EN_TOP:
-                textArea.setOffset(fs.getAscender());
+                textArea.setOffset(context.getTopBaseline() + fs.getAscender());
             break;
             case EN_BOTTOM:
-                textArea.setOffset(context.getLineHeight() - bpd + fs.getAscender());
+                textArea.setOffset(context.getBottomBaseline() - bpd + fs.getAscender());
             break;
             case EN_BASELINE:
             default:
@@ -706,7 +706,7 @@ public class TextLayoutManager extends AbstractLayoutManager
                                        - 6 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
                                        new LeafPosition(this, -1), true));
                     returnList.add
-                        (new KnuthBox(0, 0, 0, 0,
+                        (new KnuthInlineBox(0, 0, 0, 0,
                                       new LeafPosition(this, -1), true));
                     returnList.add
                         (new KnuthPenalty(0, KnuthElement.INFINITE, false,
@@ -724,14 +724,14 @@ public class TextLayoutManager extends AbstractLayoutManager
                                       (short) 1, (short) 0,
                                       wordSpaceIPD, false));
                     returnList.add
-                        (new KnuthGlue(0, 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
+                        (new KnuthGlue(0, 3 * wordSpaceIPD.opt, 0,
                                        new LeafPosition(this, vecAreaInfo.size() - 1), false));
                     returnList.add
                         (new KnuthPenalty(0, 0, false,
                                           new LeafPosition(this, -1), true));
                     returnList.add
                         (new KnuthGlue(wordSpaceIPD.opt,
-                                       - 3 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
+                                       - 3 * wordSpaceIPD.opt, 0,
                                        new LeafPosition(this, -1), true));
                     iNextStart ++;
                     break;
@@ -791,42 +791,64 @@ public class TextLayoutManager extends AbstractLayoutManager
                 for (; iTempStart < textArray.length
                         && textArray[iTempStart] != SPACE
                         && textArray[iTempStart] != NBSPACE
-                        && textArray[iTempStart] != NEWLINE;
+                     && textArray[iTempStart] != NEWLINE
+                     && !(iTempStart > iNextStart
+                          && alignment == EN_JUSTIFY
+                          && BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0);
                         iTempStart++) {
                     wordIPD.add(fs.getCharWidth(textArray[iTempStart]));
                 }
-                wordIPD.add(MinOptMax.multiply(letterSpaceIPD, (iTempStart - iThisStart - 1)));
+                int iLetterSpaces = iTempStart - iThisStart - 1;
+                wordIPD.add(MinOptMax.multiply(letterSpaceIPD, iLetterSpaces));
                 vecAreaInfo.add
                     (new AreaInfo(iThisStart, iTempStart, (short) 0,
-                                  (short) (iTempStart - iThisStart - 1),
+                                  (short) iLetterSpaces,
                                   wordIPD, false));
                 if (letterSpaceIPD.min == letterSpaceIPD.max) {
                     // constant letter space; simply return a box
                     // whose width includes letter spaces
                     returnList.add
-                        (new KnuthBox(wordIPD.opt, lead, total, middle,
+                        (new KnuthInlineBox(wordIPD.opt, lead, total, middle,
                                       new LeafPosition(this, vecAreaInfo.size() - 1), false));
-                    iNextStart = iTempStart;
                 } else {
                     // adjustable letter space;
                     // some other KnuthElements are needed
                     returnList.add
-                        (new KnuthBox(wordIPD.opt - (iTempStart - iThisStart - 1) * letterSpaceIPD.opt,
+                        (new KnuthInlineBox(wordIPD.opt - iLetterSpaces * letterSpaceIPD.opt,
                                       lead, total, middle,
                                       new LeafPosition(this, vecAreaInfo.size() - 1), false));
                     returnList.add
                         (new KnuthPenalty(0, KnuthElement.INFINITE, false,
                                           new LeafPosition(this, -1), true));
                     returnList.add
-                        (new KnuthGlue((iTempStart - iThisStart - 1) * letterSpaceIPD.opt,
-                                       (iTempStart - iThisStart - 1) * (letterSpaceIPD.max - letterSpaceIPD.opt),
-                                       (iTempStart - iThisStart - 1) * (letterSpaceIPD.opt - letterSpaceIPD.min),
+                        (new KnuthGlue(iLetterSpaces * letterSpaceIPD.opt,
+                                       iLetterSpaces * (letterSpaceIPD.max - letterSpaceIPD.opt),
+                                       iLetterSpaces * (letterSpaceIPD.opt - letterSpaceIPD.min),
                                        new LeafPosition(this, -1), true));
                     returnList.add
-                        (new KnuthBox(0, lead, total, middle,
-                                      new LeafPosition(this, -1), true));
-                    iNextStart = iTempStart;
+                        (new KnuthInlineBox(0, lead, total, middle,
+                                            new LeafPosition(this, -1), true));
                 }
+                // if the last character is '-' or '/', it could be used as a line end;
+                // add a flagged penalty element and glue element representing a suppressible 
+                // letter space if the next character is not a space
+                if (BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0
+                    && iTempStart < textArray.length
+                    && textArray[iTempStart] != SPACE
+                    && textArray[iTempStart] != NBSPACE) {
+                    returnList.add
+                        (new KnuthPenalty(0, KnuthPenalty.FLAGGED_PENALTY, true,
+                                          new LeafPosition(this, -1), false));
+                    returnList.add
+                        (new KnuthGlue(letterSpaceIPD.opt,
+                                       letterSpaceIPD.max - letterSpaceIPD.opt,
+                                       letterSpaceIPD.opt - letterSpaceIPD.min,
+                                       new LeafPosition(this, -1), false));
+                    // update the information in the AreaInfo, adding one more letter space
+                    AreaInfo ai = (AreaInfo) vecAreaInfo.get(vecAreaInfo.size() - 1);
+                    ai.iLScount ++;
+                }
+                iNextStart = iTempStart;
             }
         } // end of while
         setFinished(true);
@@ -837,19 +859,38 @@ public class TextLayoutManager extends AbstractLayoutManager
         }
     }
 
-    public KnuthElement addALetterSpaceTo(KnuthElement element) {
-        LeafPosition pos = (LeafPosition) element.getPosition();
+    public List addALetterSpaceTo(List oldList) {
+        // old list contains only a box, or the sequence: box penalty glue box;
+        // look at the Position stored in the first element in oldList
+        // which is always a box
+        ListIterator oldListIterator = oldList.listIterator();
+        LeafPosition pos = (LeafPosition) ((KnuthBox) oldListIterator.next()).getPosition();
         AreaInfo ai = (AreaInfo) vecAreaInfo.get(pos.getLeafPos());
         ai.iLScount ++;
         ai.ipdArea.add(letterSpaceIPD);
-        if (letterSpaceIPD.min == letterSpaceIPD.max) {
-            return new KnuthBox(ai.ipdArea.opt, lead, total, middle, pos, false);
+        if (BREAK_CHARS.indexOf(textArray[iTempStart - 1]) >= 0) {
+            // the last character could be used as a line break
+            // append new elements to oldList
+            oldListIterator = oldList.listIterator(oldList.size());
+            oldListIterator.add(new KnuthPenalty(0, KnuthPenalty.FLAGGED_PENALTY, true,
+                                                 new LeafPosition(this, -1), false));
+            oldListIterator.add(new KnuthGlue(letterSpaceIPD.opt,
+                                       letterSpaceIPD.max - letterSpaceIPD.opt,
+                                       letterSpaceIPD.opt - letterSpaceIPD.min,
+                                       new LeafPosition(this, -1), false));
+        } else if (letterSpaceIPD.min == letterSpaceIPD.max) {
+            // constant letter space: replace the box
+            oldListIterator.set(new KnuthInlineBox(ai.ipdArea.opt, lead, total, middle, pos, false));
         } else {
-            return new KnuthGlue(ai.iLScount * letterSpaceIPD.opt,
-                                 ai.iLScount * (letterSpaceIPD.max - letterSpaceIPD.opt),
-                                 ai.iLScount * (letterSpaceIPD.opt - letterSpaceIPD.min),
-                                 new LeafPosition(this, -1), true);
+            // adjustable letter space: replace the glue
+            oldListIterator.next(); // this would return the penalty element
+            oldListIterator.next(); // this would return the glue element
+            oldListIterator.set(new KnuthGlue(ai.iLScount * letterSpaceIPD.opt,
+                                              ai.iLScount * (letterSpaceIPD.max - letterSpaceIPD.opt),
+                                              ai.iLScount * (letterSpaceIPD.opt - letterSpaceIPD.min),
+                                              new LeafPosition(this, -1), true));
         }
+        return oldList;
     }
 
     public void hyphenate(Position pos, HyphContext hc) {
@@ -948,7 +989,7 @@ public class TextLayoutManager extends AbstractLayoutManager
     }
 
     public LinkedList getChangedKnuthElements(List oldList,
-                                              int flaggedPenalty,
+                                              /*int flaggedPenalty,*/
                                               int alignment) {
         if (isFinished()) {
             return null;
@@ -960,13 +1001,21 @@ public class TextLayoutManager extends AbstractLayoutManager
             AreaInfo ai = (AreaInfo) vecAreaInfo.get(iReturnedIndex);
             if (ai.iWScount == 0) {
                 // ai refers either to a word or a word fragment
+
+                // if the last character is '-' or '/' and the next character is not a space
+                // one of the letter spaces must be represented using a penalty and a glue,
+                // and its width must be subtracted
+                if (BREAK_CHARS.indexOf(textArray[ai.iBreakIndex - 1]) >= 0
+                    && ai.iLScount == (ai.iBreakIndex - ai.iStartIndex)) {
+                    ai.ipdArea.add(new MinOptMax(-letterSpaceIPD.min, -letterSpaceIPD.opt, -letterSpaceIPD.max));
+                }
                 if (letterSpaceIPD.min == letterSpaceIPD.max) {
                     returnList.add
-                        (new KnuthBox(ai.ipdArea.opt, lead, total, middle,
+                        (new KnuthInlineBox(ai.ipdArea.opt, lead, total, middle,
                                       new LeafPosition(this, iReturnedIndex), false));
                 } else {
                     returnList.add
-                        (new KnuthBox(ai.ipdArea.opt
+                        (new KnuthInlineBox(ai.ipdArea.opt
                                       - ai.iLScount * letterSpaceIPD.opt,
                                       lead, total, middle, 
                                       new LeafPosition(this, iReturnedIndex), false));
@@ -979,17 +1028,37 @@ public class TextLayoutManager extends AbstractLayoutManager
                                        ai.iLScount * (letterSpaceIPD.opt - letterSpaceIPD.min),
                                        new LeafPosition(this, -1), true));
                     returnList.add
-                        (new KnuthBox(0, 0, 0, 0,
+                        (new KnuthInlineBox(0, 0, 0, 0,
                                       new LeafPosition(this, -1), true));
                 }
                 if (ai.bHyphenated) {
                     returnList.add
-                        (new KnuthPenalty(hyphIPD, flaggedPenalty, true,
+                        (new KnuthPenalty(hyphIPD, KnuthPenalty.FLAGGED_PENALTY, true,
                                           new LeafPosition(this, -1), false));
                 }
+                // if the last character is '-' or '/', it could be used as a line end;
+                // add a flagged penalty element and a glue element representing a suppressible 
+                // letter space if the next character is not a space
+                if (BREAK_CHARS.indexOf(textArray[ai.iBreakIndex - 1]) >= 0
+                    && ai.iLScount == (ai.iBreakIndex - ai.iStartIndex)) {
+                    returnList.add
+                        (new KnuthPenalty(0, KnuthPenalty.FLAGGED_PENALTY, true,
+                                          new LeafPosition(this, -1), false));
+                    returnList.add
+                        (new KnuthGlue(letterSpaceIPD.opt,
+                                       letterSpaceIPD.max - letterSpaceIPD.opt,
+                                       letterSpaceIPD.opt - letterSpaceIPD.min,
+                                       new LeafPosition(this, -1), false));
+                }
                 iReturnedIndex ++;
             } else {
                 // ai refers to a space
+                if (textArray[ai.iStartIndex] == NBSPACE) {
+                    returnList.add
+                        (new KnuthPenalty(0, KnuthElement.INFINITE, false,
+                                          new LeafPosition(this, -1),
+                                          false));
+                }
                 switch (alignment) {
                 case EN_CENTER :
                     returnList.add
@@ -1003,7 +1072,7 @@ public class TextLayoutManager extends AbstractLayoutManager
                                        - 6 * LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
                                        new LeafPosition(this, -1), true));
                     returnList.add
-                        (new KnuthBox(0, 0, 0, 0,
+                        (new KnuthInlineBox(0, 0, 0, 0,
                                       new LeafPosition(this, -1), true));
                     returnList.add
                         (new KnuthPenalty(0, KnuthElement.INFINITE, false,
index ef7403c545b8f0ee136180e822e07ca91704f7ea..64089245f101d15b7dd1517a86f08d3d39df9d8a 100644 (file)
@@ -27,14 +27,16 @@ import org.apache.fop.layoutmgr.LeafPosition;
 import org.apache.fop.layoutmgr.BreakPoss;
 import org.apache.fop.layoutmgr.LayoutContext;
 import org.apache.fop.layoutmgr.PositionIterator;
-import org.apache.fop.layoutmgr.BreakPossPosIter;
 import org.apache.fop.layoutmgr.Position;
+import org.apache.fop.layoutmgr.NonLeafPosition;
 import org.apache.fop.area.Area;
 import org.apache.fop.area.Block;
 import org.apache.fop.traits.MinOptMax;
 
 import java.util.ArrayList;
+import java.util.Iterator;
 import java.util.List;
+import java.util.LinkedList;
 
 /**
  * LayoutManager for a table-cell FO.
@@ -50,6 +52,20 @@ public class Item extends BlockStackingLayoutManager {
     private int xoffset;
     private int itemIPD;
 
+    private static class StackingIter extends PositionIterator {
+        StackingIter(Iterator parentIter) {
+            super(parentIter);
+        }
+
+        protected LayoutManager getLM(Object nextObj) {
+            return ((Position) nextObj).getLM();
+        }
+
+        protected Position getPos(Object nextObj) {
+            return ((Position) nextObj);
+        }
+    }
+
     /**
      * Create a new Cell layout manager.
      */
@@ -152,6 +168,11 @@ public class Item extends BlockStackingLayoutManager {
         xoffset = off;
     }
 
+    public LinkedList getChangedKnuthElements(List oldList, int alignment) {
+        //log.debug("  Item.getChanged>");
+        return super.getChangedKnuthElements(oldList, alignment);
+    }
+
     /**
      * Add the areas for the break points.
      * The list item contains block stacking layout managers
@@ -166,26 +187,43 @@ public class Item extends BlockStackingLayoutManager {
         
         int nameId = fobj.getNameId();
         if (nameId == FO_LIST_ITEM_LABEL) {
-            addID(((ListItemLabel) fobj).getId());
+            getPSLM().addIDToPage(((ListItemLabel) fobj).getId());
         } else if (nameId == FO_LIST_ITEM_BODY) {
-            addID(((ListItemBody) fobj).getId());
+            getPSLM().addIDToPage(((ListItemBody) fobj).getId());
         }
 
-        LayoutManager childLM;
-        int iStartPos = 0;
+        LayoutManager childLM = null;
         LayoutContext lc = new LayoutContext(0);
+        LayoutManager firstLM = null;
+        LayoutManager lastLM = null;
+
+        // "unwrap" the NonLeafPositions stored in parentIter
+        // and put them in a new list; 
+        LinkedList positionList = new LinkedList();
+        Position pos;
         while (parentIter.hasNext()) {
-            LeafPosition lfp = (LeafPosition) parentIter.next();
-            // Add the block areas to Area
-            PositionIterator breakPosIter =
-              new BreakPossPosIter(childBreaks, iStartPos,
-                                   lfp.getLeafPos() + 1);
-            iStartPos = lfp.getLeafPos() + 1;
-            while ((childLM = breakPosIter.getNextChildLM()) != null) {
-                childLM.addAreas(breakPosIter, lc);
+            pos = (Position)parentIter.next();
+            if (pos instanceof NonLeafPosition) {
+                // pos was created by a child of this ListBlockLM
+                positionList.add(((NonLeafPosition) pos).getPosition());
+                lastLM = ((NonLeafPosition) pos).getPosition().getLM();
+                if (firstLM == null) {
+                    firstLM = lastLM;
+                }
+            } else {
+                // pos was created by this ListBlockLM, so it must be ignored
             }
         }
 
+        StackingIter childPosIter = new StackingIter(positionList.listIterator());
+        while ((childLM = childPosIter.getNextChildLM()) != null) {
+            // Add the block areas to Area
+            lc.setFlags(LayoutContext.FIRST_AREA, childLM == firstLM);
+            lc.setFlags(LayoutContext.LAST_AREA, childLM == lastLM);
+            lc.setStackLimit(layoutContext.getStackLimit());
+            childLM.addAreas(childPosIter, lc);
+        }
+
         /*
         if (borderProps != null) {
             TraitSetter.addBorders(curBlockArea, borderProps);
index 48d78f8752c3a2f454c858bd21fdc67c2cb25de6..d57bef99f57613d02f8280aa21b9df90901e6025 100644 (file)
@@ -25,8 +25,8 @@ import org.apache.fop.layoutmgr.LeafPosition;
 import org.apache.fop.layoutmgr.BreakPoss;
 import org.apache.fop.layoutmgr.LayoutContext;
 import org.apache.fop.layoutmgr.PositionIterator;
-import org.apache.fop.layoutmgr.BreakPossPosIter;
 import org.apache.fop.layoutmgr.Position;
+import org.apache.fop.layoutmgr.NonLeafPosition;
 import org.apache.fop.layoutmgr.TraitSetter;
 import org.apache.fop.area.Area;
 import org.apache.fop.area.Block;
@@ -34,6 +34,8 @@ import org.apache.fop.traits.MinOptMax;
 import org.apache.fop.traits.SpaceVal;
 
 import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 
 /**
@@ -46,14 +48,26 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager {
     
     private Block curBlockArea;
 
-    private int referenceIPD = 0;
-
     private List bodyBreaks = new ArrayList();
 
     //TODO space-before|after: handle space-resolution rules
     private MinOptMax spaceBefore;
     private MinOptMax spaceAfter;
 
+    private static class StackingIter extends PositionIterator {
+        StackingIter(Iterator parentIter) {
+            super(parentIter);
+        }
+
+        protected LayoutManager getLM(Object nextObj) {
+            return ((Position) nextObj).getLM();
+        }
+
+        protected Position getPos(Object nextObj) {
+            return ((Position) nextObj);
+        }
+    }
+
     /*
     private class SectionPosition extends LeafPosition {
         protected List list;
@@ -64,7 +78,7 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager {
     }*/
 
     /**
-     * Create a new table layout manager.
+     * Create a new list block layout manager.
      * @param node list-block to create the layout manager for
      */
     public ListBlockLayoutManager(ListBlock node) {
@@ -166,6 +180,11 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager {
         return null;
     }
 
+    public LinkedList getChangedKnuthElements(List oldList, int alignment) {
+        //log.debug("LBLM.getChangedKnuthElements>");
+        return super.getChangedKnuthElements(oldList, alignment);
+    }
+
     /**
      * The table area is a reference area that contains areas for
      * columns, bodies, rows and the contents are in cells.
@@ -182,26 +201,40 @@ public class ListBlockLayoutManager extends BlockStackingLayoutManager {
         addBlockSpacing(adjust, spaceBefore);
         spaceBefore = null;
         
-        addID(fobj.getId());
+        getPSLM().addIDToPage(fobj.getId());
 
         // the list block contains areas stacked from each list item
 
-        //int listHeight = 0;
-
-        LayoutManager childLM;
-        int iStartPos = 0;
+        LayoutManager childLM = null;
         LayoutContext lc = new LayoutContext(0);
+        LayoutManager firstLM = null;
+        LayoutManager lastLM = null;
+
+        // "unwrap" the NonLeafPositions stored in parentIter
+        // and put them in a new list; 
+        LinkedList positionList = new LinkedList();
+        Position pos;
         while (parentIter.hasNext()) {
-            LeafPosition lfp = (LeafPosition) parentIter.next();
-            // Add the block areas to Area
-            PositionIterator breakPosIter = new BreakPossPosIter(
-                    bodyBreaks, iStartPos, lfp.getLeafPos() + 1);
-            iStartPos = lfp.getLeafPos() + 1;
-            while ((childLM = (LayoutManager)breakPosIter.getNextChildLM()) != null) {
-                childLM.addAreas(breakPosIter, lc);
+            pos = (Position)parentIter.next();
+            if (pos instanceof NonLeafPosition
+                && ((NonLeafPosition) pos).getPosition().getLM() != this) {
+                // pos was created by a child of this ListBlockLM
+                positionList.add(((NonLeafPosition) pos).getPosition());
+                lastLM = ((NonLeafPosition) pos).getPosition().getLM();
+                if (firstLM == null) {
+                    firstLM = lastLM;
+                }
             }
         }
 
+        StackingIter childPosIter = new StackingIter(positionList.listIterator());
+        while ((childLM = childPosIter.getNextChildLM()) != null) {
+            // Add the block areas to Area
+            lc.setFlags(LayoutContext.FIRST_AREA, childLM == firstLM);
+            lc.setFlags(LayoutContext.LAST_AREA, childLM == lastLM);
+            lc.setStackLimit(layoutContext.getStackLimit());
+            childLM.addAreas(childPosIter, lc);
+        }
 
         flush();
 
index 1d203806762a9bd8bdf82fe64e74418e78d6ae0a..36da5f68da903deb9eec506988a7e2610538c100 100644 (file)
@@ -27,17 +27,22 @@ import org.apache.fop.layoutmgr.LeafPosition;
 import org.apache.fop.layoutmgr.BreakPoss;
 import org.apache.fop.layoutmgr.LayoutContext;
 import org.apache.fop.layoutmgr.PositionIterator;
-import org.apache.fop.layoutmgr.BreakPossPosIter;
 import org.apache.fop.layoutmgr.Position;
+import org.apache.fop.layoutmgr.NonLeafPosition;
 import org.apache.fop.layoutmgr.TraitSetter;
+import org.apache.fop.layoutmgr.KnuthElement;
+import org.apache.fop.layoutmgr.KnuthBox;
+import org.apache.fop.layoutmgr.KnuthPenalty;
+import org.apache.fop.layoutmgr.KnuthPossPosIter;
 import org.apache.fop.area.Area;
 import org.apache.fop.area.Block;
 import org.apache.fop.traits.MinOptMax;
 import org.apache.fop.traits.SpaceVal;
 
-import java.util.Iterator;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.LinkedList;
+import java.util.ListIterator;
 
 /**
  * LayoutManager for a list-item FO.
@@ -49,11 +54,11 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager {
     private Item label;
     private Item body;
 
-    private int referenceIPD = 0;
-
     private Block curBlockArea = null;
 
-    //private List cellList = null;
+    private LinkedList labelList = null;
+    private LinkedList bodyList = null;
+
     private int listItemHeight;
 
     //TODO space-before|after: handle space-resolution rules
@@ -68,6 +73,38 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager {
         }
     }
 
+    private class ListItemPosition extends Position {
+        private int iLabelFirstIndex;
+        private int iLabelLastIndex;
+        private int iBodyFirstIndex;
+        private int iBodyLastIndex;
+
+        public ListItemPosition(LayoutManager lm, int labelFirst, int labelLast, 
+                int bodyFirst, int bodyLast) {
+            super(lm);
+            iLabelFirstIndex = labelFirst;
+            iLabelLastIndex = labelLast;
+            iBodyFirstIndex = bodyFirst;
+            iBodyLastIndex = bodyLast;
+        }
+        
+        public int getLabelFirstIndex() {
+            return iLabelFirstIndex;
+        }
+        
+        public int getLabelLastIndex() {
+            return iLabelLastIndex;
+        }
+
+        public int getBodyFirstIndex() {
+            return iBodyFirstIndex;
+        }
+        
+        public int getBodyLastIndex() {
+            return iBodyLastIndex;
+        }
+    }
+
     /**
      * Create a new list item layout manager.
      * @param node list-item to create the layout manager for
@@ -226,6 +263,213 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager {
         return breakPoss;
     }
 
+    /**
+     * @see org.apache.fop.layoutmgr.LayoutManager#getNextKnuthElements(org.apache.fop.layoutmgr.LayoutContext, int)
+     */
+    public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+        referenceIPD = context.getRefIPD();
+
+        // label
+        labelList = label.getNextKnuthElements(context, alignment);
+
+        // body
+        bodyList = body.getNextKnuthElements(context, alignment);
+
+        // create a combined list
+        LinkedList returnedList = getCombinedKnuthElementsForListItem(labelList, bodyList);
+
+        // "wrap" the Position inside each element
+        LinkedList tempList = returnedList;
+        KnuthElement tempElement;
+        returnedList = new LinkedList();
+        ListIterator listIter = tempList.listIterator();
+        while (listIter.hasNext()) {
+            tempElement = (KnuthElement)listIter.next();
+            tempElement.setPosition(new NonLeafPosition(this, tempElement.getPosition()));
+            returnedList.add(tempElement);
+        }
+
+        setFinished(true);
+        return returnedList;
+    }
+
+    private LinkedList getCombinedKnuthElementsForListItem(LinkedList labelElements,
+                                                           LinkedList bodyElements) {
+        //Copy elements to array lists to improve element access performance
+        List[] elementLists = {new ArrayList(labelElements),
+                               new ArrayList(bodyElements)};
+        int[] fullHeights = {calcItemHeightFromContents(elementLists[0]),
+                            calcItemHeightFromContents(elementLists[1])};
+        int[] partialHeights = {0, 0};
+        int[] start = {-1, -1};
+        int[] end = {-1, -1};
+
+        int totalHeight = Math.max(fullHeights[0], fullHeights[1]);
+        int step;
+        int addedBoxHeight = 0;
+
+        LinkedList returnList = new LinkedList();
+        while ((step = getNextStep(elementLists, start, end, partialHeights))
+               > 0) {
+            // compute penalty height and box height
+            int penaltyHeight = step 
+                + getMaxRemainingHeight(fullHeights, partialHeights) 
+                - totalHeight;
+            int boxHeight = step - addedBoxHeight - penaltyHeight;
+
+            // add the new elements
+            addedBoxHeight += boxHeight;
+            ListItemPosition stepPosition = new ListItemPosition(this, 
+                    start[0], end[0], start[1], end[1]);
+            returnList.add(new KnuthBox(boxHeight, stepPosition, false));
+            if (addedBoxHeight < totalHeight) {
+                returnList.add(new KnuthPenalty(penaltyHeight, 0, false, stepPosition, false));
+            }
+        }
+
+        return returnList;
+    }
+
+    private int calcItemHeightFromContents(List elements, int start, int end) {
+        ListIterator iter = elements.listIterator(start);
+        int count = end - start + 1;
+        int len = 0;
+        while (iter.hasNext()) {
+            KnuthElement el = (KnuthElement)iter.next();
+            if (el.isBox()) {
+                len += el.getW();
+            } else if (el.isGlue()) {
+                len += el.getW();
+            } else {
+                log.debug("Ignoring penalty: " + el);
+                //ignore penalties
+            }
+            count--;
+            if (count == 0) {
+                break;
+            }
+        }
+        return len;
+    }
+    
+    private int calcItemHeightFromContents(List elements) {
+        return calcItemHeightFromContents(elements, 0, elements.size() - 1);
+    }
+
+    private int getNextStep(List[] elementLists, int[] start, int[] end, int[] partialHeights) {
+        // backup of partial heights
+        int[] backupHeights = {partialHeights[0], partialHeights[1]};
+
+        // set starting points
+        start[0] = end[0] + 1;
+        start[1] = end[1] + 1;
+
+        // get next possible sequence for label and body
+        int seqCount = 0;
+        for (int i = 0; i < start.length; i++) {
+            while (end[i] + 1 < elementLists[i].size()) {
+                end[i]++;
+                KnuthElement el = (KnuthElement)elementLists[i].get(end[i]);
+                if (el.isPenalty()) {
+                    if (el.getP() < KnuthElement.INFINITE) {
+                        //First legal break point
+                        break;
+                    }
+                } else if (el.isGlue()) {
+                    KnuthElement prev = (KnuthElement)elementLists[i].get(end[i] - 1);
+                    if (prev.isBox()) {
+                        //Second legal break point
+                        break;
+                    }
+                    partialHeights[i] += el.getW();
+                } else {
+                    partialHeights[i] += el.getW();
+                }
+            }
+            if (end[i] < start[i]) {
+                partialHeights[i] = backupHeights[i];
+            } else {
+                seqCount++;
+            }
+        }
+        if (seqCount == 0) {
+            return 0;
+        }
+        
+        // determine next step
+        int step;
+        if (backupHeights[0] == 0 && backupHeights[1] == 0) {
+            // this is the first step: choose the maximum increase, so that
+            // the smallest area in the first page will contain at least
+            // a label area and a body area
+            step = Math.max((end[0] >= start[0] ? partialHeights[0] : Integer.MIN_VALUE),
+                            (end[1] >= start[1] ? partialHeights[1] : Integer.MIN_VALUE));
+        } else {
+            // this is not the first step: choose the minimum increase
+            step = Math.min((end[0] >= start[0] ? partialHeights[0] : Integer.MAX_VALUE),
+                            (end[1] >= start[1] ? partialHeights[1] : Integer.MAX_VALUE));
+        }
+
+        // reset bigger-than-step sequences
+        for (int i = 0; i < partialHeights.length; i++) {
+            if (partialHeights[i] > step) {
+                partialHeights[i] = backupHeights[i];
+                end[i] = start[i] - 1;
+            }
+        }
+
+        return step;
+    }
+
+    private int getMaxRemainingHeight(int[] fullHeights, int[] partialHeights) {
+        return Math.max(fullHeights[0] - partialHeights[0],
+                        fullHeights[1] - partialHeights[1]);
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.LayoutManager#getChangedKnuthElements(java.util.List, int)
+     */
+    public LinkedList getChangedKnuthElements(List oldList, int alignment) {
+/*LF*/  //log.debug(" LILM.getChanged> label");
+        // label
+        labelList = label.getChangedKnuthElements(labelList, alignment);
+
+/*LF*/  //log.debug(" LILM.getChanged> body");
+        // body
+        // "unwrap" the Positions stored in the elements
+        ListIterator oldListIterator = oldList.listIterator();
+        KnuthElement oldElement = null;
+        while (oldListIterator.hasNext()) {
+            oldElement = (KnuthElement)oldListIterator.next();
+            Position innerPosition = ((NonLeafPosition) oldElement.getPosition()).getPosition();
+/*LF*/      //System.out.println(" BLM> unwrapping: " + (oldElement.isBox() ? "box    " : (oldElement.isGlue() ? "glue   " : "penalty")) + " creato da " + oldElement.getLayoutManager().getClass().getName());
+/*LF*/      //System.out.println(" BLM> unwrapping:         " + oldElement.getPosition().getClass().getName());
+            if (innerPosition != null) {
+                // oldElement was created by a descendant of this BlockLM
+                oldElement.setPosition(innerPosition);
+            } else {
+                // thisElement was created by this BlockLM
+                // modify its position in order to recognize it was not created
+                // by a child
+                oldElement.setPosition(new Position(this));
+            }
+        }
+
+        LinkedList returnedList = body.getChangedKnuthElements(oldList, alignment);
+        // "wrap" the Position inside each element
+        LinkedList tempList = returnedList;
+        KnuthElement tempElement;
+        returnedList = new LinkedList();
+        ListIterator listIter = tempList.listIterator();
+        while (listIter.hasNext()) {
+            tempElement = (KnuthElement)listIter.next();
+            tempElement.setPosition(new NonLeafPosition(this, tempElement.getPosition()));
+            returnedList.add(tempElement);
+        }
+
+        return returnedList;
+    }
+
     /**
      * Add the areas for the break points.
      * This sets the offset of each cell as it is added.
@@ -242,26 +486,62 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager {
         addBlockSpacing(adjust, spaceBefore);
         spaceBefore = null;
 
-        addID(fobj.getId());
+        getPSLM().addIDToPage(fobj.getId());
 
-        Item childLM;
         LayoutContext lc = new LayoutContext(0);
+
+        // "unwrap" the NonLeafPositions stored in parentIter
+        LinkedList positionList = new LinkedList();
+        Position pos;
         while (parentIter.hasNext()) {
-            ItemPosition lfp = (ItemPosition) parentIter.next();
-            // Add the block areas to Area
+            pos = (Position) parentIter.next();
+            if (pos instanceof NonLeafPosition) {
+                // pos contains a ListItemPosition created by this ListBlockLM
+                positionList.add(((NonLeafPosition) pos).getPosition());
+            }
+        }
 
-            for (Iterator iter = lfp.cellBreaks.iterator(); iter.hasNext();) {
-                List cellsbr = (List)iter.next();
-                PositionIterator breakPosIter;
-                breakPosIter = new BreakPossPosIter(cellsbr, 0, cellsbr.size());
+        // use the first and the last ListItemPosition to determine the 
+        // corresponding indexes in the original labelList and bodyList
+        int labelFirstIndex = ((ListItemPosition) positionList.getFirst()).getLabelFirstIndex();
+        int labelLastIndex = ((ListItemPosition) positionList.getLast()).getLabelLastIndex();
+        int bodyFirstIndex = ((ListItemPosition) positionList.getFirst()).getBodyFirstIndex();
+        int bodyLastIndex = ((ListItemPosition) positionList.getLast()).getBodyLastIndex();
+
+        // add label areas
+        if (labelFirstIndex <= labelLastIndex) {
+            KnuthPossPosIter labelIter = new KnuthPossPosIter(labelList, 
+                    labelFirstIndex, labelLastIndex + 1);
+            lc.setFlags(LayoutContext.FIRST_AREA, layoutContext.isFirstArea());
+            lc.setFlags(LayoutContext.LAST_AREA, layoutContext.isLastArea());
+            // TO DO: use the right stack limit for the label
+            lc.setStackLimit(layoutContext.getStackLimit());
+            label.addAreas(labelIter, lc);
+        }
 
-                while ((childLM = (Item)breakPosIter.getNextChildLM()) != null) {
-                    childLM.addAreas(breakPosIter, lc);
-                }
-            }
+        // reset the area bpd after adding the label areas and before adding the body areas
+        int savedBPD = 0;
+        if (labelFirstIndex <= labelLastIndex
+            && bodyFirstIndex <= bodyLastIndex) {
+            savedBPD = curBlockArea.getBPD();
+            curBlockArea.setBPD(0);
+        }
+
+        // add body areas
+        if (bodyFirstIndex <= bodyLastIndex) {
+            KnuthPossPosIter bodyIter = new KnuthPossPosIter(bodyList, 
+                    bodyFirstIndex, bodyLastIndex + 1);
+            lc.setFlags(LayoutContext.FIRST_AREA, layoutContext.isFirstArea());
+            lc.setFlags(LayoutContext.LAST_AREA, layoutContext.isLastArea());
+            // TO DO: use the right stack limit for the body
+            lc.setStackLimit(layoutContext.getStackLimit());
+            body.addAreas(bodyIter, lc);
         }
 
-        curBlockArea.setBPD(listItemHeight);
+        // after adding body areas, set the maximum area bpd
+        if (curBlockArea.getBPD() < savedBPD) {
+            curBlockArea.setBPD(savedBPD);
+        }
 
         flush();
 
diff --git a/src/java/org/apache/fop/layoutmgr/table/Body.java b/src/java/org/apache/fop/layoutmgr/table/Body.java
deleted file mode 100644 (file)
index dbdf10b..0000000
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * Copyright 1999-2005 The Apache Software Foundation.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * 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.table;
-
-import java.util.List;
-
-import org.apache.fop.fo.flow.TableBody;
-import org.apache.fop.layoutmgr.LayoutManager;
-import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
-import org.apache.fop.layoutmgr.LeafPosition;
-import org.apache.fop.layoutmgr.BreakPoss;
-import org.apache.fop.layoutmgr.LayoutContext;
-import org.apache.fop.layoutmgr.PositionIterator;
-import org.apache.fop.layoutmgr.BreakPossPosIter;
-import org.apache.fop.layoutmgr.Position;
-import org.apache.fop.layoutmgr.TraitSetter;
-import org.apache.fop.area.Area;
-import org.apache.fop.area.Block;
-import org.apache.fop.traits.MinOptMax;
-
-/**
- * LayoutManager for a table-header, table-footer and table body FO.
- * These fo objects have either rows or cells underneath.
- * Cells are organised into rows.
- */
-public class Body extends BlockStackingLayoutManager {
-    private TableBody fobj;
-    
-    private List columns;
-
-    private int xoffset;
-    private int yoffset;
-    private int bodyHeight;
-
-    //private Block curBlockArea;
-
-    private List childBreaks = new java.util.ArrayList();
-
-    /**
-     * Create a new body layout manager.
-     * @param node the table-body FO
-     */
-    public Body(TableBody node) {
-        super(node);
-        fobj = node;
-    }
-
-    /** @return the table-body|header|footer FO */
-    public TableBody getFObj() {
-        return this.fobj;
-    }
-    
-    /**
-     * Set the columns from the table.
-     *
-     * @param cols the list of columns used for this body
-     */
-    public void setColumns(List cols) {
-        columns = cols;
-    }
-
-    /**
-     * Breaks for this layout manager are of the form of before
-     * or after a row and inside a row.
-     *
-     * @param context the layout context for finding the breaks
-     * @return the next break possibility
-     */
-    public BreakPoss getNextBreakPoss(LayoutContext context) {
-        Row curLM; // currently active LM
-
-        MinOptMax stackSize = new MinOptMax();
-        BreakPoss lastPos = null;
-
-        while ((curLM = (Row)getChildLM()) != null) {
-            // Make break positions
-            // Set up a LayoutContext
-            int ipd = context.getRefIPD();
-            BreakPoss bp;
-
-            LayoutContext childLC = new LayoutContext(0);
-            childLC.setStackLimit(
-                  MinOptMax.subtract(context.getStackLimit(),
-                                     stackSize));
-            childLC.setRefIPD(ipd);
-
-            curLM.setColumns(columns);
-
-            boolean over = false;
-
-            while (!curLM.isFinished()) {
-                if ((bp = curLM.getNextBreakPoss(childLC)) != null) {
-                    if (stackSize.opt + bp.getStackingSize().opt > context.getStackLimit().max) {
-                        // reset to last break
-                        if (lastPos != null) {
-                            LayoutManager lm = lastPos.getLayoutManager();
-                            lm.resetPosition(lastPos.getPosition());
-                            if (lm != curLM) {
-                                curLM.resetPosition(null);
-                            }
-                        } else {
-                            curLM.resetPosition(null);
-                        }
-                        over = true;
-                        break;
-                    }
-                    stackSize.add(bp.getStackingSize());
-                    lastPos = bp;
-                    childBreaks.add(bp);
-
-                    if (bp.nextBreakOverflows()) {
-                        over = true;
-                        break;
-                    }
-
-                    childLC.setStackLimit(MinOptMax.subtract(
-                                             context.getStackLimit(), stackSize));
-                }
-            }
-            BreakPoss breakPoss = new BreakPoss(
-                                    new LeafPosition(this, childBreaks.size() - 1));
-            if (over) {
-                breakPoss.setFlag(BreakPoss.NEXT_OVERFLOWS, true);
-            }
-            breakPoss.setStackingSize(stackSize);
-            return breakPoss;
-        }
-
-        setFinished(true);
-        return null;
-    }
-
-    /**
-     * Set the x offset of this body within the table.
-     * This is used to set the row offsets.
-     * @param off the x offset
-     */
-    public void setXOffset(int off) {
-        xoffset = off;
-    }
-
-    /**
-     * Set the y offset of this body within the table.
-     * This is used to set the row offsets.
-     *
-     * @param off the y offset position
-     */
-    public void setYOffset(int off) {
-        yoffset = off;
-    }
-
-    /**
-     * Add the areas for the break points.
-     * This sets the offset of each row as it is added.
-     *
-     * @param parentIter the position iterator
-     * @param layoutContext the layout context for adding areas
-     */
-    public void addAreas(PositionIterator parentIter,
-                         LayoutContext layoutContext) {
-        getParentArea(null);
-
-        Row childLM;
-        int iStartPos = 0;
-        LayoutContext lc = new LayoutContext(0);
-        int rowoffset = 0;
-        while (parentIter.hasNext()) {
-            LeafPosition lfp = (LeafPosition) parentIter.next();
-            // Add the block areas to Area
-            PositionIterator breakPosIter 
-                = new BreakPossPosIter(childBreaks, iStartPos,
-                                   lfp.getLeafPos() + 1);
-            iStartPos = lfp.getLeafPos() + 1;
-            int lastheight = 0;
-            while ((childLM = (Row)breakPosIter.getNextChildLM()) != null) {
-                childLM.setXOffset(xoffset);
-                childLM.setYOffset(yoffset + rowoffset);
-                childLM.addAreas(breakPosIter, lc);
-                lastheight = childLM.getRowHeight();
-            }
-            rowoffset += lastheight;
-        }
-        bodyHeight = rowoffset;
-
-        flush();
-
-        childBreaks.clear();
-        //curBlockArea = null;
-    }
-
-    /**
-     * Get the body height of the body after adjusting.
-     * Should only be called after adding the body areas.
-     *
-     * @return the body height of this body
-     */
-    public int getBodyHeight() {
-        return bodyHeight;
-    }
-
-    /**
-     * Return an Area which can contain the passed childArea. The childArea
-     * may not yet have any content, but it has essential traits set.
-     * In general, if the LayoutManager already has an Area it simply returns
-     * it. Otherwise, it makes a new Area of the appropriate class.
-     * It gets a parent area for its area by calling its parent LM.
-     * Finally, based on the dimensions of the parent area, it initializes
-     * its own area. This includes setting the content IPD and the maximum
-     * BPD.
-     *
-     * @param childArea the child area
-     * @return the parent are of the child
-     */
-    public Area getParentArea(Area childArea) {
-        return parentLM.getParentArea(childArea);
-    }
-
-    /**
-     * Add the child area.
-     * The table-header, table-footer and table-body areas return
-     * the areas return by the children.
-     *
-     * @param childArea the child area to add
-     */
-    public void addChildArea(Area childArea) {
-        parentLM.addChildArea(childArea);
-    }
-
-    /**
-     * Reset the position of the layout manager.
-     *
-     * @param resetPos the position to reset to
-     */
-    public void resetPosition(Position resetPos) {
-        if (resetPos == null) {
-            reset(null);
-        }
-    }
-
-    /**
-     * Create a body area.
-     * This area has the background and width set.
-     *
-     * @return the new body area
-     */
-    public Area createColumnArea() {
-        Area curBlockArea = new Block();
-
-        TraitSetter.addBackground(curBlockArea, fobj.getCommonBorderPaddingBackground());
-        return curBlockArea;
-    }
-
-}
-
index 0fd99849c83616f9850223626d7ba2e4ae620d1e..a311270b430e2ce16696a9cf1bd1962fe22a3ac1 100644 (file)
@@ -138,7 +138,7 @@ public class Caption extends BlockStackingLayoutManager {
     public void addAreas(PositionIterator parentIter,
                          LayoutContext layoutContext) {
         getParentArea(null);
-        addID(fobj.getId());
+        getPSLM().addIDToPage(fobj.getId());
 
         LayoutManager childLM;
         int iStartPos = 0;
index 5a68aeb663999ffafd721da4ed150c048a12cb65..ca8475ba5f7e4823394f9d82c7b4530a46073e9a 100644 (file)
@@ -23,30 +23,39 @@ import org.apache.fop.fo.flow.Table;
 import org.apache.fop.fo.flow.TableCell;
 import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
 import org.apache.fop.fo.properties.LengthRangeProperty;
+import org.apache.fop.layoutmgr.AreaAdditionUtil;
+import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
 import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
+import org.apache.fop.layoutmgr.KnuthElement;
+import org.apache.fop.layoutmgr.KnuthGlue;
+import org.apache.fop.layoutmgr.KnuthPenalty;
 import org.apache.fop.layoutmgr.LayoutManager;
 import org.apache.fop.layoutmgr.LeafPosition;
 import org.apache.fop.layoutmgr.BreakPoss;
 import org.apache.fop.layoutmgr.LayoutContext;
 import org.apache.fop.layoutmgr.MinOptMaxUtil;
+import org.apache.fop.layoutmgr.NonLeafPosition;
 import org.apache.fop.layoutmgr.PositionIterator;
-import org.apache.fop.layoutmgr.BreakPossPosIter;
 import org.apache.fop.layoutmgr.Position;
 import org.apache.fop.layoutmgr.TraitSetter;
 import org.apache.fop.area.Area;
 import org.apache.fop.area.Block;
 import org.apache.fop.area.Trait;
 import org.apache.fop.traits.MinOptMax;
+import org.apache.tools.ant.taskdefs.condition.IsSet;
 
 import java.util.ArrayList;
+import java.util.LinkedList;
 import java.util.List;
 
 /**
  * LayoutManager for a table-cell FO.
  * A cell contains blocks. These blocks fill the cell.
  */
-public class Cell extends BlockStackingLayoutManager {
+public class Cell extends BlockStackingLayoutManager implements BlockLevelLayoutManager {
+    
     private TableCell fobj;
+    private PrimaryGridUnit gridUnit;
     
     private Block curBlockArea;
 
@@ -65,16 +74,17 @@ public class Cell extends BlockStackingLayoutManager {
     private int borderAndPaddingBPD;
     private boolean emptyCell = true;
 
-    /** List of Lists containing GridUnit instances, one List per row. */
+    /** List of Lists containing OldGridUnit instances, one List per row. */
     private List rows = new java.util.ArrayList(); 
     
     /**
      * Create a new Cell layout manager.
      * @node table-cell FO for which to create the LM
      */
-    public Cell(TableCell node) {
+    public Cell(TableCell node, PrimaryGridUnit pgu) {
         super(node);
         fobj = node;
+        this.gridUnit = pgu;
     }
 
     /** @return the table-cell FO */
@@ -82,6 +92,10 @@ public class Cell extends BlockStackingLayoutManager {
         return this.fobj;
     }
     
+    private boolean isSeparateBorderModel() {
+        return fobj.isSeparateBorderModel();
+    }
+    
     /**
      * @see org.apache.fop.layoutmgr.AbstractLayoutManager#initProperties()
      */
@@ -90,7 +104,7 @@ public class Cell extends BlockStackingLayoutManager {
         borderAndPaddingBPD = 0;
         borderAndPaddingBPD += fobj.getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
         borderAndPaddingBPD += fobj.getCommonBorderPaddingBackground().getBorderAfterWidth(false);
-        if (!fobj.isSeparateBorderModel()) {
+        if (!isSeparateBorderModel()) {
             borderAndPaddingBPD /= 2;
         }
         borderAndPaddingBPD += fobj.getCommonBorderPaddingBackground().getPaddingBefore(false);
@@ -121,22 +135,12 @@ public class Cell extends BlockStackingLayoutManager {
 
     private int getIPIndents() {
         int iIndents = 0;
-        startBorderWidth = 0;
-        endBorderWidth = 0;
-        for (int i = 0; i < rows.size(); i++) {
-            List gridUnits = (List)rows.get(i);
-            startBorderWidth = Math.max(startBorderWidth, 
-                    ((GridUnit)gridUnits.get(0)).
-                        effBorders.getBorderStartWidth(false));
-            endBorderWidth = Math.max(endBorderWidth, 
-                    ((GridUnit)gridUnits.get(gridUnits.size() - 1)).
-                        effBorders.getBorderEndWidth(false));
-        }
-        //iIndents += fobj.getCommonBorderPaddingBackground().getBorderStartWidth(false);
+        int[] startEndBorderWidths = gridUnit.getStartEndBorderWidths();
+        startBorderWidth += startEndBorderWidths[0];
+        endBorderWidth += startEndBorderWidths[1];
         iIndents += startBorderWidth;
-        //iIndents += fobj.getCommonBorderPaddingBackground().getBorderEndWidth(false);
         iIndents += endBorderWidth;
-        if (!fobj.isSeparateBorderModel()) {
+        if (!isSeparateBorderModel()) {
             iIndents /= 2;
         }
         iIndents += fobj.getCommonBorderPaddingBackground().getPaddingStart(false);
@@ -144,6 +148,111 @@ public class Cell extends BlockStackingLayoutManager {
         return iIndents;
     }
     
+    /**
+     * @see org.apache.fop.layoutmgr.LayoutManager#getNextKnuthElements(org.apache.fop.layoutmgr.LayoutContext, int)
+     */
+    public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+        MinOptMax stackSize = new MinOptMax();
+        MinOptMax stackLimit = new MinOptMax(context.getStackLimit());
+
+        BreakPoss lastPos = null;
+
+        referenceIPD = context.getRefIPD(); 
+        cellIPD = referenceIPD;
+        cellIPD -= getIPIndents();
+        if (isSeparateBorderModel()) {
+            int borderSep = fobj.getBorderSeparation().getLengthPair()
+                    .getIPD().getLength().getValue();
+            cellIPD -= borderSep;
+        }
+
+        LinkedList returnedList = null;
+        LinkedList contentList = new LinkedList();
+        LinkedList returnList = new LinkedList();
+        Position returnPosition = new NonLeafPosition(this, null);
+
+        BlockLevelLayoutManager curLM; // currently active LM
+        BlockLevelLayoutManager prevLM = null; // previously active LM
+        while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) {
+            LayoutContext childLC = new LayoutContext(0);
+            // curLM is a ?
+            childLC.setStackLimit(MinOptMax.subtract(context
+                    .getStackLimit(), stackLimit));
+            childLC.setRefIPD(cellIPD);
+
+            // get elements from curLM
+            returnedList = curLM.getNextKnuthElements(childLC, alignment);
+            if (returnedList.size() == 1
+                    && ((KnuthElement) returnedList.getFirst()).isPenalty()
+                    && ((KnuthPenalty) returnedList.getFirst()).getP() == -KnuthElement.INFINITE) {
+                // a descendant of this block has break-before
+                if (returnList.size() == 0) {
+                    // the first child (or its first child ...) has
+                    // break-before;
+                    // all this block, including space before, will be put in
+                    // the
+                    // following page
+                }
+                contentList.addAll(returnedList);
+
+                // "wrap" the Position inside each element
+                // moving the elements from contentList to returnList
+                returnedList = new LinkedList();
+                wrapPositionElements(contentList, returnList);
+
+                return returnList;
+            } else {
+                if (prevLM != null) {
+                    // there is a block handled by prevLM
+                    // before the one handled by curLM
+                    if (mustKeepTogether() 
+                            || prevLM.mustKeepWithNext()
+                            || curLM.mustKeepWithPrevious()) {
+                        // add an infinite penalty to forbid a break between
+                        // blocks
+                        contentList.add(new KnuthPenalty(0,
+                                KnuthElement.INFINITE, false,
+                                new Position(this), false));
+                    } else if (!((KnuthElement) contentList.getLast()).isGlue()) {
+                        // add a null penalty to allow a break between blocks
+                        contentList.add(new KnuthPenalty(0, 0, false,
+                                new Position(this), false));
+                    } else {
+                        // the last element in contentList is a glue;
+                        // it is a feasible breakpoint, there is no need to add
+                        // a penalty
+                    }
+                }
+                contentList.addAll(returnedList);
+                if (returnedList.size() == 0) {
+                    //Avoid NoSuchElementException below (happens with empty blocks)
+                    continue;
+                }
+                if (((KnuthElement) returnedList.getLast()).isPenalty()
+                        && ((KnuthPenalty) returnedList.getLast()).getP() == -KnuthElement.INFINITE) {
+                    // a descendant of this block has break-after
+                    if (curLM.isFinished()) {
+                        // there is no other content in this block;
+                        // it's useless to add space after before a page break
+                        setFinished(true);
+                    }
+
+                    returnedList = new LinkedList();
+                    wrapPositionElements(contentList, returnList);
+
+                    return returnList;
+                }
+            }
+            prevLM = curLM;
+        }
+
+        returnedList = new LinkedList();
+        wrapPositionElements(contentList, returnList);
+        
+        setFinished(true);
+        return returnList;
+    }
+    
     /**
      * Get the next break possibility for this cell.
      * A cell contains blocks so there are breaks around the blocks
@@ -152,7 +261,7 @@ public class Cell extends BlockStackingLayoutManager {
      * @param context the layout context
      * @return the next break possibility
      */
-    public BreakPoss getNextBreakPoss(LayoutContext context) {
+    public BreakPoss getNextBreakPossOLDOLDOLD(LayoutContext context) {
         LayoutManager curLM; // currently active LM
 
         MinOptMax stackSize = new MinOptMax();
@@ -270,6 +379,16 @@ public class Cell extends BlockStackingLayoutManager {
         this.inRowIPDOffset = off;
     }
     
+    /**
+     * Set the content height for this cell. This method is used during
+     * addAreas() stage.
+     *
+     * @param h the height of the contents of this cell
+     */
+    public void setContentHeight(int h) {
+        usedBPD = h;
+    }
+    
     /**
      * Set the row height that contains this cell. This method is used during
      * addAreas() stage.
@@ -280,6 +399,21 @@ public class Cell extends BlockStackingLayoutManager {
         rowHeight = h;
     }
 
+    private int getContentHeight(int rowHeight, GridUnit gu) {
+        int bpd = rowHeight;
+        if (isSeparateBorderModel()) {
+            bpd -= gu.getPrimary().getBorders().getBorderBeforeWidth(false);
+            bpd -= gu.getPrimary().getBorders().getBorderAfterWidth(false);
+        } else {
+            bpd -= gu.getPrimary().getHalfMaxBorderWidth();
+        }
+        CommonBorderPaddingBackground cbpb 
+            = gu.getCell().getCommonBorderPaddingBackground(); 
+        bpd -= cbpb.getPaddingBefore(false);
+        bpd -= cbpb.getPaddingAfter(false);
+        return bpd;
+    }
+    
     /**
      * Add the areas for the break points.
      * The cell contains block stacking layout managers
@@ -291,40 +425,38 @@ public class Cell extends BlockStackingLayoutManager {
     public void addAreas(PositionIterator parentIter,
                          LayoutContext layoutContext) {
         getParentArea(null);
-        BreakPoss bp1 = (BreakPoss)parentIter.peekNext();
-        bBogus = !bp1.generatesAreas(); 
+        //BreakPoss bp1 = (BreakPoss)parentIter.peekNext();
+        bBogus = false;//!bp1.generatesAreas(); 
 
         if (!isBogus()) {
-            addID(fobj.getId());
+            getPSLM().addIDToPage(fobj.getId());
         }
 
-        if (fobj.isSeparateBorderModel()) {
+        if (isSeparateBorderModel()) {
             if (!emptyCell || fobj.showEmptyCells()) {
                 TraitSetter.addBorders(curBlockArea, fobj.getCommonBorderPaddingBackground());
                 TraitSetter.addBackground(curBlockArea, fobj.getCommonBorderPaddingBackground());
             }
         } else {
             TraitSetter.addBackground(curBlockArea, fobj.getCommonBorderPaddingBackground());
-            //TODO Set these booleans right
-            boolean[] outer = new boolean[] {false, false, false, false};
-            if (rows.size() == 1 && ((List)rows.get(0)).size() == 1) {
+            boolean[] outer = new boolean[] {
+                    gridUnit.getFlag(GridUnit.FIRST_IN_TABLE), 
+                    gridUnit.getFlag(GridUnit.LAST_IN_TABLE),
+                    gridUnit.getFlag(GridUnit.IN_FIRST_COLUMN),
+                    gridUnit.getFlag(GridUnit.IN_LAST_COLUMN)};
+            if (!gridUnit.hasSpanning()) {
                 //Can set the borders directly if there's no span
-                CommonBorderPaddingBackground effBorders =
-                    ((GridUnit)((List)rows.get(0)).get(0)).effBorders;
-                //TODO Next line is a temporary hack!
-                TraitSetter.addCollapsingBorders(curBlockArea, 
-                        fobj.getCommonBorderPaddingBackground(), outer);
                 TraitSetter.addCollapsingBorders(curBlockArea, 
-                        effBorders, outer);
+                        gridUnit.getBorders(), outer);
             } else {
                 int dy = yoffset;
-                for (int y = 0; y < rows.size(); y++) {
-                    List gridUnits = (List)rows.get(y);
+                for (int y = 0; y < gridUnit.getRows().size(); y++) {
+                    GridUnit[] gridUnits = (GridUnit[])gridUnit.getRows().get(y);
                     int dx = xoffset;
                     int lastRowHeight = 0;
-                    for (int x = 0; x < gridUnits.size(); x++) {
-                        GridUnit gu = (GridUnit)gridUnits.get(x);
-                        if (!gu.effBorders.hasBorder()) {
+                    for (int x = 0; x < gridUnits.length; x++) {
+                        GridUnit gu = gridUnits[x];
+                        if (!gu.hasBorders()) {
                             continue;
                         }
                         
@@ -332,18 +464,35 @@ public class Cell extends BlockStackingLayoutManager {
                         Block block = new Block();
                         block.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
                         block.setPositioning(Block.ABSOLUTE);
-                        block.setBPD(gu.row.getRowHeight());
-                        lastRowHeight = gu.row.getRowHeight();
-                        int ipd = gu.column.getWidth().getValue();
-                        int borderStartWidth = gu.effBorders.getBorderStartWidth(false) / 2; 
+
+                        int bpd = getContentHeight(rowHeight, gu);
+                        if (isSeparateBorderModel()) {
+                            bpd += (gu.getBorders().getBorderBeforeWidth(false));
+                            bpd += (gu.getBorders().getBorderAfterWidth(false));
+                        } else {
+                            bpd += gridUnit.getHalfMaxBeforeBorderWidth() 
+                                    - (gu.getBorders().getBorderBeforeWidth(false) / 2);
+                            bpd += gridUnit.getHalfMaxAfterBorderWidth() 
+                                    - (gu.getBorders().getBorderAfterWidth(false) / 2);
+                        }
+                        block.setBPD(bpd);
+                        //TODO This needs to be fixed for row spanning
+                        lastRowHeight = rowHeight;
+                        int ipd = gu.getColumn().getColumnWidth().getValue();
+                        int borderStartWidth = gu.getBorders().getBorderStartWidth(false) / 2; 
                         ipd -= borderStartWidth;
-                        ipd -= gu.effBorders.getBorderEndWidth(false) / 2;
+                        ipd -= gu.getBorders().getBorderEndWidth(false) / 2;
                         block.setIPD(ipd);
                         block.setXOffset(dx + borderStartWidth);
-                        block.setYOffset(dy);
-                        TraitSetter.addCollapsingBorders(block, gu.effBorders, outer);
+                        int halfCollapsingBorderHeight = 0;
+                        if (!isSeparateBorderModel()) {
+                            halfCollapsingBorderHeight += 
+                                gu.getBorders().getBorderBeforeWidth(false) / 2;
+                        }
+                        block.setYOffset(dy - halfCollapsingBorderHeight);
+                        TraitSetter.addCollapsingBorders(block, gu.getBorders(), outer);
                         parentLM.addChildArea(block);
-                        dx += gu.column.getWidth().getValue();
+                        dx += gu.getColumn().getColumnWidth().getValue();
                     }
                     dy += lastRowHeight;
                 }
@@ -364,24 +513,9 @@ public class Cell extends BlockStackingLayoutManager {
             }
         }
 
-        LayoutManager childLM;
-        int iStartPos = 0;
-        LayoutContext lc = new LayoutContext(0);
-        while (parentIter.hasNext()) {
-            LeafPosition lfp = (LeafPosition) parentIter.next();
-            // Add the block areas to Area
-            PositionIterator breakPosIter =
-              new BreakPossPosIter(childBreaks, iStartPos,
-                                   lfp.getLeafPos() + 1);
-            iStartPos = lfp.getLeafPos() + 1;
-            while ((childLM = breakPosIter.getNextChildLM()) != null) {
-                childLM.addAreas(breakPosIter, lc);
-            }
-        }
-
+        AreaAdditionUtil.addAreas(parentIter, layoutContext);
         
-        int contentBPD = rowHeight;
-        contentBPD -= borderAndPaddingBPD;
+        int contentBPD = getContentHeight(rowHeight, gridUnit);
         curBlockArea.setBPD(contentBPD);
 
         flush();
@@ -410,23 +544,28 @@ public class Cell extends BlockStackingLayoutManager {
             curBlockArea.setPositioning(Block.ABSOLUTE);
             int indent = 0;
             indent += startBorderWidth;
-            if (!fobj.isSeparateBorderModel()) {
+            if (!isSeparateBorderModel()) {
                 indent /= 2;
             }
             indent += fobj.getCommonBorderPaddingBackground().getPaddingStart(false);
             // set position
             int halfBorderSep = 0;
-            if (fobj.isSeparateBorderModel()) {
+            if (isSeparateBorderModel()) {
                 halfBorderSep = fobj.getBorderSeparation().getLengthPair()
                         .getIPD().getLength().getValue() / 2;
             }
-            int halfCollapsingBorderHeight = 0;
-            if (!fobj.isSeparateBorderModel()) {
-                halfCollapsingBorderHeight += 
-                    fobj.getCommonBorderPaddingBackground().getBorderBeforeWidth(false) / 2;
+            int borderAdjust = 0;
+            if (!isSeparateBorderModel()) {
+                if (gridUnit.hasSpanning()) {
+                    borderAdjust -= gridUnit.getHalfMaxBeforeBorderWidth();
+                } else {
+                    borderAdjust += gridUnit.getHalfMaxBeforeBorderWidth();
+                }
+            } else {
+                //borderAdjust += gridUnit.getBorders().getBorderBeforeWidth(false);
             }
             curBlockArea.setXOffset(xoffset + inRowIPDOffset + halfBorderSep + indent);
-            curBlockArea.setYOffset(yoffset - halfCollapsingBorderHeight);
+            curBlockArea.setYOffset(yoffset - borderAdjust);
             curBlockArea.setIPD(cellIPD);
             //curBlockArea.setHeight();
 
@@ -461,5 +600,52 @@ public class Cell extends BlockStackingLayoutManager {
         }
     }
 
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#negotiateBPDAdjustment(int, org.apache.fop.layoutmgr.KnuthElement)
+     */
+    public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#discardSpace(org.apache.fop.layoutmgr.KnuthGlue)
+     */
+    public void discardSpace(KnuthGlue spaceGlue) {
+        // TODO Auto-generated method stub
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether()
+     */
+    public boolean mustKeepTogether() {
+        //TODO Keeps will have to be more sophisticated sooner or later
+        return ((BlockLevelLayoutManager)getParent()).mustKeepTogether()/* 
+                || !fobj.getKeepTogether().getWithinPage().isAuto()
+                || !fobj.getKeepTogether().getWithinColumn().isAuto()*/;
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious()
+     */
+    public boolean mustKeepWithPrevious() {
+        return false; //TODO FIX ME
+        /*
+        return !fobj.getKeepWithPrevious().getWithinPage().isAuto()
+            || !fobj.getKeepWithPrevious().getWithinColumn().isAuto();
+            */
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext()
+     */
+    public boolean mustKeepWithNext() {
+        return false; //TODO FIX ME
+        /*
+        return !fobj.getKeepWithNext().getWithinPage().isAuto()
+            || !fobj.getKeepWithNext().getWithinColumn().isAuto();
+            */
+    }
+
 }
 
index 18999f0553d4826b56db91375c1138ca4a04f655..c64bfc1b4fd9fd47eef0ca671c5c148021708414 100644 (file)
@@ -28,14 +28,26 @@ import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo;
  */
 public abstract class CollapsingBorderModel {
 
+    /** before side */
+    protected static final int BEFORE = CommonBorderPaddingBackground.BEFORE;
+    /** after side */
+    protected static final int AFTER = CommonBorderPaddingBackground.AFTER;
+    /** start side */
+    protected static final int START = CommonBorderPaddingBackground.START;
+    /** end side */
+    protected static final int END = CommonBorderPaddingBackground.END;
+    
+    /** Flag: current grid unit is either start or end of the table. */
+    public static final int VERTICAL_START_END_OF_TABLE = 1;
+    
     /** Indicates that the cell is/starts in the first row being painted on a particular page */
-    public static final int FIRST_ROW_IN_TABLE_PART = 1;
+    //public static final int FIRST_ROW_IN_TABLE_PART = 1;
     /** Indicates that the cell is/ends in the last row being painted on a particular page */
-    public static final int LAST_ROW_IN_TABLE_PART  = 2;
+    //public static final int LAST_ROW_IN_TABLE_PART  = 2;
     /** Indicates that the cell is/starts in the first row of a body/table-header/table-footer */
-    public static final int FIRST_ROW_IN_GROUP      = 4;
+    //public static final int FIRST_ROW_IN_GROUP      = 4;
     /** Indicates that the cell is/end in the last row of a body/table-header/table-footer */
-    public static final int LAST_ROW_IN_GROUP       = 8;
+    //public static final int LAST_ROW_IN_GROUP       = 8;
     
     private static CollapsingBorderModel collapse = null;
     private static CollapsingBorderModel collapseWithPrecedence = null;
@@ -111,8 +123,8 @@ public abstract class CollapsingBorderModel {
     
     /**
      * Determines the winning BorderInfo.
-     * @param current cell info of the current element
-     * @param neighbour cell info of the neighbouring element
+     * @param current grid unit of the current element
+     * @param neighbour grid unit of the neighbouring element
      * @return the winning BorderInfo
      */
     public abstract BorderInfo determineWinner(
index e3e61033a8fd8b6daf722eb6b78c3b0de2d56e69..98d7fd5bccca989ec621c9b986a48cfbb8db1f47 100644 (file)
 package org.apache.fop.layoutmgr.table;
 
 import org.apache.fop.fo.Constants;
-import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
+import org.apache.fop.fo.flow.Table;
+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.properties.CommonBorderPaddingBackground.BorderInfo;
 
 /**
@@ -29,48 +33,43 @@ import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo;
  */
 public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel {
 
-    private static final int BEFORE = CommonBorderPaddingBackground.BEFORE;
-    private static final int AFTER = CommonBorderPaddingBackground.AFTER;
-    private static final int START = CommonBorderPaddingBackground.START;
-    private static final int END = CommonBorderPaddingBackground.END;
-    
     public BorderInfo determineWinner(GridUnit currentGridUnit, 
             GridUnit otherGridUnit, int side, int flags) {
         final boolean vertical = isVerticalRelation(side);
         final int otherSide = getOtherSide(side);
         
         //Get cells
-        Cell currentCell = currentGridUnit.layoutManager;
-        Cell otherCell = null;
+        TableCell currentCell = currentGridUnit.getCell();
+        TableCell otherCell = null;
         if (otherGridUnit != null) {
-            otherCell = otherGridUnit.layoutManager;
+            otherCell = otherGridUnit.getCell();
         }
         
         //Get rows
-        Row currentRow = currentGridUnit.row;
-        Row otherRow = null;
+        TableRow currentRow = currentGridUnit.getRow();
+        TableRow otherRow = null;
         if (vertical && otherCell != null) {
-            otherRow = otherGridUnit.row;
+            otherRow = otherGridUnit.getRow();
         }
         
         //get bodies
-        Body currentBody = (Body)currentRow.getParent();
-        Body otherBody = null;
+        TableBody currentBody = currentGridUnit.getBody();
+        TableBody otherBody = null;
         if (otherRow != null) {
-            otherBody = (Body)otherRow.getParent();
+            otherBody = otherGridUnit.getBody();
         }
 
         //get columns
-        Column currentColumn = (Column)currentGridUnit.column;
-        Column otherColumn = null;
+        TableColumn currentColumn = currentGridUnit.getColumn();
+        TableColumn otherColumn = null;
         if (otherGridUnit != null) {
-            otherColumn = (Column)otherGridUnit.column;
+            otherColumn = otherGridUnit.getColumn();
         }
         
         //TODO get column groups
         
         //Get table
-        TableLayoutManager table = (TableLayoutManager)currentBody.getParent();
+        Table table = currentGridUnit.getTable();
         
         //----------------------------------------------------------------------
         //We're creating two arrays containing the applicable BorderInfos for
@@ -85,44 +84,51 @@ public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel {
         if (otherGridUnit != null) {
             other[0] = otherGridUnit.getOriginalBorderInfoForCell(otherSide);
         }
-        if (side == BEFORE 
-                || side == AFTER
-                || (currentColumn.isFirst() && side == START)
-                || (currentColumn.isLast() && side == END)) {
+        if ((currentRow != null) 
+                && (side == BEFORE 
+                    || side == AFTER
+                    || (currentGridUnit.getFlag(GridUnit.IN_FIRST_COLUMN) && side == START)
+                    || (currentGridUnit.getFlag(GridUnit.IN_LAST_COLUMN) && side == END))) {
             //row
-            current[1] = currentRow.getFObj().getCommonBorderPaddingBackground().getBorderInfo(side);
+            current[1] = currentRow.getCommonBorderPaddingBackground().getBorderInfo(side);
         }
         if (otherRow != null) {
             //row
-            other[1] = otherRow.getFObj().getCommonBorderPaddingBackground().getBorderInfo(otherSide);
+            other[1] = otherRow.getCommonBorderPaddingBackground().getBorderInfo(otherSide);
         }
-        if ((side == BEFORE && currentRow.isFirstInBody())
-                || (side == AFTER && currentRow.isLastInBody())
-                || (currentColumn.isFirst() && side == START)
-                || (currentColumn.isLast() && side == END)) {
+        if ((side == BEFORE && currentGridUnit.getFlag(GridUnit.FIRST_IN_BODY))
+                || (side == AFTER && currentGridUnit.getFlag(GridUnit.LAST_IN_BODY))
+                || (currentGridUnit.getFlag(GridUnit.IN_FIRST_COLUMN) && side == START)
+                || (currentGridUnit.getFlag(GridUnit.IN_LAST_COLUMN) && side == END)) {
             //row group (=body, table-header or table-footer)
-            current[2] = currentBody.getFObj().getCommonBorderPaddingBackground().getBorderInfo(side);
+            current[2] = currentBody.getCommonBorderPaddingBackground().getBorderInfo(side);
         }
-        if ((otherSide == BEFORE && otherRow.isFirstInBody())
-                || (otherSide == AFTER && otherRow.isLastInBody())) {
+        if (otherGridUnit != null
+                && otherBody != null
+                && ((otherSide == BEFORE && otherGridUnit.getFlag(GridUnit.FIRST_IN_BODY))
+                    || (otherSide == AFTER && otherGridUnit.getFlag(GridUnit.LAST_IN_BODY)))) {
             //row group (=body, table-header or table-footer)
-            other[2] = otherBody.getFObj().getCommonBorderPaddingBackground().getBorderInfo(otherSide);
+            other[2] = otherBody.getCommonBorderPaddingBackground().getBorderInfo(otherSide);
         }
         if ((side == BEFORE && otherGridUnit == null)
                 || (side == AFTER && otherGridUnit == null)
                 || (side == START)
                 || (side == END)) {
             //column
-            current[3] = currentColumn.getFObj().getCommonBorderPaddingBackground().getBorderInfo(side);
+            current[3] = currentColumn.getCommonBorderPaddingBackground().getBorderInfo(side);
         }
         if (otherColumn != null) {
             //column
-            other[3] = otherColumn.getFObj().getCommonBorderPaddingBackground().getBorderInfo(otherSide);
+            other[3] = otherColumn.getCommonBorderPaddingBackground().getBorderInfo(otherSide);
         }
         //TODO current[4] and other[4] for column groups
-        if (otherGridUnit == null) {
+        if (otherGridUnit == null
+            && ((side == BEFORE && (flags & VERTICAL_START_END_OF_TABLE) > 0)
+                    || (side == AFTER && (flags & VERTICAL_START_END_OF_TABLE) > 0)
+                    || (side == START)
+                    || (side == END))) {
             //table
-            current[5] = table.getTable().getCommonBorderPaddingBackground().getBorderInfo(side);
+            current[5] = table.getCommonBorderPaddingBackground().getBorderInfo(side);
         }
         //other[6] is always null, since it's always the same table
         
@@ -136,7 +142,6 @@ public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel {
         
         // *** Rule 2 ***
         if (!doRule2(current, other)) {
-            return null; //paint no border
         }
         
         // *** Rule 3 ***
@@ -161,7 +166,7 @@ public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel {
     }
 
     private BorderInfo doRule1(BorderInfo[] current, BorderInfo[] other) {
-        for (int i = 0; i < current.length - 1; i++) {
+        for (int i = 0; i < current.length; i++) {
             if ((current[i] != null) && (current[i].getStyle() == Constants.EN_HIDDEN)) {
                 return current[i];
             }
@@ -174,7 +179,7 @@ public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel {
     
     private boolean doRule2(BorderInfo[] current, BorderInfo[] other) {
         boolean found = false;
-        for (int i = 0; i < current.length - 1; i++) {
+        for (int i = 0; i < current.length; i++) {
             if ((current[i] != null) && (current[i].getStyle() != Constants.EN_NONE)) {
                 found = true;
                 break;
@@ -190,7 +195,7 @@ public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel {
     private BorderInfo doRule3(BorderInfo[] current, BorderInfo[] other) {
         int width = 0;
         //Find max border width
-        for (int i = 0; i < current.length - 1; i++) {
+        for (int i = 0; i < current.length; i++) {
             if ((current[i] != null) && (current[i].getRetainedWidth() > width)) {
                 width = current[i].getRetainedWidth();
             }
@@ -201,13 +206,12 @@ public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel {
         BorderInfo widest = null;
         int count = 0;
         //See if there's only one with the widest border
-        for (int i = 0; i < current.length - 1; i++) {
+        for (int i = 0; i < current.length; i++) {
             if ((current[i] != null) && (current[i].getRetainedWidth() == width)) {
                 count++;
                 if (widest == null) {
                     widest = current[i];
                 }
-                break;
             } else {
                 current[i] = null; //Discard the narrower ones
             }
@@ -216,7 +220,6 @@ public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel {
                 if (widest == null) {
                     widest = other[i];
                 }
-                break;
             } else {
                 other[i] = null; //Discard the narrower ones
             }
@@ -231,26 +234,24 @@ public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel {
     private BorderInfo doRule4(BorderInfo[] current, BorderInfo[] other) {
         int pref = getPreferenceValue(Constants.EN_INSET); //Lowest preference
         //Find highest preference value
-        for (int i = 0; i < current.length - 1; i++) {
+        for (int i = 0; i < current.length; i++) {
             if (current[i] != null) {
                 int currPref = getPreferenceValue(current[i].getStyle());
                 if (currPref > pref) {
                     pref = currPref;
-                    break;
                 }
             }
             if (other[i] != null) {
                 int currPref = getPreferenceValue(other[i].getStyle());
                 if (currPref > pref) {
                     pref = currPref;
-                    break;
                 }
             }
         }
         BorderInfo preferred = null;
         int count = 0;
         //See if there's only one with the preferred border style
-        for (int i = 0; i < current.length - 1; i++) {
+        for (int i = 0; i < current.length; i++) {
             if (current[i] != null) {
                 int currPref = getPreferenceValue(current[i].getStyle());
                 if (currPref == pref) {
@@ -284,7 +285,7 @@ public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel {
     }
 
     private BorderInfo doRule5(BorderInfo[] current, BorderInfo[] other) {
-        for (int i = 0; i < current.length - 1; i++) {
+        for (int i = 0; i < current.length; i++) {
             if (current[i] != null) {
                 return current[i];
             }
diff --git a/src/java/org/apache/fop/layoutmgr/table/Column.java b/src/java/org/apache/fop/layoutmgr/table/Column.java
deleted file mode 100644 (file)
index a09d6f7..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 1999-2005 The Apache Software Foundation.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * 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.table;
-
-import org.apache.fop.datatypes.Length;
-import org.apache.fop.layoutmgr.AbstractLayoutManager;
-import org.apache.fop.layoutmgr.BreakPoss;
-import org.apache.fop.layoutmgr.LayoutContext;
-import org.apache.fop.layoutmgr.PositionIterator;
-import org.apache.fop.layoutmgr.TraitSetter;
-import org.apache.fop.fo.flow.TableCell;
-import org.apache.fop.fo.flow.TableColumn;
-import org.apache.fop.area.Area;
-import org.apache.fop.area.Block;
-
-/**
- * LayoutManager for a table-column FO.
- * The table creates an area for the table-column background, this class
- * is used to do the area creation. This is used during the layout to handle
- * column properties.
- */
-public class Column extends AbstractLayoutManager {
-    
-    private TableColumn fobj;
-    
-    private Length columnWidth;
-
-    /**
-     * Create a new column layout manager.
-     * @param node the table-column FO
-     */
-    public Column(TableColumn node) {
-         super(node);
-         fobj = node;
-         columnWidth = fobj.getColumnWidth();
-    }
-
-    /** @return the table-column FO */
-    public TableColumn getFObj() {
-        return this.fobj;
-    }
-    
-    /** @return true if the column is the first column */
-    public boolean isFirst() {
-        return ((TableLayoutManager)getParent()).isFirst(this);
-    }
-    
-    /** @return true if the column is the last column */
-    public boolean isLast() {
-        return ((TableLayoutManager)getParent()).isLast(this);
-    }
-    
-    /**
-     * Get the next break possibility.
-     * Columns do not create or return any areas.
-     *
-     * @param context the layout context
-     * @return the break possibility, always null
-     */
-    public BreakPoss getNextBreakPoss(LayoutContext context) {
-        return null;
-    }
-
-    /**
-     * Add the areas.
-     * Although this adds no areas it is used to add the id
-     * reference of the table-column.
-     *
-     * @param parentIter the position iterator
-     * @param layoutContext the context
-     */
-    public void addAreas(PositionIterator parentIter,
-                         LayoutContext layoutContext) {
-    }
-
-    /**
-     * Get the parent area.
-     * This does nothing.
-     *
-     * @param childArea the child area
-     * @return always null
-     */
-    public Area getParentArea(Area childArea) {
-        return null;
-    }
-
-    /**
-     * Overrides the default column-with coming from the FO.
-     * @param width the new width to use
-     */
-    public void setWidth(Length width) {
-        this.columnWidth = width;
-    }
-    
-    /**
-     * Get the width of this column.
-     *
-     * @return the width of the column
-     */
-    public Length getWidth() {
-        return columnWidth;
-    }
-
-    /**
-     * Create a column area.
-     * This area has the background and width set.
-     * The Body manager will then set the offset of the column.
-     *
-     * @return the new column area
-     */
-    public Area createColumnArea() {
-        Area curBlockArea = new Block();
-
-        TraitSetter.addBackground(curBlockArea, fobj.getCommonBorderPaddingBackground());
-        return curBlockArea;
-    }
-}
-
diff --git a/src/java/org/apache/fop/layoutmgr/table/ColumnSetup.java b/src/java/org/apache/fop/layoutmgr/table/ColumnSetup.java
new file mode 100644 (file)
index 0000000..c732763
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr.table;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.fop.fo.flow.Table;
+import org.apache.fop.fo.flow.TableColumn;
+
+/**
+ * Class holding a number of columns making up the column setup of a row.
+ */
+public class ColumnSetup {
+
+    /** Logger **/
+    private static Log log = LogFactory.getLog(ColumnSetup.class);
+
+    private Table table;
+    private List columns = new java.util.ArrayList();
+    private int maxColIndexReferenced = 0;
+    
+    public ColumnSetup(Table table) {
+        this.table = table;
+        prepareExplicitColumns();
+        if (getColumnCount() == 0) {
+            createColumnsFromFirstRow();
+        }
+    }
+    
+    private void prepareExplicitColumns() {
+        List rawCols = table.getColumns();
+        if (rawCols != null) {
+            int colnum = 1;
+            ListIterator iter = rawCols.listIterator();
+            while (iter.hasNext()) {
+                TableColumn col = (TableColumn)iter.next();
+                if (col.hasColumnNumber()) {
+                    colnum = col.getColumnNumber();
+                }
+                for (int i = 0; i < col.getNumberColumnsRepeated(); i++) {
+                    while (colnum > columns.size()) {
+                        columns.add(null);
+                    }
+                    columns.set(colnum - 1, col);
+                    colnum++;
+                }
+            }
+            //Post-processing the list (looking for gaps)
+            int pos = 1;
+            ListIterator ppIter = columns.listIterator();
+            while (ppIter.hasNext()) {
+                TableColumn col = (TableColumn)ppIter.next();
+                if (col == null) {
+                    log.error("Found a gap in the table-columns at position " + pos);
+                }
+                pos++;
+            }
+        }
+    }
+
+    /**
+     * Returns a column. If the index of the column is bigger than the number of explicitly
+     * defined columns the last column is returned.
+     * @param index index of the column (1 is the first column)
+     * @return the requested column
+     */
+    public TableColumn getColumn(int index) {
+        int size = columns.size();
+        if (index > size) {
+            maxColIndexReferenced = Math.max(maxColIndexReferenced, index);
+            return (TableColumn)columns.get(size - 1);
+        } else {
+            return (TableColumn)columns.get(index - 1);
+        }
+    }
+    /** @see java.lang.Object#toString() */
+    public String toString() {
+        return columns.toString();
+    }
+
+    /** @return the number of columns in the setup. */
+    public int getColumnCount() {
+        if (maxColIndexReferenced > columns.size()) {
+            return maxColIndexReferenced;
+        } else {
+            return columns.size();
+        }
+   }
+    
+    /** @return an Iterator over all columns */
+    public Iterator iterator() {
+        return this.columns.iterator();
+    }
+    
+    private void createColumnsFromFirstRow() {
+        //TODO Create oldColumns from first row here 
+        //--> rule 2 in "fixed table layout", see CSS2, 17.5.2
+        //Alternative: extend oldColumns on-the-fly, but in this case we need the
+        //new property evaluation context so proportional-column-width() works
+        //correctly.
+        if (columns.size() == 0) {
+            this.columns.add(table.getDefaultColumn());
+        }
+    }
+
+    /**
+     * @param col column index (1 is first column)
+     * @return the X offset of the requested column
+     */
+    public int getXOffset(int col) {
+        int xoffset = 0;
+        for (int i = 1; i < col; i++) {
+            xoffset += getColumn(i).getColumnWidth().getValue();
+        }
+        return xoffset;
+    }
+
+}
diff --git a/src/java/org/apache/fop/layoutmgr/table/EffRow.java b/src/java/org/apache/fop/layoutmgr/table/EffRow.java
new file mode 100644 (file)
index 0000000..675c290
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr.table;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.fop.traits.MinOptMax;
+
+/**
+ * This class represents an effective row in a table and holds a list of grid units occupying
+ * the row as well as some additional values.
+ */
+public class EffRow {
+    
+    private List gridUnits = new java.util.ArrayList();
+    private int index;
+    private int bodyType;
+    private MinOptMax height;
+    private MinOptMax explicitHeight;
+    
+    public EffRow(int index, int bodyType) {
+        this.index = index;
+        this.bodyType = bodyType;
+        this.height = height;
+    }
+    
+    public int getIndex() {
+        return this.index;
+    }
+    
+    public int getBodyType() {
+        return this.bodyType;
+    }
+    
+    public MinOptMax getHeight() {
+        return this.height;
+    }
+    
+    public void setHeight(MinOptMax mom) {
+        this.height = mom;
+    }
+    
+    public MinOptMax getExplicitHeight() {
+        return this.explicitHeight;
+    }
+    
+    public void setExplicitHeight(MinOptMax mom) {
+        this.explicitHeight = mom;
+    }
+    
+    public List getGridUnits() {
+        return gridUnits;
+    }
+    
+    /**
+     * Returns the grid unit at a given position.
+     * @param column index of the grid unit in the row (zero based)
+     * @return the requested grid unit.
+     */
+    public GridUnit getGridUnit(int column) {
+        return (GridUnit)gridUnits.get(column);
+    }
+    
+    /**
+     * Returns the grid unit at a given position. In contrast to getGridUnit() this 
+     * method returns null if there's no grid unit at the given position. The number of
+     * grid units for row x can be smaller than the number of grid units for row x-1.
+     * @param column index of the grid unit in the row (zero based)
+     * @return the requested grid unit or null if there's no grid unit at this position.
+     */
+    public GridUnit safelyGetGridUnit(int column) {
+        if (column < gridUnits.size()) {
+            return (GridUnit)gridUnits.get(column);
+        } else {
+            return null;
+        }
+    }
+    
+    public void setFlagForAllGridUnits(int flag, boolean value) {
+        Iterator iter = gridUnits.iterator();
+        while (iter.hasNext()) {
+            GridUnit gu = (GridUnit)iter.next();
+            gu.setFlag(flag, value);
+        }
+    }
+
+    /** @see java.lang.Object#toString() */
+    public String toString() {
+        StringBuffer sb = new StringBuffer("EffRow {");
+        sb.append(index);
+        if (getBodyType() == TableRowIterator.BODY) {
+            sb.append(" in body");
+        } else if (getBodyType() == TableRowIterator.HEADER) {
+            sb.append(" in header");
+        } else {
+            sb.append(" in footer");
+        }
+        sb.append(", ").append(height);
+        sb.append(", ").append(explicitHeight);
+        sb.append(", ").append(gridUnits.size()).append(" gu");
+        sb.append("}");
+        return sb.toString();
+    }
+}
\ No newline at end of file
diff --git a/src/java/org/apache/fop/layoutmgr/table/EmptyGridUnit.java b/src/java/org/apache/fop/layoutmgr/table/EmptyGridUnit.java
new file mode 100644 (file)
index 0000000..8d17420
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr.table;
+
+import org.apache.fop.fo.flow.TableBody;
+import org.apache.fop.fo.flow.TableColumn;
+import org.apache.fop.fo.flow.TableRow;
+
+/**
+ * GridUnit subclass for empty grid units.
+ */
+public class EmptyGridUnit extends GridUnit {
+
+    private TableRow row;
+    private TableBody body;
+    
+    /**
+     * @param row Optional table-row instance
+     * @param column table-column instance
+     * @param body table-body the grid unit belongs to
+     * @param startCol column index 
+     */
+    public EmptyGridUnit(TableRow row, TableColumn column, TableBody body, 
+            int startCol) {
+        super(null, null, column, startCol, 0);
+        this.row = row;
+        this.body = body;
+    }
+    
+    /** @see org.apache.fop.layoutmgr.table.GridUnit#isPrimary() */
+    public boolean isPrimary() {
+        return true;
+    }
+    
+    /** @see org.apache.fop.layoutmgr.table.GridUnit#getBody() */
+    public TableBody getBody() {
+        return this.body;
+    }
+
+    /** @see org.apache.fop.layoutmgr.table.GridUnit#getRow() */
+    public TableRow getRow() {
+        return this.row;
+    }
+}
index e89f9d8ec03e306e7e1419c4078db3d4f7a520d8..5b1d85d2b7f8cd9810e4861b0fb72fc5101565be 100644 (file)
 
 package org.apache.fop.layoutmgr.table;
 
+import org.apache.fop.fo.FONode;
 import org.apache.fop.fo.flow.Table;
+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.properties.CommonBorderPaddingBackground;
 import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo;
 
-
+/**
+ * This class represents one grid unit inside a table.
+ */
 public class GridUnit {
+
+    /** Indicates that the grid unit is in the first column. */
+    public static final int IN_FIRST_COLUMN = 0;
+    /** Indicates that the grid unit is in the last column. */
+    public static final int IN_LAST_COLUMN = 1;
+    /** Indicates that the grid unit is in the first row (context: table). */
+    public static final int FIRST_IN_TABLE = 2;
+    /** Indicates that the grid unit is in the first row (context: body). */
+    public static final int FIRST_IN_BODY = 3;
+    /** Indicates that the grid unit is in the last row (context: body). */
+    public static final int LAST_IN_BODY = 4;
+    /** Indicates that the grid unit is in the last row (context: table). */
+    public static final int LAST_IN_TABLE = 5;
     
-    /** layout manager for the cell occupying this grid unit, may be null */
-    public Cell layoutManager;
-    /** layout manager for the column that this grid unit belongs to */
-    public Column column;
-    /** layout manager for the row that this grid unit belongs to */
-    public Row row;
+    /** Primary grid unit */
+    private PrimaryGridUnit primary;
+    /** Table cell which occupies this grid unit */
+    private TableCell cell;
+    /** Table row which occupied this grid unit (may be null) */
+    private TableRow row;
+    /** Table column that this grid unit belongs to */
+    private TableColumn column;
+    
+    /** start index of grid unit within row in column direction */
+    private int startCol;
     /** index of grid unit within cell in column direction */
-    public int colSpanIndex;
+    private int colSpanIndex;
     /** index of grid unit within cell in row direction */
-    public int rowSpanIndex;
-    /** effective borders for a cell slot (used for collapsing border model) */
-    public CommonBorderPaddingBackground effBorders;
+    private int rowSpanIndex;
+    /** effective borders for a cell slot */
+    private CommonBorderPaddingBackground effBorders;
+    /** flags for the grid unit */
+    private byte flags = 0;
+    
+    
+    public GridUnit(TableCell cell, TableColumn column, int startCol, int colSpanIndex) {
+        this(null, cell, column, startCol, colSpanIndex);
+    }
+    
+    public GridUnit(PrimaryGridUnit primary, TableColumn column, int startCol, int colSpanIndex) {
+        this(primary, primary.getCell(), column, startCol, colSpanIndex);
+    }
     
-    public GridUnit(Cell layoutManager, int colSpanIndex) {
-        this.layoutManager = layoutManager;
+    protected GridUnit(PrimaryGridUnit primary, TableCell cell, TableColumn column, int startCol, int colSpanIndex) {
+        this.primary = primary;
+        this.cell = cell;
+        this.column = column;
+        this.startCol = startCol;
         this.colSpanIndex = colSpanIndex;
-        this.rowSpanIndex = 0;
     }
     
-    public GridUnit(Cell layoutManager) {
-        this(layoutManager, 0);
+    public TableCell getCell() {
+        return this.cell;
+    }
+    
+    public TableColumn getColumn() {
+        return this.column;
+    }
+    
+    public TableRow getRow() {
+        if (this.row != null) {
+            return this.row;
+        } else if (getCell().getParent() instanceof TableRow) {
+            return (TableRow)getCell().getParent();
+        } else {
+            return null;
+        }
+    }
+    
+    /**
+     * Sets the table-row FO, if applicable.
+     * @param rowFO the table-row FO
+     */
+    public void setRow(TableRow rowFO) {
+        this.row = rowFO;
+    }
+
+    public TableBody getBody() {
+        FONode node = getCell();
+        while (node != null && !(node instanceof TableBody)) {
+            node = node.getParent();
+        }
+        return (TableBody)node;
+    }
+    
+    public Table getTable() {
+        FONode node = getBody();
+        while (node != null && !(node instanceof Table)) {
+            node = node.getParent();
+        }
+        return (Table)node;
+    }
+    
+    /**
+     * @return the primary grid unit if this is a spanned grid unit
+     */
+    public PrimaryGridUnit getPrimary() {
+        return (isPrimary() ? (PrimaryGridUnit)this : this.primary);
     }
 
-    /** @return true if the grid unit is the primary of a cell */
-    public boolean isPrimaryGridUnit() {
-        return (colSpanIndex == 0) && (rowSpanIndex == 0);
+    public boolean isPrimary() {
+        return false;
+    }
+    
+    public boolean isEmpty() {
+        return this.cell == null;
+    }
+    
+    public int getStartCol() {
+        return this.startCol;
     }
     
     /** @return true if the grid unit is the last in column spanning direction */
     public boolean isLastGridUnitColSpan() {
-        if (layoutManager != null) {
-            return (colSpanIndex == layoutManager.getFObj().getNumberColumnsSpanned() - 1);
+        if (cell != null) {
+            return (colSpanIndex == cell.getNumberColumnsSpanned() - 1);
         } else {
             return true;
         }
@@ -64,68 +154,136 @@ public class GridUnit {
     
     /** @return true if the grid unit is the last in column spanning direction */
     public boolean isLastGridUnitRowSpan() {
-        if (layoutManager != null) {
-            return (rowSpanIndex == layoutManager.getFObj().getNumberRowsSpanned() - 1);
+        if (cell != null) {
+            return (rowSpanIndex == cell.getNumberRowsSpanned() - 1);
         } else {
             return true;
         }
     }
     
-    /** @return true if the cell is part of a span in column direction */
-    public boolean isColSpan() {
-        return (colSpanIndex > 0);
+    /**
+     * @return the index of the grid unit inside a cell in row direction
+     */
+    public int getRowSpanIndex() {
+        return this.rowSpanIndex;
+    }
+    
+    /**
+     * @return the index of the grid unit inside a cell in column direction
+     */
+    public int getColSpanIndex() {
+        return this.colSpanIndex;
     }
 
+    /**
+     * Returns a BorderInfo instance for a side of the currently applicable cell before border
+     * resolution (i.e. the value from the FO). A return value of null indicates an empty cell.
+     * See CollapsingBorderModel(EyeCatching) where this method is used. 
+     * @param side for which side to return the BorderInfo
+     * @return the requested BorderInfo instance or null if the grid unit is an empty cell
+     */
     public BorderInfo getOriginalBorderInfoForCell(int side) {
-        if (layoutManager != null) {
-            return layoutManager.getFObj().getCommonBorderPaddingBackground().getBorderInfo(side);
+        if (cell != null) {
+            return cell.getCommonBorderPaddingBackground().getBorderInfo(side);
         } else {
             return null;
         }
     }
     
     /**
-     * Assign the borders from the given cell to this cell info. Used in
+     * @return the resolved normal borders for this grid unit
+     */
+    public CommonBorderPaddingBackground getBorders() {
+        return this.effBorders;
+    }
+    
+    /**
+     * @return true if the grid unit has any borders.
+     */
+    public boolean hasBorders() {
+        return (getBorders() != null) && getBorders().hasBorder();
+    }
+    
+    /**
+     * Assigns the borders from the given cell to this cell info. Used in
      * case of separate border model.
-     * @param current cell to take the borders from
      */
-    public void assignBorder(Cell current) {
-        if (current != null) {
-            this.effBorders = current.getFObj().getCommonBorderPaddingBackground();
+    public void assignBorderForSeparateBorderModel() {
+        if (cell != null) {
+            this.effBorders = cell.getCommonBorderPaddingBackground();
         }
     }
     
     /**
-     * Assign the borders directly.
-     * @param borders the borders to use
+     * Resolve collapsing borders for the given cell. Used in case of the collapsing border model.
+     * @param other neighbouring grid unit if any
+     * @param side the side to resolve (one of CommonBorderPaddingBackground.BEFORE|AFTER|START|END)
      */
-    public void assignBorder(CommonBorderPaddingBackground borders) {
-        if (borders != null) {
-            this.effBorders = borders;
-        }
+    public void resolveBorder(GridUnit other, int side) {
+        resolveBorder(other, side, 0);
     }
     
     /**
-     * Resolve collapsing borders for the given cell and store the resulting
-     * borders in this cell info. Use in case of the collapsing border model.
-     * @param current cell to resolve borders for
-     * @param before cell before the current cell, if any
-     * @param after cell after the current cell, if any
-     * @param start cell preceeding the current cell, if any
-     * @param end cell succeeding of the current cell, if any
+     * Resolve collapsing borders for the given cell. Used in case of the collapsing border model.
+     * @param other neighbouring grid unit if any
+     * @param side the side to resolve (one of CommonBorderPaddingBackground.BEFORE|AFTER|START|END)
+     * @param resFlags flags for the border resolution
      */
-    public static void resolveBorder(Table table,
-            CommonBorderPaddingBackground target,
-            GridUnit current, GridUnit other, int side) {
-        if (current == null) {
-            return;
-        }
-        
+    public void resolveBorder(GridUnit other, int side, int resFlags) {
         CollapsingBorderModel borderModel = CollapsingBorderModel.getBorderModelFor(
-                table.getBorderCollapse());
-        target.setBorderInfo(
-                borderModel.determineWinner(current, other, 
-                        side, 0), side);
+                getTable().getBorderCollapse());
+        if (this.effBorders == null) {
+            this.effBorders = new CommonBorderPaddingBackground();
+        }
+        this.effBorders.setBorderInfo(
+                borderModel.determineWinner(this, other, 
+                        side, resFlags), side);
     }
     
-}
\ No newline at end of file
+    public boolean getFlag(int which) {
+        return (flags & (1 << which)) != 0;
+    }
+    
+    public void setFlag(int which, boolean value) {
+        if (value) {
+            flags |= (1 << which); //set flag
+        } else {
+            flags &= ~(1 << which); //clear flag
+        }
+    }
+    
+    /**
+     * @return the grid unit just below this grid unit if the cell is spanning.
+     */
+    public GridUnit createNextRowSpanningGridUnit() {
+        if (isLastGridUnitRowSpan()) {
+            return null;
+        } else {
+            //cloning the current GridUnit with adjustments
+            GridUnit gu = new GridUnit(getPrimary(), getColumn(), startCol, colSpanIndex);
+            gu.rowSpanIndex = rowSpanIndex + 1;
+            return gu;
+        }
+    }
+
+    /** @see java.lang.Object#toString() */
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        if (isEmpty()) {
+            sb.append("EMPTY");
+        } else if (isPrimary()) {
+            sb.append("Primary");
+        }
+        sb.append("GridUnit:");
+        if (colSpanIndex > 0) {
+            sb.append(" colSpan=").append(colSpanIndex);
+        }
+        if (rowSpanIndex > 0) {
+            sb.append(" rowSpan=").append(rowSpanIndex);
+        }
+        sb.append(" startCol=").append(startCol);
+        sb.append(" flags=").append(Integer.toBinaryString(flags));
+        return sb.toString();
+    }
+
+}
diff --git a/src/java/org/apache/fop/layoutmgr/table/PrimaryGridUnit.java b/src/java/org/apache/fop/layoutmgr/table/PrimaryGridUnit.java
new file mode 100644 (file)
index 0000000..2695890
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr.table;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.fop.fo.flow.TableCell;
+import org.apache.fop.fo.flow.TableColumn;
+import org.apache.fop.fo.properties.LengthRangeProperty;
+
+/**
+ * This class represents a primary grid unit of a spanned cell.
+ */
+public class PrimaryGridUnit extends GridUnit {
+
+    /** Cell layout manager. */
+    private Cell cellLM;
+    /** List of Knuth elements representing the contents of the cell. */
+    private LinkedList elements;
+    /** Index of row where this cell starts */
+    private int startRow;
+    /** Links to the spanned grid units. (List of GridUnit arrays, one array represents a row) */ 
+    private List rows;
+    /** The calculated size of the cell's content. (cached value) */
+    private int contentLength = -1;
+    
+    public PrimaryGridUnit(TableCell cell, TableColumn column, int startCol, int startRow) {
+        super(cell, column, startCol, 0);
+        this.startRow = startRow;
+        if (cell != null) {
+            cellLM = new Cell(cell, this);
+        }
+    }
+    
+    public Cell getCellLM() {
+        return cellLM;
+    }
+    
+    public boolean isPrimary() {
+        return true;
+    }
+    
+    public void setElements(LinkedList elements) {
+        this.elements = elements;
+    }
+    
+    public LinkedList getElements() {
+        return this.elements;
+    }
+    
+    /** 
+     * @return Returns the half the maximum before border width of this cell.
+     */
+    public int getHalfMaxBeforeBorderWidth() {
+        int value = 0;
+        if (getRows() != null) {
+            int before = 0;
+            //first row for before borders
+            GridUnit[] row = (GridUnit[])getRows().get(0);
+            for (int i = 0; i < row.length; i++) {
+                if (row[i].hasBorders()) {
+                    before = Math.max(before, 
+                            row[i].getBorders().getBorderBeforeWidth(false));
+                }
+            }
+            value += before / 2;
+        } else {
+            if (hasBorders()) {
+                value += getBorders().getBorderBeforeWidth(false) / 2;
+            }
+        }
+        return value;
+    }
+    
+    /** 
+     * @return Returns the half the maximum after border width of this cell.
+     */
+    public int getHalfMaxAfterBorderWidth() {
+        int value = 0;
+        if (getRows() != null) {
+            //Last row for after borders
+            int after = 0;
+            GridUnit[] row = (GridUnit[])getRows().get(getRows().size() - 1);
+            for (int i = 0; i < row.length; i++) {
+                if (row[i].hasBorders()) {
+                    after = Math.max(after, row[i].getBorders().getBorderAfterWidth(false));
+                }
+            }
+            value += after / 2;
+        } else {
+            if (hasBorders()) {
+                value += getBorders().getBorderAfterWidth(false) / 2;
+            }
+        }
+        return value;
+    }
+    
+    /** 
+     * @return Returns the sum of half the maximum before and after border 
+     * widths of this cell.
+     */
+    public int getHalfMaxBorderWidth() {
+        return getHalfMaxBeforeBorderWidth() + getHalfMaxAfterBorderWidth();
+    }
+    
+    /** @param value The length of the cell content to remember. */
+    public void setContentLength(int value) {
+        this.contentLength = value;
+    }
+    
+    /** @return Returns the length of the cell content. */
+    public int getContentLength() {
+        return contentLength;
+    }
+
+    /** 
+     * @return Returns the length of the cell content after the bpd/height attributes on cell
+     * and row have been taken into account.
+     */
+    public int getEffectiveContentLength() {
+        int value = getContentLength();
+        if (!getCell().getBlockProgressionDimension().getMinimum().isAuto()) {
+            value = Math.max(value, 
+                    getCell().getBlockProgressionDimension().getMinimum().getLength().getValue());
+        }
+        if (getRow() != null 
+                && !getRow().getBlockProgressionDimension().getMinimum().isAuto()) {
+            value = Math.max(value, 
+                    getRow().getBlockProgressionDimension().getMinimum().getLength().getValue());
+        }
+        return value;
+    }
+    
+    /** @return true if cell/row has an explicit BPD/height */
+    public boolean hasBPD() {
+        if (!getCell().getBlockProgressionDimension().getOptimum().isAuto()) {
+            return true;
+        }
+        if (getRow() != null 
+                && !getRow().getBlockProgressionDimension().getOptimum().isAuto()) {
+            return true;
+        }
+        return false;
+    }
+
+    public List getRows() {
+        return this.rows;
+    }
+    
+    public void addRow(GridUnit[] row) {
+        if (rows == null) {
+            rows = new java.util.ArrayList();
+        }
+        rows.add(row);
+    }
+    
+    public int getStartRow() {
+        return this.startRow;
+    }
+
+    public int[] getStartEndBorderWidths() {
+        int[] widths = new int[2];
+        if (rows == null) {
+            widths[0] = getBorders().getBorderStartWidth(false);
+            widths[1] = getBorders().getBorderEndWidth(false);
+        } else {
+            for (int i = 0; i < rows.size(); i++) {
+                GridUnit[] gridUnits = (GridUnit[])rows.get(i);
+                widths[0] = Math.max(widths[0], 
+                        (gridUnits[0]).
+                            getBorders().getBorderStartWidth(false));
+                widths[1] = Math.max(widths[1], 
+                        (gridUnits[gridUnits.length - 1]).
+                            getBorders().getBorderEndWidth(false));
+            }
+        }
+        return widths;
+    }
+    
+    /** @see java.lang.Object#toString() */
+    public String toString() {
+        StringBuffer sb = new StringBuffer(super.toString());
+        sb.append(" startRow=").append(startRow);
+        return sb.toString();
+    }
+
+    /** @return true if this cell spans over more than one grid unit. */
+    public boolean hasSpanning() {
+        return (getCell().getNumberColumnsSpanned() > 1) 
+            || (getCell().getNumberRowsSpanned() > 1);
+    }
+    
+}
diff --git a/src/java/org/apache/fop/layoutmgr/table/Row.java b/src/java/org/apache/fop/layoutmgr/table/Row.java
deleted file mode 100644 (file)
index c3d06d4..0000000
+++ /dev/null
@@ -1,623 +0,0 @@
-/*
- * Copyright 1999-2005 The Apache Software Foundation.
- * 
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * 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.table;
-
-import org.apache.fop.fo.FONode;
-import org.apache.fop.fo.flow.Table;
-import org.apache.fop.fo.flow.TableBody;
-import org.apache.fop.fo.flow.TableCell;
-import org.apache.fop.fo.flow.TableRow;
-import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
-import org.apache.fop.fo.properties.LengthRangeProperty;
-import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
-import org.apache.fop.layoutmgr.LayoutManager;
-import org.apache.fop.layoutmgr.LeafPosition;
-import org.apache.fop.layoutmgr.BreakPoss;
-import org.apache.fop.layoutmgr.LayoutContext;
-import org.apache.fop.layoutmgr.MinOptMaxUtil;
-import org.apache.fop.layoutmgr.PositionIterator;
-import org.apache.fop.layoutmgr.BreakPossPosIter;
-import org.apache.fop.layoutmgr.Position;
-import org.apache.fop.layoutmgr.TraitSetter;
-import org.apache.fop.area.Area;
-import org.apache.fop.area.Block;
-import org.apache.fop.area.Trait;
-import org.apache.fop.traits.MinOptMax;
-
-import java.util.Iterator;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.ListIterator;
-
-/**
- * LayoutManager for a table-row FO.
- * The row contains cells that are organised according to the columns.
- * A break in a table row will contain breaks for each table cell.
- * If there are row spanning cells then these cells belong to this row
- * but effect the occupied columns of future rows.
- */
-public class Row extends BlockStackingLayoutManager {
-    
-    private TableRow fobj;
-    
-    private List gridUnits = null;
-    private List columns = null;
-    private int referenceIPD;
-    private int rowHeight;
-    private int xoffset;
-    private int yoffset;
-
-    private class RowPosition extends LeafPosition {
-        protected List cellBreaks;
-        protected RowPosition(LayoutManager lm, int pos, List l) {
-            super(lm, pos);
-            cellBreaks = l;
-        }
-    }
-
-    /**
-     * Create a new row layout manager.
-     *
-     */
-    public Row(TableRow node) {
-        super(node);
-        fobj = node;
-    }
-
-    /** @return the table-row FO */
-    public TableRow getFObj() {
-        return this.fobj;
-    }
-    
-    /**
-     * @return the table owning this row
-     */
-    public Table getTable() {
-        FONode node = fobj.getParent();
-        while (!(node instanceof Table)) {
-            node = node.getParent();
-        }
-        return (Table)node;
-    }
-    
-    /**
-     * Set the columns from the table.
-     *
-     * @param cols the list of columns for this table
-     */
-    public void setColumns(List cols) {
-        columns = cols;
-    }
-
-    /** @return true if this is the layout manager for the first row in a body. */
-    public boolean isFirstInBody() {
-        return ((TableBody)getFObj().getParent()).isFirst(getFObj());
-    }
-    
-    /** @return true if this is the layout manager for the last row in a body. */
-    public boolean isLastInBody() {
-        return ((TableBody)getFObj().getParent()).isLast(getFObj());
-    }
-    
-    /**
-     * Gets the Column at a given index.
-     * @param index index of the column (index must be >= 1)
-     * @return the requested Column
-     */
-    private Column getColumn(int index) {
-        int size = columns.size();
-        if (index > size - 1) {
-            return (Column)columns.get(size - 1);
-        } else {
-            return (Column)columns.get(index - 1);
-        }
-    }
-    
-    private void prepareGridUnits() {
-        gridUnits = new java.util.ArrayList();
-        List availableCells = new java.util.ArrayList();
-        // add cells to list
-        while (childLMiter.hasNext()) {
-            curChildLM = (LayoutManager) childLMiter.next();
-            curChildLM.setParent(this);
-            curChildLM.initialize();
-            availableCells.add(curChildLM);
-        }
-        
-        //Transfer available cells to their slots
-        int colnum = 1;
-        ListIterator iter = availableCells.listIterator();
-        while (iter.hasNext()) {
-            Cell cellLM = (Cell)iter.next();
-            TableCell cell = cellLM.getFObj();
-            if (cell.hasColumnNumber()) {
-                colnum = cell.getColumnNumber();
-            }
-            while (colnum > gridUnits.size()) {
-                gridUnits.add(null);
-            }
-            if (gridUnits.get(colnum - 1) != null) {
-                log.error("Overlapping cell at position " + colnum);
-            }
-            //Add cell info for primary slot
-            GridUnit info = new GridUnit(cellLM);
-            info.row = this;
-            gridUnits.set(colnum - 1, info);
-            info.column = getColumn(colnum);
-            
-            //Add cell infos on spanned slots if any
-            for (int j = 1; j < cell.getNumberColumnsSpanned(); j++) {
-                colnum++;
-                GridUnit infoSpan = new GridUnit(cellLM, j);
-                infoSpan.row = this;
-                infoSpan.column = getColumn(colnum);
-                if (colnum > gridUnits.size()) {
-                    gridUnits.add(infoSpan);
-                } else {
-                    if (gridUnits.get(colnum - 1) != null) {
-                        log.error("Overlapping cell at position " + colnum);
-                        //TODO throw layout exception
-                    }
-                    gridUnits.set(colnum - 1, infoSpan);
-                }
-            }
-            colnum++;
-        }
-        
-        //Post-processing the list (looking for gaps and resolve start and end borders)
-        postProcessGridUnits();
-    }
-
-    private void postProcessGridUnits() {
-        for (int pos = 1; pos <= gridUnits.size(); pos++) {
-            GridUnit gu = (GridUnit)gridUnits.get(pos - 1);
-            
-            //Empty grid units
-            if (gu == null) {
-                //Add grid unit
-                gu = new GridUnit(null);
-                gu.row = this;
-                gu.column = getColumn(pos);
-                gridUnits.set(pos - 1, gu);
-            }
-        }
-            
-        //Border resolution now that the empty grid units are filled
-        for (int pos = 1; pos <= gridUnits.size(); pos++) {
-            GridUnit starting = (GridUnit)gridUnits.get(pos - 1);
-         
-            //Border resolution
-            if (getTable().isSeparateBorderModel()) {
-                starting.assignBorder(starting.layoutManager);
-            } else {
-                //Neighbouring grid unit at start edge 
-                GridUnit start = null;
-                int find = pos - 1;
-                while (find >= 1) {
-                    GridUnit candidate = (GridUnit)gridUnits.get(find - 1);
-                    if (candidate.isLastGridUnitColSpan()) {
-                        start = candidate;
-                        break;
-                    }
-                    find--;
-                }
-                
-                //Ending grid unit for current cell
-                GridUnit ending = null;
-                if (starting.layoutManager != null) {
-                    pos += starting.layoutManager.getFObj().getNumberColumnsSpanned() - 1;
-                }
-                ending = (GridUnit)gridUnits.get(pos - 1);
-                
-                //Neighbouring grid unit at end edge 
-                GridUnit end = null;
-                find = pos + 1;
-                while (find <= gridUnits.size()) {
-                    GridUnit candidate = (GridUnit)gridUnits.get(find - 1);
-                    if (candidate.isPrimaryGridUnit()) {
-                        end = candidate;
-                        break;
-                    }
-                    find++;
-                }
-                CommonBorderPaddingBackground borders = new CommonBorderPaddingBackground();
-                GridUnit.resolveBorder(getTable(), borders, starting, 
-                        (start != null ? start : null), 
-                        CommonBorderPaddingBackground.START);
-                starting.effBorders = borders;
-                if (starting != ending) {
-                    borders = new CommonBorderPaddingBackground();
-                }
-                GridUnit.resolveBorder(getTable(), borders, ending, 
-                        (end != null ? end : null), 
-                        CommonBorderPaddingBackground.END);
-                ending.effBorders = borders;
-                //Only start and end borders here, before and after during layout
-                //TODO resolve before and after borders during layout
-            }
-        }
-    }
-    
-    /**
-     * Get the cell info for a cell.
-     *
-     * @param pos the position of the cell (must be >= 1)
-     * @return the cell info object
-     */
-    protected GridUnit getCellInfo(int pos) {
-        if (gridUnits == null) {
-            prepareGridUnits();
-        }
-        if (pos <= gridUnits.size()) {
-            return (GridUnit)gridUnits.get(pos - 1);
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Get the next break possibility.
-     * A row needs to get the possible breaks for each cell
-     * in the row and find a suitable break across all cells.
-     *
-     * @param context the layout context for getting breaks
-     * @return the next break possibility
-     */
-    public BreakPoss getNextBreakPoss(LayoutContext context) {
-        //LayoutManager curLM; // currently active LM
-        GridUnit curGridUnit; //currently active grid unit
-
-        BreakPoss lastPos = null;
-        List breakList = new java.util.ArrayList();
-
-        int min = 0;
-        int opt = 0;
-        int max = 0;
-
-        // This is used for the displacement of the individual cells
-        int ipdOffset = 0;
-        
-        int startColumn = 1;
-        boolean over = false;
-
-        while ((curGridUnit = getCellInfo(startColumn)) != null) {
-            Cell cellLM = curGridUnit.layoutManager;
-            if (curGridUnit.isColSpan()) {
-                //skip spanned slots
-                startColumn++;
-                continue;
-            }
-            
-            List childBreaks = new ArrayList();
-            MinOptMax stackSize = new MinOptMax();
-
-            // Set up a LayoutContext
-            // the ipd is from the current column
-            referenceIPD = context.getRefIPD();
-            BreakPoss bp;
-
-            LayoutContext childLC = new LayoutContext(0);
-            childLC.setStackLimit(
-                  MinOptMax.subtract(context.getStackLimit(),
-                                     stackSize));
-
-            //Determine which columns this cell will occupy
-            List spannedGridUnits = new java.util.ArrayList();
-            getGridUnitsForCell(cellLM, startColumn, spannedGridUnits);
-            int childRefIPD = 0;
-            for (int i = 0; i < spannedGridUnits.size(); i++) {
-                Column col = ((GridUnit)spannedGridUnits.get(i)).column;
-                childRefIPD += col.getWidth().getValue();
-            }
-            childLC.setRefIPD(childRefIPD);
-
-            if (cellLM != null) {
-                cellLM.addGridUnitsFromRow(spannedGridUnits);
-                cellLM.setInRowIPDOffset(ipdOffset);
-                while (!cellLM.isFinished()) {
-                    if ((bp = cellLM.getNextBreakPoss(childLC)) != null) {
-                        if (stackSize.opt + bp.getStackingSize().opt > context.getStackLimit().max) {
-                            // reset to last break
-                            if (lastPos != null) {
-                                LayoutManager lm = lastPos.getLayoutManager();
-                                lm.resetPosition(lastPos.getPosition());
-                                if (lm != cellLM) {
-                                    cellLM.resetPosition(null);
-                                }
-                            } else {
-                                cellLM.resetPosition(null);
-                            }
-                            over = true;
-                            break;
-                        }
-                        stackSize.add(bp.getStackingSize());
-                        lastPos = bp;
-                        childBreaks.add(bp);
-
-                        if (bp.nextBreakOverflows()) {
-                            over = true;
-                            break;
-                        }
-
-                        childLC.setStackLimit(MinOptMax.subtract(
-                                                 context.getStackLimit(), stackSize));
-                    }
-                }
-                startColumn += cellLM.getFObj().getNumberColumnsSpanned();
-            } else {
-                //Skipping empty cells
-                //log.debug("empty cell at pos " + startColumn);
-                startColumn++;
-            }
-            
-            //Adjust in-row x offset for individual cells
-            ipdOffset += childRefIPD;
-            
-            
-            // the min is the maximum min of all cells
-            if (stackSize.min > min) {
-                min = stackSize.min;
-            }
-            // the optimum is the maximum of all optimums
-            if (stackSize.opt > opt) {
-                opt = stackSize.opt;
-            }
-            // the maximum is the largest maximum
-            if (stackSize.max > max) {
-                max = stackSize.max;
-            }
-
-            if (childBreaks.size() > 0) {
-                breakList.add(childBreaks);
-            }
-        }
-        MinOptMax rowSize = new MinOptMax(min, opt, max);
-        LengthRangeProperty specifiedBPD = fobj.getBlockProgressionDimension();
-        if (specifiedBPD.getEnum() != EN_AUTO) {
-            if ((specifiedBPD.getMaximum().getEnum() != EN_AUTO)
-                    && (specifiedBPD.getMaximum().getLength().getValue() < rowSize.min)) {
-                log.warn("maximum height of row is smaller than the minimum "
-                        + "height of its contents");
-            }
-            MinOptMaxUtil.restrict(rowSize, specifiedBPD);
-        }
-        rowHeight = rowSize.opt;
-
-        boolean fin = true;
-        startColumn = 1;
-        //Check if any of the cell LMs haven't finished, yet
-        while ((curGridUnit = getCellInfo(startColumn)) != null) {
-            Cell cellLM = curGridUnit.layoutManager;
-            if (cellLM == null) {
-                //skip empty cell
-                startColumn++;
-                continue;
-            }
-            if (!cellLM.isFinished()) {
-                fin = false;
-                break;
-            }
-            startColumn += cellLM.getFObj().getNumberColumnsSpanned();
-        }
-
-        setFinished(fin);
-        RowPosition rp = new RowPosition(this, breakList.size() - 1, breakList);
-        BreakPoss breakPoss = new BreakPoss(rp);
-        if (over) {
-            breakPoss.setFlag(BreakPoss.NEXT_OVERFLOWS, true);
-        }
-        breakPoss.setStackingSize(rowSize);
-        return breakPoss;
-    }
-
-    /**
-     * Determines the grid units that are spanned by the given cell.
-     * @param cellLM table-cell LM
-     * @param startCell starting cell index (must be >= 1)
-     * @param spannedGridUnits List to receive the applicable grid units
-     */
-    private void getGridUnitsForCell(Cell cellLM, int startCell, List spannedGridUnits) {
-        int count;
-        if (cellLM != null) {
-            count = cellLM.getFObj().getNumberColumnsSpanned();
-        } else {
-            count = 1;
-        }
-        spannedGridUnits.clear();
-        for (int i = 0; i < count; i++) {
-            spannedGridUnits.add(this.gridUnits.get(startCell + i - 1));
-        }
-    }
-
-    /**
-     * Reset the layoutmanager "iterator" so that it will start
-     * with the passed Position's generating LM
-     * on the next call to getChildLM.
-     * @param pos a Position returned by a child layout manager
-     * representing a potential break decision.
-     * If pos is null, then back up to the first child LM.
-     */
-    protected void reset(Position pos) {
-        //LayoutManager curLM; // currently active LM
-        GridUnit curGridUnit;
-        int cellIndex = 1;
-
-        if (pos == null) {
-            while ((curGridUnit = getCellInfo(cellIndex)) != null) {
-                if (curGridUnit.layoutManager != null) {
-                    curGridUnit.layoutManager.resetPosition(null);
-                }
-                cellIndex++;
-            }
-        } else {
-            RowPosition rpos = (RowPosition)pos;
-            List breaks = rpos.cellBreaks;
-
-            while ((curGridUnit = getCellInfo(cellIndex)) != null) {
-                if (curGridUnit.layoutManager != null) {
-                    List childbreaks = (List)breaks.get(cellIndex);
-                    curGridUnit.layoutManager.resetPosition(
-                            (Position)childbreaks.get(childbreaks.size() - 1));
-                }
-                cellIndex++;
-            }
-        }
-
-        setFinished(false);
-    }
-
-    /**
-     * Set the x position offset of this row.
-     * This is used to set the position of the areas returned by this row.
-     *
-     * @param off the x offset
-     */
-    public void setXOffset(int off) {
-        xoffset = off;
-    }
-    
-    /**
-     * Set the y position offset of this row.
-     * This is used to set the position of the areas returned by this row.
-     *
-     * @param off the y offset
-     */
-    public void setYOffset(int off) {
-        yoffset = off;
-    }
-
-    /**
-     * Add the areas for the break points.
-     * This sets the offset of each cell as it is added.
-     *
-     * @param parentIter the position iterator
-     * @param layoutContext the layout context for adding areas
-     */
-    public void addAreas(PositionIterator parentIter,
-                         LayoutContext layoutContext) {
-        getParentArea(null);
-        BreakPoss bp1 = (BreakPoss)parentIter.peekNext();
-        bBogus = !bp1.generatesAreas();
-        if (!isBogus()) {
-            addID(fobj.getId());
-        }
-
-        Cell childLM;
-        int iStartPos = 0;
-        LayoutContext lc = new LayoutContext(0);
-        while (parentIter.hasNext()) {
-            RowPosition lfp = (RowPosition) parentIter.next();
-            
-            //area exclusively for painting the row background
-            Block rowArea = getRowArea();
-            if (rowArea != null) {
-                rowArea.setBPD(rowHeight);
-                rowArea.setIPD(referenceIPD);
-                rowArea.setXOffset(xoffset);
-                rowArea.setYOffset(yoffset);
-                parentLM.addChildArea(rowArea);
-            }
-
-            for (Iterator iter = lfp.cellBreaks.iterator(); iter.hasNext();) {
-                List cellsbr = (List)iter.next();
-                BreakPossPosIter breakPosIter;
-                breakPosIter = new BreakPossPosIter(cellsbr, 0, cellsbr.size());
-                iStartPos = lfp.getLeafPos() + 1;
-
-                while ((childLM = (Cell)breakPosIter.getNextChildLM()) != null) {
-                    childLM.setXOffset(xoffset);
-                    childLM.setYOffset(yoffset);
-                    childLM.setRowHeight(rowHeight);
-                    childLM.addAreas(breakPosIter, lc);
-                }
-            }
-        }
-
-        flush();
-    }
-
-    /**
-     * Get the row height of the row after adjusting.
-     * Should only be called after adding the row areas.
-     *
-     * @return the row height of this row after adjustment
-     */
-    public int getRowHeight() {
-        return rowHeight;
-    }
-
-    /**
-     * Return an Area which can contain the passed childArea. The childArea
-     * may not yet have any content, but it has essential traits set.
-     * In general, if the LayoutManager already has an Area it simply returns
-     * it. Otherwise, it makes a new Area of the appropriate class.
-     * It gets a parent area for its area by calling its parent LM.
-     * Finally, based on the dimensions of the parent area, it initializes
-     * its own area. This includes setting the content IPD and the maximum
-     * BPD.
-     *
-     * @param childArea the child area
-     * @return the parent are for the child
-     */
-    public Area getParentArea(Area childArea) {
-        return parentLM.getParentArea(childArea);
-    }
-
-    /**
-     * Add the child.
-     * Rows return the areas returned by the child elements.
-     * This simply adds the area to the parent layout manager.
-     *
-     * @param childArea the child area
-     */
-    public void addChildArea(Area childArea) {
-        parentLM.addChildArea(childArea);
-    }
-
-    /**
-     * Reset the position of this layout manager.
-     *
-     * @param resetPos the position to reset to
-     */
-    public void resetPosition(Position resetPos) {
-        if (resetPos == null) {
-            reset(null);
-        }
-    }
-
-
-    /**
-     * Get the area for this row for background.
-     *
-     * @return the row area
-     */
-    public Block getRowArea() {
-        if (fobj.getCommonBorderPaddingBackground().hasBackground()) {
-            Block block = new Block();
-            block.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
-            block.setPositioning(Block.ABSOLUTE);
-            TraitSetter.addBackground(block, fobj.getCommonBorderPaddingBackground());
-            return block;
-        } else {
-            return null;
-        }
-    }
-
-}
-
index 638ee0681a09bb2709cd4933a330b04577b44d9c..639eaf3a2a8326891d577a2cb9c275d74c056b48 100644 (file)
@@ -139,7 +139,7 @@ public class TableAndCaptionLayoutManager extends BlockStackingLayoutManager {
     public void addAreas(PositionIterator parentIter,
                          LayoutContext layoutContext) {
         getParentArea(null);
-        addID(fobj.getId());
+        getPSLM().addIDToPage(fobj.getId());
 
         LayoutManager childLM;
         int iStartPos = 0;
diff --git a/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java
new file mode 100644 (file)
index 0000000..9958c2d
--- /dev/null
@@ -0,0 +1,863 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr.table;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fop.area.Block;
+import org.apache.fop.area.Trait;
+import org.apache.fop.fo.flow.Table;
+import org.apache.fop.fo.flow.TableRow;
+import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
+import org.apache.fop.fo.properties.LengthRangeProperty;
+import org.apache.fop.layoutmgr.ElementListUtils;
+import org.apache.fop.layoutmgr.KnuthBox;
+import org.apache.fop.layoutmgr.KnuthElement;
+import org.apache.fop.layoutmgr.KnuthPenalty;
+import org.apache.fop.layoutmgr.KnuthPossPosIter;
+import org.apache.fop.layoutmgr.LayoutContext;
+import org.apache.fop.layoutmgr.LayoutManager;
+import org.apache.fop.layoutmgr.MinOptMaxUtil;
+import org.apache.fop.layoutmgr.Position;
+import org.apache.fop.layoutmgr.PositionIterator;
+import org.apache.fop.layoutmgr.TraitSetter;
+import org.apache.fop.traits.MinOptMax;
+
+/**
+ * Layout manager for table contents, particularly managing the creation of combined element lists.
+ */
+public class TableContentLayoutManager {
+
+    /** Logger **/
+    private static Log log = LogFactory.getLog(TableContentLayoutManager.class);
+
+    private TableLayoutManager tableLM;
+    private TableRowIterator trIter;
+    private TableRowIterator headerIter;
+    private TableRowIterator footerIter;
+    private LinkedList headerList;
+    private LinkedList footerList;
+    private int headerNetHeight = 0;
+    private int footerNetHeight = 0;
+
+    private int startXOffset;
+    private int usedBPD;
+    
+    /**
+     * Main constructor
+     * @param parent Parent layout manager
+     */
+    public TableContentLayoutManager(TableLayoutManager parent) {
+        this.tableLM = parent;
+        Table table = getTableLM().getTable();
+        this.trIter = new TableRowIterator(table, getTableLM().getColumns(), TableRowIterator.BODY);
+        if (table.getTableHeader() != null) {
+            headerIter = new TableRowIterator(table, 
+                    getTableLM().getColumns(), TableRowIterator.HEADER);
+        }
+        if (table.getTableFooter() != null) {
+            footerIter = new TableRowIterator(table, 
+                    getTableLM().getColumns(), TableRowIterator.FOOTER);
+        }
+    }
+    
+    /**
+     * @return the table layout manager
+     */
+    public TableLayoutManager getTableLM() {
+        return this.tableLM;
+    }
+    
+    /** @return true if the table uses the separate border model. */
+    private boolean isSeparateBorderModel() {
+        return getTableLM().getTable().isSeparateBorderModel();
+    }
+
+    /**
+     * @return the column setup of this table
+     */
+    public ColumnSetup getColumns() {
+        return getTableLM().getColumns();
+    }
+    
+    /** @return the net header height */
+    protected int getHeaderNetHeight() {
+        return this.headerNetHeight;
+    }
+
+    /** @return the net footer height */
+    protected int getFooterNetHeight() {
+        return this.headerNetHeight;
+    }
+
+    /** @return the header element list */
+    protected LinkedList getHeaderElements() {
+        return this.headerList;
+    }
+
+    /** @return the footer element list */
+    protected LinkedList getFooterElements() {
+        return this.footerList;
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.LayoutManager#getNextKnuthElements(org.apache.fop.layoutmgr.LayoutContext, int)
+     */
+    public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+        log.debug("==> Columns: " + getTableLM().getColumns());
+        KnuthBox headerAsFirst = null;
+        KnuthBox headerAsSecondToLast = null;
+        KnuthBox footerAsLast = null;
+        if (headerIter != null) {
+            this.headerList = getKnuthElementsForRowIterator(
+                    headerIter, context, alignment, TableRowIterator.HEADER);
+            ElementListUtils.removeLegalBreaks(this.headerList);
+            this.headerNetHeight = ElementListUtils.calcContentLength(this.headerList);
+            if (log.isDebugEnabled()) {
+                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
+                headerAsFirst = box;
+                //returnList.add(0, box);
+            } else {
+                headerAsSecondToLast = box;
+                //returnList.add(box);
+            }
+        }
+        if (footerIter != null) {
+            this.footerList = getKnuthElementsForRowIterator(
+                    footerIter, context, alignment, TableRowIterator.FOOTER);
+            ElementListUtils.removeLegalBreaks(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;
+                //returnList.add(box);
+            }
+        }
+        LinkedList returnList = getKnuthElementsForRowIterator(
+                trIter, context, alignment, TableRowIterator.BODY);
+        if (headerAsFirst != null) {
+            returnList.add(0, headerAsFirst);
+        } else if (headerAsSecondToLast != null) {
+            returnList.add(headerAsSecondToLast);
+        }
+        if (footerAsLast != null) {
+            returnList.add(footerAsLast);
+        }
+        return returnList;
+    }
+    
+    /**
+     * Creates Knuth elements by iterating over a TableRowIterator.
+     * @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)
+     * @return An element list
+     */
+    private LinkedList getKnuthElementsForRowIterator(TableRowIterator iter, 
+            LayoutContext context, int alignment, int bodyType) {
+        LinkedList returnList = new LinkedList();
+        EffRow[] rowGroup = null;
+        while ((rowGroup = iter.getNextRowGroup()) != null) {
+            if (!isSeparateBorderModel()) {
+                resolveNormalBeforeAfterBordersForRowGroup(rowGroup, iter);
+            }
+            createElementsForRowGroup(context, alignment, bodyType, 
+                        returnList, rowGroup);
+        }
+        
+        //Remove last penalty
+        KnuthElement last = (KnuthElement)returnList.getLast();
+        if (last.isPenalty() && last.getW() == 0 && last.getP() == 0) {
+            returnList.removeLast();
+        }
+        return returnList;
+    }
+
+    /**
+     * Resolves normal borders for a row group.
+     * @param iter Table row iterator to operate on
+     */
+    private void resolveNormalBeforeAfterBordersForRowGroup(EffRow[] rowGroup, 
+            TableRowIterator iter) {
+        for (int rgi = 0; rgi < rowGroup.length; rgi++) {
+            EffRow row = rowGroup[rgi];
+            EffRow prev = iter.getCachedRow(row.getIndex() - 1);
+            EffRow next = iter.getCachedRow(row.getIndex() + 1);
+            if (next == null) {
+                //It wasn't read, yet, or we are at the last row
+                next = iter.getNextRow();
+                iter.backToPreviewRow();
+            }
+            if ((prev == null) && (iter == this.trIter) && (this.headerIter != null)) {
+                prev = this.headerIter.getLastRow();
+            }
+            if ((next == null) && (iter == this.headerIter)) {
+                next = this.trIter.getFirstRow();
+            }
+            if ((next == null) && (iter == this.trIter) && (this.footerIter != null)) {
+                next = this.footerIter.getFirstRow();
+            }
+            if ((prev == null) && (iter == this.footerIter)) {
+                //TODO This could be bad for memory consumption because it already causes the
+                //whole body iterator to be prefetched!
+                prev = this.trIter.getLastRow();
+            }
+            log.debug(prev + " - " + row + " - " + next);
+            
+            //Determine the grid units necessary for getting all the borders right
+            int guCount = row.getGridUnits().size();
+            if (prev != null) {
+                guCount = Math.max(guCount, prev.getGridUnits().size());
+            }
+            if (next != null) {
+                guCount = Math.max(guCount, next.getGridUnits().size());
+            }
+            GridUnit gu = row.getGridUnit(0);
+            //Create empty grid units to hold resolved borders of neighbouring cells
+            //TODO maybe this needs to be done differently (and sooner)
+            for (int i = 0; i < guCount - row.getGridUnits().size(); i++) {
+                //TODO This block in untested!
+                int pos = row.getGridUnits().size() + i;
+                row.getGridUnits().add(new EmptyGridUnit(gu.getRow(), 
+                        this.tableLM.getColumns().getColumn(pos + 1), gu.getBody(), 
+                        pos));
+            }
+            
+            //Now resolve normal borders
+            if (getTableLM().getTable().isSeparateBorderModel()) {
+                //nop, borders are already assigned at this point
+            } else {
+                for (int i = 0; i < row.getGridUnits().size(); i++) {
+                    gu = row.getGridUnit(i);
+                    GridUnit other;
+                    int flags = 0;
+                    if (prev != null && i < prev.getGridUnits().size()) {
+                        other = prev.getGridUnit(i);
+                    } else {
+                        other = null;
+                    }
+                    if (other == null 
+                            || other.isEmpty() 
+                            || gu.isEmpty() 
+                            || gu.getPrimary() != other.getPrimary()) {
+                        if ((iter == this.trIter)
+                                && gu.getFlag(GridUnit.FIRST_IN_TABLE)
+                                && (this.headerIter == null)) {
+                            flags |= CollapsingBorderModel.VERTICAL_START_END_OF_TABLE;
+                        }
+                        if ((iter == this.headerIter)
+                                && gu.getFlag(GridUnit.FIRST_IN_TABLE)) {
+                            flags |= CollapsingBorderModel.VERTICAL_START_END_OF_TABLE;
+                        }
+                        gu.resolveBorder(other, 
+                                CommonBorderPaddingBackground.BEFORE, flags);
+                    }
+                    
+                    flags = 0;
+                    if (next != null && i < next.getGridUnits().size()) {
+                        other = next.getGridUnit(i);
+                    } else {
+                        other = null;
+                    }
+                    if (other == null 
+                            || other.isEmpty() 
+                            || gu.isEmpty() 
+                            || gu.getPrimary() != other.getPrimary()) {
+                        if ((iter == this.trIter)
+                                && gu.getFlag(GridUnit.LAST_IN_TABLE)
+                                && (this.footerIter == null)) {
+                            flags |= CollapsingBorderModel.VERTICAL_START_END_OF_TABLE;
+                        }
+                        if ((iter == this.footerIter)
+                                && gu.getFlag(GridUnit.LAST_IN_TABLE)) {
+                            flags |= CollapsingBorderModel.VERTICAL_START_END_OF_TABLE;
+                        }
+                        gu.resolveBorder(other, 
+                                CommonBorderPaddingBackground.AFTER, flags);
+                    }
+                }
+
+            }
+        }
+    }
+
+    /**
+     * Creates Knuth elements for a row group (see TableRowIterator.getNextRowGroup()).
+     * @param context Active LayoutContext
+     * @param alignment alignment indicator
+     * @param bodyType Indicates what kind of body is being processed (BODY, HEADER or FOOTER)
+     * @param returnList List to received the generated elements
+     * @param rowGroup row group to process
+     */
+    private void createElementsForRowGroup(LayoutContext context, int alignment, 
+            int bodyType, LinkedList returnList, 
+            EffRow[] rowGroup) {
+        log.debug("Handling row group with " + rowGroup.length + " rows...");
+        MinOptMax[] rowHeights = new MinOptMax[rowGroup.length];
+        MinOptMax[] explicitRowHeights = new MinOptMax[rowGroup.length];
+        EffRow row;
+        int maxColumnCount = 0;
+        List pgus = new java.util.ArrayList(); //holds a list of a row's primary grid units
+        for (int rgi = 0; rgi < rowGroup.length; rgi++) {
+            row = rowGroup[rgi];
+            rowHeights[rgi] = new MinOptMax(0, 0, Integer.MAX_VALUE);
+            explicitRowHeights[rgi] = new MinOptMax(0, 0, Integer.MAX_VALUE);
+            
+            pgus.clear();
+            TableRow tableRow = null;
+            int minContentHeight = 0;
+            int maxCellHeight = 0;
+            for (int j = 0; j < row.getGridUnits().size(); j++) {
+                maxColumnCount = Math.max(maxColumnCount, row.getGridUnits().size());
+                GridUnit gu = row.getGridUnit(j);
+                if ((gu.isPrimary() || (gu.getColSpanIndex() == 0 && gu.isLastGridUnitRowSpan())) 
+                        && !gu.isEmpty()) {
+                    PrimaryGridUnit primary = gu.getPrimary();
+                    
+                    if (gu.isPrimary()) {
+                        primary.getCellLM().setParent(tableLM);
+                     
+                        //Determine the table-row if any
+                        if (tableRow == null && primary.getRow() != null) {
+                            tableRow = primary.getRow();
+                            
+                            //Check for bpd on row, see CSS21, 17.5.3 Table height algorithms
+                            LengthRangeProperty bpd = tableRow.getBlockProgressionDimension();
+                            if (!bpd.getMinimum().isAuto()) {
+                                minContentHeight = Math.max(minContentHeight, 
+                                        bpd.getMinimum().getLength().getValue());
+                            }
+                            MinOptMaxUtil.restrict(explicitRowHeights[rgi], bpd);
+                            
+                        }
+
+                        //Calculate width of cell
+                        int spanWidth = 0;
+                        for (int i = primary.getStartCol(); 
+                                i < primary.getStartCol() + primary.getCell().getNumberColumnsSpanned();
+                                i++) {
+                            spanWidth += getTableLM().getColumns().getColumn(i + 1)
+                                .getColumnWidth().getValue();
+                        }
+                        LayoutContext childLC = new LayoutContext(0);
+                        childLC.setStackLimit(context.getStackLimit()); //necessary?
+                        childLC.setRefIPD(spanWidth);
+                        
+                        //Get the element list for the cell contents
+                        LinkedList elems = primary.getCellLM().getNextKnuthElements(childLC, alignment);
+                        primary.setElements(elems);
+                        log.debug("Elements: " + elems);
+                    }
+
+                    
+                    //Calculate height of cell contents
+                    primary.setContentLength(ElementListUtils.calcContentLength(
+                            primary.getElements()));
+                    maxCellHeight = Math.max(maxCellHeight, primary.getContentLength());
+
+                    //Calculate height of row, see CSS21, 17.5.3 Table height algorithms
+                    if (gu.isLastGridUnitRowSpan()) {
+                        int effCellContentHeight = minContentHeight;
+                        LengthRangeProperty bpd = primary.getCell().getBlockProgressionDimension();
+                        if (!bpd.getMinimum().isAuto()) {
+                            effCellContentHeight = Math.max(effCellContentHeight,
+                                    bpd.getMinimum().getLength().getValue());
+                        }
+                        if (gu.getRowSpanIndex() == 0) {
+                            //TODO ATM only non-row-spanned cells are taken for this
+                            MinOptMaxUtil.restrict(explicitRowHeights[rgi], bpd);
+                        }
+                        effCellContentHeight = Math.max(effCellContentHeight, 
+                                primary.getContentLength());
+                        int borderWidths;
+                        if (isSeparateBorderModel()) {
+                            borderWidths = primary.getBorders().getBorderBeforeWidth(false)
+                                    + primary.getBorders().getBorderAfterWidth(false);
+                        } else {
+                            borderWidths = primary.getHalfMaxBorderWidth();
+                        }
+                        int padding = 0;
+                        CommonBorderPaddingBackground cbpb 
+                            = primary.getCell().getCommonBorderPaddingBackground(); 
+                        padding += cbpb.getPaddingBefore(false);
+                        padding += cbpb.getPaddingAfter(false);
+                        int effRowHeight = effCellContentHeight + padding + borderWidths;
+                        for (int previous = 0; previous < gu.getRowSpanIndex(); previous++) {
+                            effRowHeight -= rowHeights[rgi - previous - 1].opt;
+                        }
+                        if (effRowHeight > rowHeights[rgi].min) {
+                            //This is the new height of the (grid) row
+                            MinOptMaxUtil.extendMinimum(rowHeights[rgi], effRowHeight, false);
+                            row.setHeight(rowHeights[rgi]);
+                        }
+                    }
+                    
+                    if (gu.isPrimary()) {
+                        pgus.add(primary);
+                    }
+                }
+            }
+
+            row.setExplicitHeight(explicitRowHeights[rgi]);
+            if (row.getHeight().opt > row.getExplicitHeight().max) {
+                log.warn("Contents of row " + row.getIndex() + " violate a maximum constraint "
+                        + "in block-progression-dimension. Due to its contents the row grows "
+                        + "to " + row.getHeight().opt + " millipoints. The row constraint resolve "
+                        + "to " + row.getExplicitHeight());
+            }
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("rowGroup:");
+            for (int i = 0; i < rowHeights.length; i++) {
+                log.debug("  height=" + rowHeights[i] + " explicit=" + explicitRowHeights[i]);
+            }
+        }
+        TableStepper stepper = new TableStepper(this);
+        LinkedList returnedList = stepper.getCombinedKnuthElementsForRowGroup(
+                rowGroup, maxColumnCount, bodyType);
+        if (returnedList != null) {
+            returnList.addAll(returnedList);
+        }
+        
+    }
+
+    protected int getXOffsetOfGridUnit(GridUnit gu) {
+        int col = gu.getStartCol();
+        return startXOffset + getTableLM().getColumns().getXOffset(col + 1);
+    }
+    
+    public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) {
+        this.usedBPD = 0;
+        RowPainter painter = new RowPainter(layoutContext);
+
+        List positions = new java.util.ArrayList();
+        List footerElements = null;
+        Position lastPos = null;
+        while (parentIter.hasNext()) {
+            Position pos = (Position)parentIter.next();
+            lastPos = pos;
+            if (pos instanceof TableHeaderFooterPosition) {
+                TableHeaderFooterPosition thfpos = (TableHeaderFooterPosition)pos;
+                //these positions need to be unpacked
+                if (thfpos.header) {
+                    //header positions for the last part are the second-to-last element and need to
+                    //be handled first before all other TableContentPositions
+                    PositionIterator nestedIter = new KnuthPossPosIter(thfpos.nestedElements);
+                    while (nestedIter.hasNext()) {
+                        Position containedPos = (Position)nestedIter.next();
+                        if (containedPos instanceof TableContentPosition) {
+                            TableContentPosition tcpos = (TableContentPosition)containedPos;
+                            painter.handleTableContentPosition(tcpos);
+                        } else {
+                            log.debug("Ignoring position: " + containedPos);
+                        }
+                    }
+                    painter.addAreasAndFlushRow(true);
+                } else {
+                    //Positions for footers are simply added at the end
+                    footerElements = thfpos.nestedElements;
+                }
+            } else if (pos instanceof TableHFPenaltyPosition) {
+                //ignore for now, see special handling below if break is at a penalty
+                //Only if the last position in this part/page us such a position it will be used 
+            } else {
+                //leave order as is for the rest
+                positions.add(pos);
+            }
+        }
+        if (lastPos instanceof TableHFPenaltyPosition) {
+            TableHFPenaltyPosition penaltyPos = (TableHFPenaltyPosition)lastPos;
+            log.debug("Break at penalty!");
+            if (penaltyPos.headerElements != null) {
+                //Header positions for the penalty position are in the last element and need to
+                //be handled first before all other TableContentPositions
+                PositionIterator nestedIter = new KnuthPossPosIter(penaltyPos.headerElements);
+                while (nestedIter.hasNext()) {
+                    Position containedPos = (Position)nestedIter.next();
+                    if (containedPos instanceof TableContentPosition) {
+                        TableContentPosition tcpos = (TableContentPosition)containedPos;
+                        painter.handleTableContentPosition(tcpos);
+                    } else {
+                        log.debug("Ignoring position: " + containedPos);
+                    }
+                }
+                painter.addAreasAndFlushRow(true);
+            }
+            if (penaltyPos.footerElements != null) {
+                footerElements = penaltyPos.footerElements; 
+            }
+        }
+
+        
+        Iterator posIter = positions.iterator();
+        //Iterate over all steps
+        while (posIter.hasNext()) {
+            Position pos = (Position)posIter.next();
+            if (pos instanceof TableContentPosition) {
+                TableContentPosition tcpos = (TableContentPosition)pos;
+                painter.handleTableContentPosition(tcpos);
+            } else {
+                log.debug("Ignoring position: " + pos);
+            }
+        }
+        painter.addAreasAndFlushRow(true);
+
+        if (footerElements != null) {
+            //Positions for footers are simply added at the end
+            PositionIterator iter = new KnuthPossPosIter(footerElements);
+            while (iter.hasNext()) {
+                Position pos = (Position)iter.next();
+                if (pos instanceof TableContentPosition) {
+                    TableContentPosition tcpos = (TableContentPosition)pos;
+                    painter.handleTableContentPosition(tcpos);
+                } else {
+                    log.debug("Ignoring position: " + pos);
+                }
+            }
+            painter.addAreasAndFlushRow(true);
+        }
+        
+        painter.notifyEndOfSequence();
+        this.usedBPD += painter.getAccumulatedBPD();
+    }
+   
+    private class RowPainter {
+        
+        private TableRow rowFO = null;
+        private int colCount = getColumns().getColumnCount();
+        private int yoffset = 0;
+        private int accumulatedBPD = 0;
+        private EffRow lastRow = null;
+        private LayoutContext layoutContext;
+        private int lastRowHeight = 0;
+        private int[] firstRow = new int[3];
+        private Map[] rowOffsets = new Map[] {new java.util.HashMap(), 
+                new java.util.HashMap(), new java.util.HashMap()};
+
+        //These three variables are our buffer to recombine the individual steps into cells
+        private PrimaryGridUnit[] gridUnits = new PrimaryGridUnit[colCount];
+        private int[] start = new int[colCount];
+        private int[] end = new int[colCount];
+        private int[] partLength = new int[colCount];
+        
+        public RowPainter(LayoutContext layoutContext) {
+            this.layoutContext = layoutContext;
+            Arrays.fill(firstRow, -1);
+        }
+        
+        public int getAccumulatedBPD() {
+            return this.accumulatedBPD;
+        }
+        
+        public void notifyEndOfSequence() {
+            this.accumulatedBPD += lastRowHeight; //for last row
+        }
+        
+        public void handleTableContentPosition(TableContentPosition tcpos) {
+            log.debug("===handleTableContentPosition(" + tcpos);
+            rowFO = null;
+            if (lastRow != tcpos.row && lastRow != null) {
+                addAreasAndFlushRow(false);
+                yoffset += lastRowHeight;
+                this.accumulatedBPD += lastRowHeight;
+            }
+            lastRow = tcpos.row;
+            Iterator partIter = tcpos.gridUnitParts.iterator();
+            //Iterate over all grid units in the current step
+            while (partIter.hasNext()) {
+                GridUnitPart gup = (GridUnitPart)partIter.next();
+                log.debug(">" + gup);
+                int colIndex = gup.pgu.getStartCol();
+                if (gridUnits[colIndex] != gup.pgu) {
+                    gridUnits[colIndex] = gup.pgu;
+                    start[colIndex] = gup.start;
+                    end[colIndex] = gup.end;
+                } else {
+                    if (gup.end < end[colIndex]) {
+                        throw new IllegalStateException("Internal Error: stepper problem");
+                    }
+                    end[colIndex] = gup.end;
+                }
+                if (rowFO == null) {
+                    //Find the row if any
+                    rowFO = gridUnits[colIndex].getRow();
+                }
+            }
+        }
+        
+        public int addAreasAndFlushRow(boolean forcedFlush) {
+            int actualRowHeight = 0;
+            int readyCount = 0;
+            
+            int bt = lastRow.getBodyType();
+            rowOffsets[bt].put(new Integer(lastRow.getIndex()), new Integer(yoffset));
+
+            for (int i = 0; i < gridUnits.length; i++) {
+                if ((gridUnits[i] != null) 
+                        && (forcedFlush || (end[i] == gridUnits[i].getElements().size() - 1))) {
+                    log.debug("getting len for " + i + " " 
+                            + start[i] + "-" + end[i]);
+                    readyCount++;
+                    int len = ElementListUtils.calcContentLength(
+                            gridUnits[i].getElements(), start[i], end[i]);
+                    partLength[i] = len;
+                    log.debug("len of part: " + len);
+                    if (start[i] == 0 && lastRow.getExplicitHeight().min > 0) {
+                        len = Math.max(len, lastRow.getExplicitHeight().opt);
+                    }
+                    
+                    //Now add the borders to the contentLength
+                    if (isSeparateBorderModel()) {
+                        len += gridUnits[i].getBorders().getBorderBeforeWidth(false);
+                        len += gridUnits[i].getBorders().getBorderAfterWidth(false);
+                    }
+                    int startRow = Math.max(gridUnits[i].getStartRow(), firstRow[bt]);
+                    Integer storedOffset = (Integer)rowOffsets[bt].get(new Integer(startRow));
+                    int effYOffset;
+                    if (storedOffset != null) {
+                        effYOffset = storedOffset.intValue();
+                    } else {
+                        effYOffset = yoffset;
+                    }
+                    len -= yoffset - effYOffset;
+                    actualRowHeight = Math.max(actualRowHeight, len);
+                }
+            }
+            if (readyCount == 0) {
+                return 0;
+            }
+            lastRowHeight = actualRowHeight;
+            
+            //Add areas for row
+            addRowBackgroundArea(rowFO, actualRowHeight, layoutContext.getRefIPD(), yoffset);
+            for (int i = 0; i < gridUnits.length; i++) {
+                GridUnit currentGU = lastRow.safelyGetGridUnit(i);
+                if ((gridUnits[i] != null) 
+                        && (forcedFlush || (end[i] == gridUnits[i].getElements().size() - 1))
+                        && (currentGU == null || currentGU.isLastGridUnitRowSpan())) {
+                    //the last line in the "if" above is to avoid a premature end of an 
+                    //row-spanned cell because no GridUnitParts are generated after a cell is
+                    //finished with its content. currentGU can be null if there's no grid unit
+                    //at this place in the current row (empty cell and no borders to process)
+                    if (log.isDebugEnabled()) {
+                        log.debug((forcedFlush ? "FORCED " : "") + "flushing..." + i + " " 
+                                + start[i] + "-" + end[i]);
+                    }
+                    addAreasForCell(gridUnits[i], start[i], end[i], 
+                            layoutContext, lastRow, yoffset, 
+                            partLength[i], actualRowHeight);
+                    gridUnits[i] = null;
+                    start[i] = 0;
+                    end[i] = 0;
+                    partLength[i] = 0;
+                }
+            }
+            return actualRowHeight;
+        }
+
+        private void addAreasForCell(PrimaryGridUnit pgu, int start, int end, 
+                LayoutContext layoutContext, EffRow row, 
+                int yoffset, int contentHeight, int rowHeight) {
+            int bt = row.getBodyType();
+            if (firstRow[bt] < 0) {
+                firstRow[bt] = row.getIndex();
+            }
+            //Determine the first row in this sequence
+            //TODO Maybe optimize since addAreasAndFlushRow uses almost the same code
+            int startRow = Math.max(pgu.getStartRow(), firstRow[bt]);
+            int effYOffset = ((Integer)rowOffsets[bt].get(new Integer(startRow))).intValue();
+            int effCellHeight = rowHeight;
+            effCellHeight += yoffset - effYOffset;
+            log.debug("current row: " + row.getIndex());
+            log.debug("start row: " + pgu.getStartRow() + " " + yoffset + " " + effYOffset);
+            log.debug("contentHeight: " + contentHeight + " rowHeight=" + rowHeight 
+                    + " effCellHeight=" + effCellHeight);
+            Cell cellLM = pgu.getCellLM();
+            cellLM.setXOffset(getXOffsetOfGridUnit(pgu));
+            cellLM.setYOffset(effYOffset);
+            cellLM.setContentHeight(contentHeight);
+            cellLM.setRowHeight(effCellHeight);
+            //cellLM.setRowHeight(row.getHeight().opt);
+            cellLM.addAreas(new KnuthPossPosIter(pgu.getElements(), 
+                    start, end + 1), layoutContext);
+        }
+        
+    }
+
+    /**
+     * Get the area for a row for background.
+     * @param row the table-row object or null
+     * @return the row area or null if there's no background to paint
+     */
+    public Block getRowArea(TableRow row) {
+        if (row == null || !row.getCommonBorderPaddingBackground().hasBackground()) {
+            return null;
+        } else {
+            Block block = new Block();
+            block.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE);
+            block.setPositioning(Block.ABSOLUTE);
+            TraitSetter.addBackground(block, row.getCommonBorderPaddingBackground());
+            return block;
+        }
+    }
+
+    public void addRowBackgroundArea(TableRow row, int bpd, int ipd, int yoffset) {
+        //Add row background if any
+        Block rowBackground = getRowArea(row);
+        if (rowBackground != null) {
+            rowBackground.setBPD(bpd);
+            rowBackground.setIPD(ipd);
+            rowBackground.setXOffset(this.startXOffset);
+            rowBackground.setYOffset(yoffset);
+            getTableLM().addChildArea(rowBackground);
+        }
+    }
+    
+    
+    /**
+     * Sets the overall starting x-offset. Used for proper placement of cells.
+     * @param startXOffset starting x-offset (table's start-indent)
+     */
+    public void setStartXOffset(int startXOffset) {
+        this.startXOffset = startXOffset;
+    }
+
+    public int getUsedBPD() {
+        return this.usedBPD;
+    }
+    
+    protected static class GridUnitPart {
+        
+        protected PrimaryGridUnit pgu;
+        protected int start;
+        protected int end;
+        
+        protected GridUnitPart(PrimaryGridUnit pgu, int start, int end) {
+            this.pgu = pgu;
+            this.start = start;
+            this.end = end;
+        }
+        
+        /** @see java.lang.Object#toString() */
+        public String toString() {
+            StringBuffer sb = new StringBuffer("Part: ");
+            sb.append(start).append("-").append(end);
+            sb.append(" ").append(pgu);
+            return sb.toString();
+        }
+        
+    }
+    
+    public static class TableContentPosition extends Position {
+
+        protected List gridUnitParts;
+        protected EffRow row;
+        
+        protected TableContentPosition(LayoutManager lm, List gridUnitParts, 
+                EffRow row) {
+            super(lm);
+            this.gridUnitParts = gridUnitParts;
+            this.row = row;
+        }
+        
+        /** @see java.lang.Object#toString() */
+        public String toString() {
+            StringBuffer sb = new StringBuffer("TableContentPosition {");
+            sb.append(gridUnitParts);
+            sb.append("}");
+            return sb.toString();
+        }
+    }
+    
+    public static class TableHeaderFooterPosition extends Position {
+        
+        protected boolean header;
+        protected List nestedElements;
+        
+        protected TableHeaderFooterPosition(LayoutManager lm, 
+                boolean header, List nestedElements) {
+            super(lm);
+            this.header = header;
+            this.nestedElements = nestedElements;
+        }
+        
+        /** @see java.lang.Object#toString() */
+        public String toString() {
+            StringBuffer sb = new StringBuffer("Table");
+            sb.append(header ? "Header" : "Footer");
+            sb.append("Position {");
+            sb.append(nestedElements);
+            sb.append("}");
+            return sb.toString();
+        }
+    }
+
+    public static class TableHFPenaltyPosition extends Position {
+        
+        protected List headerElements;
+        protected List footerElements;
+        
+        protected TableHFPenaltyPosition(LayoutManager lm) {
+            super(lm);
+        }
+        
+        /** @see java.lang.Object#toString() */
+        public String toString() {
+            StringBuffer sb = new StringBuffer("TableHFPenaltyPosition");
+            sb.append(" {");
+            sb.append("header:");
+            sb.append(headerElements);
+            sb.append(", footer:");
+            sb.append(footerElements);
+            sb.append("}");
+            return sb.toString();
+        }
+    }
+    
+    private class KnuthBoxCellWithBPD extends KnuthBox {
+        
+        private PrimaryGridUnit pgu;
+        
+        public KnuthBoxCellWithBPD(int w, PrimaryGridUnit pgu) {
+            super(w, null, true);
+            this.pgu = pgu;
+        }
+    }
+
+}
index 653c953c258a8d1909a67067925f7b82a8b889ef..317a68447bf1e5ab08ef70b515e0325ff33efacc 100644 (file)
@@ -21,14 +21,17 @@ package org.apache.fop.layoutmgr.table;
 import org.apache.fop.datatypes.Length;
 import org.apache.fop.datatypes.PercentBase;
 import org.apache.fop.fo.flow.Table;
+import org.apache.fop.fo.flow.TableColumn;
 import org.apache.fop.fo.properties.TableColLength;
+import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
 import org.apache.fop.layoutmgr.BlockStackingLayoutManager;
+import org.apache.fop.layoutmgr.KnuthElement;
+import org.apache.fop.layoutmgr.KnuthGlue;
+import org.apache.fop.layoutmgr.KnuthPenalty;
 import org.apache.fop.layoutmgr.LayoutManager;
 import org.apache.fop.layoutmgr.LeafPosition;
-import org.apache.fop.layoutmgr.BreakPoss;
 import org.apache.fop.layoutmgr.LayoutContext;
 import org.apache.fop.layoutmgr.PositionIterator;
-import org.apache.fop.layoutmgr.BreakPossPosIter;
 import org.apache.fop.layoutmgr.Position;
 import org.apache.fop.layoutmgr.TraitSetter;
 import org.apache.fop.area.Area;
@@ -37,28 +40,26 @@ import org.apache.fop.traits.MinOptMax;
 import org.apache.fop.traits.SpaceVal;
 
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 
 /**
  * LayoutManager for a table FO.
- * A table consists of columns, table header, table footer and multiple
+ * A table consists of oldColumns, table header, table footer and multiple
  * table bodies.
  * The header, footer and body add the areas created from the table cells.
- * The table then creates areas for the columns, bodies and rows
+ * The table then creates areas for the oldColumns, bodies and rows
  * the render background.
  */
-public class TableLayoutManager extends BlockStackingLayoutManager {
+public class TableLayoutManager extends BlockStackingLayoutManager 
+                implements BlockLevelLayoutManager {
     private Table fobj;
     
-    private List columns = null;
+    private TableContentLayoutManager contentLM; 
+    private ColumnSetup columns = null;
 
     private Block curBlockArea;
 
-    private List bodyBreaks = new java.util.ArrayList();
-    private BreakPoss headerBreak;
-    private BreakPoss footerBreak;
-    private boolean firstRowHandled = false;
-    
     private int referenceIPD;
     private boolean autoLayout = true;
 
@@ -82,6 +83,7 @@ public class TableLayoutManager extends BlockStackingLayoutManager {
     public TableLayoutManager(Table node) {
         super(node);
         fobj = node;
+        this.columns = new ColumnSetup(node);
     }
 
     /** @return the table FO */
@@ -90,14 +92,12 @@ public class TableLayoutManager extends BlockStackingLayoutManager {
     }
     
     /**
-     * Set the columns for this table.
-     *
-     * @param cols the list of column layout managers
+     * @return the column setup for this table.
      */
-    public void setColumns(List cols) {
-        columns = cols;
+    public ColumnSetup getColumns() {
+        return this.columns;
     }
-
+    
     /** @see org.apache.fop.layoutmgr.AbstractLayoutManager#initProperties() */
     protected void initProperties() {
         super.initProperties();
@@ -118,15 +118,11 @@ public class TableLayoutManager extends BlockStackingLayoutManager {
     }
     
     /**
-     * Get the next break possibility.
-     * The break possibility depends on the height of the header and footer
-     * and possible breaks inside the table body.
-     *
-     * @param context the layout context for finding breaks
-     * @return the next break possibility
+     * @see org.apache.fop.layoutmgr.LayoutManager#getNextKnuthElements(org.apache.fop.layoutmgr.LayoutContext, int)
      */
-    public BreakPoss getNextBreakPoss(LayoutContext context) {
-        Body curLM; // currently active LM
+    public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
+        
+        //Body curLM; // currently active LM
 
         referenceIPD = context.getRefIPD();
         if (fobj.getInlineProgressionDimension().getOptimum().getEnum() != EN_AUTO) {
@@ -146,200 +142,122 @@ public class TableLayoutManager extends BlockStackingLayoutManager {
             stackSize.add(spaceBefore);
         }
 
-        BreakPoss lastPos = null;
-
         fobj.setLayoutDimension(PercentBase.BLOCK_IPD, referenceIPD);
         fobj.setLayoutDimension(PercentBase.BLOCK_BPD, context.getStackLimit().opt);
         fobj.setLayoutDimension(PercentBase.REFERENCE_AREA_IPD, referenceIPD);
         fobj.setLayoutDimension(PercentBase.REFERENCE_AREA_BPD, context.getStackLimit().opt);
 
-        if (columns == null) {
-            createColumnsFromFirstRow();
-        }
-        
         // either works out table of column widths or if proportional-column-width function
         // is used works out total factor, so that value of single unit can be computed.
         int sumCols = 0;
         float factors = 0;
-        if (columns != null) {
-            for (Iterator i = columns.iterator(); i.hasNext(); ) {
-                Column column = (Column) i.next();
-                Length width = column.getWidth();
-                sumCols += width.getValue();
-                if (width instanceof TableColLength) {
-                    factors += ((TableColLength) width).getTableUnits();
-                }
+        for (Iterator i = columns.iterator(); i.hasNext(); ) {
+            TableColumn column = (TableColumn) i.next();
+            Length width = column.getColumnWidth();
+            sumCols += width.getValue();
+            if (width instanceof TableColLength) {
+                factors += ((TableColLength) width).getTableUnits();
             }
         }
-        // sets TABLE_UNITS in case where one or more columns is defined using proportional-column-width
+        // sets TABLE_UNITS in case where one or more oldColumns is defined using 
+        // proportional-column-width
         if (sumCols < contentIPD) {
             if (fobj.getLayoutDimension(PercentBase.TABLE_UNITS).floatValue() == 0.0) {
                 fobj.setLayoutDimension(PercentBase.TABLE_UNITS,
                                       (contentIPD - sumCols) / factors);
             }
         }
-        
-        boolean headerFooterBuilt = false;
-
-        while ((curLM = (Body)getChildLM()) != null) {
-            if (!headerFooterBuilt) {
-                //Calculate the headers and footers only when needed
-                MinOptMax headerSize = null;
-                if (getTable().getTableHeader() != null) {
-                    if (!getTable().omitHeaderAtBreak() || !firstRowHandled) {
-                        Body tableHeader = new Body(getTable().getTableHeader());
-                        tableHeader.setParent(this);
-                        headerBreak = getHeight(tableHeader, context);
-                        headerSize = headerBreak.getStackingSize();
-                        stackSize.add(headerSize);
-                    }
-                }
 
-                //TODO Implement table-omit-footer-at-break once the page breaking
-                //is improved, so we don't have to do this twice
-                MinOptMax footerSize = null;
-                if (getTable().getTableFooter() != null) {
-                    Body tableFooter = new Body(getTable().getTableFooter());
-                    tableFooter.setParent(this);
-                    footerBreak = getHeight(tableFooter, context);
-                    footerSize = footerBreak.getStackingSize();
-                    stackSize.add(footerSize);
-                }
+        LinkedList returnedList = null;
+        LinkedList contentList = new LinkedList();
+        LinkedList returnList = new LinkedList();
+        //Position returnPosition = new NonLeafPosition(this, null);
+        //Body prevLM = null;
 
-                if (stackSize.opt > context.getStackLimit().max) {
-                    BreakPoss breakPoss = new BreakPoss(
-                                            new LeafPosition(this, 0));
-                    breakPoss.setFlag(BreakPoss.NEXT_OVERFLOWS, true);
-                    breakPoss.setStackingSize(stackSize);
-                    return breakPoss;
+        LayoutContext childLC = new LayoutContext(0);
+        childLC.setStackLimit(
+              MinOptMax.subtract(context.getStackLimit(),
+                                 stackSize));
+        childLC.setRefIPD(context.getRefIPD());
+
+        contentLM = new TableContentLayoutManager(this);
+        returnedList = contentLM.getNextKnuthElements(childLC, alignment);
+        log.debug(returnedList);
+        
+            if (returnedList.size() == 1
+                    && ((KnuthElement) returnedList.getFirst()).isPenalty()
+                    && ((KnuthPenalty) returnedList.getFirst()).getP() == -KnuthElement.INFINITE) {
+                // a descendant of this block has break-before
+                if (returnList.size() == 0) {
+                    // the first child (or its first child ...) has
+                    // break-before;
+                    // all this block, including space before, will be put in
+                    // the
+                    // following page
+                    //FIX ME
+                    //bSpaceBeforeServed = false;
                 }
-                headerFooterBuilt = true;
-            }
-            
-            // Make break positions
-            // Set up a LayoutContext
-            int ipd = context.getRefIPD();
-            BreakPoss bp;
-
-            LayoutContext childLC = new LayoutContext(0);
-            childLC.setStackLimit(
-                  MinOptMax.subtract(context.getStackLimit(),
-                                     stackSize));
-            childLC.setRefIPD(ipd);
-
-            curLM.setColumns(columns);
-
-            boolean over = false;
-            while (!curLM.isFinished()) {
-                if ((bp = curLM.getNextBreakPoss(childLC)) != null) {
-                    if (stackSize.opt + bp.getStackingSize().opt > context.getStackLimit().max) {
-                        // reset to last break
-                        if (lastPos != null) {
-                            LayoutManager lm = lastPos.getLayoutManager();
-                            lm.resetPosition(lastPos.getPosition());
-                            if (lm != curLM) {
-                                curLM.resetPosition(null);
-                            }
-                        } else {
-                            curLM.resetPosition(null);
-                        }
-                        over = true;
-                        break;
+                contentList.addAll(returnedList);
+
+                // "wrap" the Position inside each element
+                // moving the elements from contentList to returnList
+                returnedList = new LinkedList();
+                wrapPositionElements(contentList, returnList);
+
+                return returnList;
+            } else {
+                /*
+                if (prevLM != null) {
+                    // there is a block handled by prevLM
+                    // before the one handled by curLM
+                    if (mustKeepTogether() 
+                            || prevLM.mustKeepWithNext()
+                            || curLM.mustKeepWithPrevious()) {
+                        // add an infinite penalty to forbid a break between
+                        // blocks
+                        contentList.add(new KnuthPenalty(0,
+                                KnuthElement.INFINITE, false,
+                                new Position(this), false));
+                    } else if (!((KnuthElement) contentList.getLast()).isGlue()) {
+                        // add a null penalty to allow a break between blocks
+                        contentList.add(new KnuthPenalty(0, 0, false,
+                                new Position(this), false));
+                    } else {
+                        // the last element in contentList is a glue;
+                        // it is a feasible breakpoint, there is no need to add
+                        // a penalty
                     }
-                    stackSize.add(bp.getStackingSize());
-                    lastPos = bp;
-                    bodyBreaks.add(bp);
-                    firstRowHandled = true;
-
-                    if (bp.nextBreakOverflows()) {
-                        over = true;
-                        break;
+                }*/
+                contentList.addAll(returnedList);
+                /*
+                if (returnedList.size() == 0) {
+                    //Avoid NoSuchElementException below (happens with empty blocks)
+                    continue;
+                }*/
+                if (((KnuthElement) returnedList.getLast()).isPenalty()
+                        && ((KnuthPenalty) returnedList.getLast()).getP() == -KnuthElement.INFINITE) {
+                    // a descendant of this block has break-after
+                    if (false /*curLM.isFinished()*/) {
+                        // there is no other content in this block;
+                        // it's useless to add space after before a page break
+                        setFinished(true);
                     }
 
-                    childLC.setStackLimit(MinOptMax.subtract(
-                                             context.getStackLimit(), stackSize));
+                    returnedList = new LinkedList();
+                    wrapPositionElements(contentList, returnList);
+
+                    return returnList;
                 }
-            }
-            BreakPoss breakPoss = new BreakPoss(
-                                    new LeafPosition(this, bodyBreaks.size() - 1));
-            if (over) {
-                breakPoss.setFlag(BreakPoss.NEXT_OVERFLOWS, true);
-            }
-            breakPoss.setStackingSize(stackSize);
-            return breakPoss;
+            
         }
+        wrapPositionElements(contentList, returnList);
         setFinished(true);
-        return null;
-    }
-
-    private void createColumnsFromFirstRow() {
-        this.columns = new java.util.ArrayList();
-        //TODO Create columns from first row here 
-        //--> rule 2 in "fixed table layout", see CSS2, 17.5.2
-        //Alternative: extend columns on-the-fly, but in this case we need the
-        //new property evaluation context so proportional-column-width() works
-        //correctly.
-        if (columns.size() == 0) {
-            Column col = new Column(getTable().getDefaultColumn());
-            col.setParent(this);
-            this.columns.add(col);
-        }
-    }
-    
-    /**
-     * @param column the column to check
-     * @return true if the column is the first column
-     */
-    public boolean isFirst(Column column) {
-        return (this.columns.size() == 0 || this.columns.get(0) == column);
-    }
-    
-    /**
-     * @param column the column to check
-     * @return true if the column is the last column
-     */
-    public boolean isLast(Column column) {
-        return (this.columns.size() == 0 || this.columns.get(columns.size() - 1) == column);
+        return returnList;
     }
     
-    /**
-     * Get the break possibility and height of the table header or footer.
-     *
-     * @param lm the header or footer layout manager
-     * @param context the parent layout context
-     * @return the break possibility containing the stacking size
-     */
-    protected BreakPoss getHeight(Body lm, LayoutContext context) {
-        int referenceIPD = context.getRefIPD();
-        int contentIPD = referenceIPD - getIPIndents();
-        BreakPoss bp;
-
-        MinOptMax stackSize = new MinOptMax();
-
-        LayoutContext childLC = new LayoutContext(0);
-        childLC.setStackLimit(context.getStackLimit());
-        childLC.setRefIPD(contentIPD);
-
-        lm.setColumns(columns);
-
-        List breaks = new java.util.ArrayList();
-        while (!lm.isFinished()) {
-            if ((bp = lm.getNextBreakPoss(childLC)) != null) {
-                stackSize.add(bp.getStackingSize());
-                breaks.add(bp);
-                childLC.setStackLimit(MinOptMax.subtract(
-                                         context.getStackLimit(), stackSize));
-            }
-        }
-        BreakPoss breakPoss = new BreakPoss(
-                               new SectionPosition(this, breaks.size() - 1, breaks));
-        breakPoss.setStackingSize(stackSize);
-        return breakPoss;
-    }
-
     /**
      * The table area is a reference area that contains areas for
-     * columns, bodies, rows and the contents are in cells.
+     * oldColumns, bodies, rows and the contents are in cells.
      *
      * @param parentIter the position iterator
      * @param layoutContext the layout context for adding areas
@@ -347,7 +265,7 @@ public class TableLayoutManager extends BlockStackingLayoutManager {
     public void addAreas(PositionIterator parentIter,
                          LayoutContext layoutContext) {
         getParentArea(null);
-        addID(fobj.getId());
+        getPSLM().addIDToPage(fobj.getId());
 
         // if adjusted space before
         double adjust = layoutContext.getSpaceAdjust();
@@ -359,49 +277,14 @@ public class TableLayoutManager extends BlockStackingLayoutManager {
         // add column, body then row areas
 
         int tableHeight = 0;
-        Body childLM;
+        //Body childLM;
         LayoutContext lc = new LayoutContext(0);
 
-        // add table header areas
-        if (headerBreak != null) {
-            SectionPosition pos = (SectionPosition)headerBreak.getPosition();
-            List list = pos.list;
-            PositionIterator breakPosIter = new BreakPossPosIter(list, 0, list.size() + 1);
-            while ((childLM = (Body)breakPosIter.getNextChildLM()) != null) {
-                childLM.setXOffset(startXOffset);
-                childLM.addAreas(breakPosIter, lc);
-                tableHeight += childLM.getBodyHeight();
-            }
-        }
-
-        int iStartPos = 0;
-        while (parentIter.hasNext()) {
-            LeafPosition lfp = (LeafPosition) parentIter.next();
-            // Add the block areas to Area
-            PositionIterator breakPosIter =
-              new BreakPossPosIter(bodyBreaks, iStartPos,
-                                   lfp.getLeafPos() + 1);
-            iStartPos = lfp.getLeafPos() + 1;
-            while ((childLM = (Body)breakPosIter.getNextChildLM()) != null) {
-                childLM.setXOffset(startXOffset);
-                childLM.setYOffset(tableHeight);
-                childLM.addAreas(breakPosIter, lc);
-                tableHeight += childLM.getBodyHeight();
-            }
-        }
 
-        // add footer areas
-        if (footerBreak != null) {
-            SectionPosition pos = (SectionPosition)footerBreak.getPosition();
-            List list = pos.list;
-            PositionIterator breakPosIter = new BreakPossPosIter(list, 0, list.size() + 1);
-            while ((childLM = (Body)breakPosIter.getNextChildLM()) != null) {
-                childLM.setXOffset(startXOffset);
-                childLM.setYOffset(tableHeight);
-                childLM.addAreas(breakPosIter, lc);
-                tableHeight += childLM.getBodyHeight();
-            }
-        }
+        lc.setRefIPD(referenceIPD - getIPIndents());
+        contentLM.setStartXOffset(startXOffset);
+        contentLM.addAreas(parentIter, lc);
+        tableHeight += contentLM.getUsedBPD();
 
         curBlockArea.setBPD(tableHeight);
 
@@ -420,7 +303,7 @@ public class TableLayoutManager extends BlockStackingLayoutManager {
         // if adjusted space after
         addBlockSpacing(adjust, spaceAfter);
 
-        bodyBreaks.clear();
+        //bodyBreaks.clear();
         curBlockArea = null;
     }
 
@@ -474,5 +357,47 @@ public class TableLayoutManager extends BlockStackingLayoutManager {
         }
     }
 
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#negotiateBPDAdjustment(int, org.apache.fop.layoutmgr.KnuthElement)
+     */
+    public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#discardSpace(org.apache.fop.layoutmgr.KnuthGlue)
+     */
+    public void discardSpace(KnuthGlue spaceGlue) {
+        // TODO Auto-generated method stub
+        
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether()
+     */
+    public boolean mustKeepTogether() {
+        //TODO Keeps will have to be more sophisticated sooner or later
+        return ((BlockLevelLayoutManager)getParent()).mustKeepTogether() 
+                || !fobj.getKeepTogether().getWithinPage().isAuto()
+                || !fobj.getKeepTogether().getWithinColumn().isAuto();
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious()
+     */
+    public boolean mustKeepWithPrevious() {
+        return !fobj.getKeepWithPrevious().getWithinPage().isAuto()
+                || !fobj.getKeepWithPrevious().getWithinColumn().isAuto();
+    }
+
+    /**
+     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext()
+     */
+    public boolean mustKeepWithNext() {
+        return !fobj.getKeepWithNext().getWithinPage().isAuto()
+                || !fobj.getKeepWithNext().getWithinColumn().isAuto();
+    }
+
 }
 
diff --git a/src/java/org/apache/fop/layoutmgr/table/TableRowIterator.java b/src/java/org/apache/fop/layoutmgr/table/TableRowIterator.java
new file mode 100644 (file)
index 0000000..e9e8665
--- /dev/null
@@ -0,0 +1,439 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr.table;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fop.fo.flow.Table;
+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.properties.CommonBorderPaddingBackground;
+
+
+/**
+ * <p>Iterator that lets the table layout manager step over all rows of a table.
+ * </p>
+ * <p>Note: This class is not thread-safe.
+ * </p>
+ */
+public class TableRowIterator {
+
+    /** Selects the list of table-body elements for iteration. */
+    public static final int BODY = 0;
+    /** Selects the table-header element for iteration. */
+    public static final int HEADER = 1;
+    /** Selects the table-footer element for iteration. */
+    public static final int FOOTER = 2; 
+    
+    /** Logger **/
+    private static Log log = LogFactory.getLog(TableRowIterator.class);
+
+    /** The table on with this instance operates. */
+    protected Table table;
+    private ColumnSetup columns;
+    private int type;
+    
+    /** Holds the current row (TableCell instances) */
+    private List currentRow = new java.util.ArrayList();
+    /** Holds the grid units of cell from the last row while will span over the current row 
+     * (GridUnit instance) */
+    private List lastRowsSpanningCells = new java.util.ArrayList();
+    private int currentRowIndex = -1;
+    //TODO rows should later be a Jakarta Commons LinkedList so concurrent modifications while 
+    //using a ListIterator are possible
+    /** List of cache rows. */
+    private List rows = new java.util.ArrayList();
+    //private int indexOfFirstRowInList;
+    private int currentIndex = -1;
+    
+    //prefetch state
+    private ListIterator bodyIterator = null;
+    private ListIterator childInBodyIterator = null;
+    
+    public TableRowIterator(Table table, ColumnSetup columns, int what) {
+        this.table = table;
+        this.columns = columns;
+        this.type = what;
+        switch(what) {
+            case HEADER: {
+                List bodyList = new java.util.ArrayList();
+                bodyList.add(table.getTableHeader());
+                this.bodyIterator = bodyList.listIterator();
+                break;
+            }
+            case FOOTER: {
+                List bodyList = new java.util.ArrayList();
+                bodyList.add(table.getTableFooter());
+                this.bodyIterator = bodyList.listIterator();
+                break;
+            }
+            default: {
+                this.bodyIterator = table.getChildNodes();
+            }
+        }
+    }
+    
+    public void prefetchAll() {
+        while (prefetchNext()) {
+            log.trace("found row...");
+        }
+    }
+    
+    /**
+     * Returns the next row group if any. A row group in this context is the minimum number of 
+     * consecutive rows which contains all spanned grid units of its cells.
+     * @return the next row group, or null
+     */
+    public EffRow[] getNextRowGroup() {
+        EffRow firstRowInGroup = getNextRow();
+        if (firstRowInGroup == null) {
+            return null;
+        }
+        EffRow lastRowInGroup = firstRowInGroup;
+        int lastIndex = lastRowInGroup.getIndex();
+        boolean allFinished = true;
+        do {
+            Iterator iter = lastRowInGroup.getGridUnits().iterator();
+            while (iter.hasNext()) {
+                GridUnit gu = (GridUnit)iter.next();
+                if (!gu.isLastGridUnitRowSpan()) {
+                    allFinished = false;
+                    break;
+                }
+            }
+            if (!allFinished) {
+                lastIndex = lastRowInGroup.getIndex();
+                lastRowInGroup = getNextRow();
+                if (lastRowInGroup == null) {
+                    allFinished = true;
+                }
+            }
+        } while (!allFinished);
+        int rowCount = lastIndex - firstRowInGroup.getIndex() + 1;
+        EffRow[] rowGroup = new EffRow[rowCount];
+        for (int i = 0; i < rowCount; i++) {
+            rowGroup[i] = getCachedRow(i + firstRowInGroup.getIndex());
+        }
+        return rowGroup;
+    }
+    
+    public EffRow getNextRow() {
+        currentIndex++;
+        boolean moreRows = true;
+        while (moreRows && rows.size() < currentIndex + 1) {
+            moreRows = prefetchNext();
+        }
+        if (currentIndex < rows.size()) {
+            return getCachedRow(currentIndex);
+        } else {
+            return null;
+        }
+    }
+    
+    public void backToPreviewRow() {
+        currentIndex--;
+    }
+    
+    public EffRow getFirstRow() {
+        if (rows.size() == 0) {
+            prefetchNext();
+        }
+        return getCachedRow(0);
+    }
+    
+    public EffRow getLastRow() {
+        while (prefetchNext()) {
+            //nop
+        }
+        return getCachedRow(rows.size() - 1);
+    }
+    
+    public EffRow getCachedRow(int index) {
+        if (index < 0 || index >= rows.size()) {
+            return null;
+        } else {
+            return (EffRow)rows.get(index);
+        }
+    }
+    
+    private boolean prefetchNext() {
+        boolean firstInTable = false;
+        boolean firstInBody = false;
+        if (childInBodyIterator != null) {
+            if (!childInBodyIterator.hasNext()) {
+                //force skip on to next body
+                childInBodyIterator = null;
+            }
+        }
+        if (childInBodyIterator == null) {
+            if (bodyIterator.hasNext()) {
+                childInBodyIterator = ((TableBody)bodyIterator.next()).getChildNodes();
+                if (rows.size() == 0) {
+                    firstInTable = true;
+                }
+                firstInBody = true;
+            } else {
+                //no more rows
+                if (rows.size() > 0) {
+                    getCachedRow(rows.size() - 1).setFlagForAllGridUnits(
+                            GridUnit.LAST_IN_BODY, true);
+                    if ((type == FOOTER || table.getTableFooter() == null) 
+                            && type != HEADER) {
+                        getCachedRow(rows.size() - 1).setFlagForAllGridUnits(
+                                GridUnit.LAST_IN_TABLE, true);
+                    }
+                }
+                return false;
+            }
+        }
+        Object node = childInBodyIterator.next();
+        this.currentRow.clear();
+        this.currentRowIndex++;
+        TableRow rowFO = null;
+        if (node instanceof TableRow) {
+            rowFO = (TableRow)node;
+            ListIterator cellIterator = rowFO.getChildNodes();
+            while (cellIterator.hasNext()) {
+                this.currentRow.add(cellIterator.next());
+            }
+        } else if (node instanceof TableCell) {
+            this.currentRow.add(node);
+            if (!((TableCell)node).endsRow()) {
+                while (childInBodyIterator.hasNext()) {
+                    TableCell cell = (TableCell)childInBodyIterator.next();
+                    if (cell.startsRow()) {
+                        //next row already starts here, one step back
+                        childInBodyIterator.previous();
+                        break;
+                    }
+                    this.currentRow.add(cell);
+                    if (cell.endsRow()) {
+                        break;
+                    }
+                }
+            }
+        } else {
+            throw new IllegalStateException("Illegal class found: " + node.getClass().getName());
+        }
+        EffRow gridUnits = buildGridRow(this.currentRow, rowFO);
+        if (firstInBody) {
+            gridUnits.setFlagForAllGridUnits(GridUnit.FIRST_IN_BODY, true);
+        }
+        if (firstInTable && (type == HEADER || table.getTableHeader() == null)
+                && type != FOOTER) {
+            gridUnits.setFlagForAllGridUnits(GridUnit.FIRST_IN_TABLE, true);
+        }
+        log.debug(gridUnits);
+        rows.add(gridUnits);
+        return true;
+    }
+
+    private void safelySetListItem(List list, int position, Object obj) {
+        while (position >= list.size()) {
+            list.add(null);
+        }
+        list.set(position, obj);
+    }
+    
+    private Object safelyGetListItem(List list, int position) {
+        if (position >= list.size()) {
+            return null;
+        } else {
+            return list.get(position);
+        }
+    }
+    
+    private EffRow buildGridRow(List cells, TableRow rowFO) {
+        EffRow row = new EffRow(this.currentRowIndex, type);
+        List gridUnits = row.getGridUnits();
+        
+        TableBody bodyFO = null;
+        
+        //Create all row-spanned grid units based on information from the last row
+        int colnum = 1;
+        ListIterator spanIter = lastRowsSpanningCells.listIterator();
+        GridUnit[] horzSpan = null;
+        while (spanIter.hasNext()) {
+            GridUnit gu = (GridUnit)spanIter.next();
+            if (gu != null) {
+                if (gu.getColSpanIndex() == 0) {
+                    horzSpan = new GridUnit[gu.getCell().getNumberColumnsSpanned()];
+                }
+                GridUnit newGU = gu.createNextRowSpanningGridUnit();
+                newGU.setRow(rowFO);
+                safelySetListItem(gridUnits, colnum - 1, newGU);
+                horzSpan[newGU.getColSpanIndex()] = newGU;
+                if (newGU.isLastGridUnitColSpan()) {
+                    //Add the array of row-spanned grid units to the primary grid unit
+                    newGU.getPrimary().addRow(horzSpan);
+                    horzSpan = null;
+                }
+                if (newGU.isLastGridUnitRowSpan()) {
+                    spanIter.set(null);
+                } else {
+                    spanIter.set(newGU);
+                }
+            }
+            colnum++;
+        }
+        
+        //Transfer available cells to their slots
+        colnum = 1;
+        ListIterator iter = cells.listIterator();
+        while (iter.hasNext()) {
+            TableCell cell = (TableCell)iter.next();
+            
+            if (cell.hasColumnNumber()) {
+                colnum = cell.getColumnNumber();
+            } else {
+                //Skip columns with spanning grid units
+                while (safelyGetListItem(gridUnits, colnum - 1) != null) {
+                    colnum++;
+                }
+            }
+
+            if (safelyGetListItem(gridUnits, colnum - 1) != null) {
+                log.error("Overlapping cell at position " + colnum);
+                //TODO throw layout exception
+            }
+            TableColumn col = columns.getColumn(colnum);
+
+            //Add grid unit for primary grid unit
+            PrimaryGridUnit gu = new PrimaryGridUnit(cell, col, colnum - 1, this.currentRowIndex);
+            safelySetListItem(gridUnits, colnum - 1, gu);
+            boolean hasRowSpanningLeft = !gu.isLastGridUnitRowSpan();
+            if (hasRowSpanningLeft) {
+                safelySetListItem(lastRowsSpanningCells, colnum - 1, gu);
+            }
+            
+            if (gu.hasSpanning()) {
+                //Add grid units on spanned slots if any
+                horzSpan = new GridUnit[cell.getNumberColumnsSpanned()];
+                horzSpan[0] = gu;
+                for (int j = 1; j < cell.getNumberColumnsSpanned(); j++) {
+                    colnum++;
+                    GridUnit guSpan = new GridUnit(gu, columns.getColumn(colnum), colnum - 1, j);
+                    if (safelyGetListItem(gridUnits, colnum - 1) != null) {
+                        log.error("Overlapping cell at position " + colnum);
+                        //TODO throw layout exception
+                    }
+                    safelySetListItem(gridUnits, colnum - 1, guSpan);
+                    if (hasRowSpanningLeft) {
+                        safelySetListItem(lastRowsSpanningCells, colnum - 1, gu);
+                    }
+                    horzSpan[j] = guSpan;
+                }
+                gu.addRow(horzSpan);
+            }
+            
+            //Gather info for empty grid units (used later)
+            if (bodyFO == null) {
+                bodyFO = gu.getBody();
+            }
+            
+            colnum++;
+        }
+        
+        //Post-processing the list (looking for gaps and resolve start and end borders)
+        fillEmptyGridUnits(gridUnits, rowFO, bodyFO);
+        resolveStartEndBorders(gridUnits);
+        
+        return row;
+    }
+    
+    private void fillEmptyGridUnits(List gridUnits, TableRow row, TableBody body) {
+        for (int pos = 1; pos <= gridUnits.size(); pos++) {
+            GridUnit gu = (GridUnit)gridUnits.get(pos - 1);
+            
+            //Empty grid units
+            if (gu == null) {
+                //Add grid unit
+                gu = new EmptyGridUnit(row, columns.getColumn(pos), body, 
+                        pos - 1);
+                gridUnits.set(pos - 1, gu);
+            }
+            
+            //Set flags
+            gu.setFlag(GridUnit.IN_FIRST_COLUMN, (pos == 1));
+            gu.setFlag(GridUnit.IN_LAST_COLUMN, (pos == gridUnits.size()));
+        }
+    }
+    
+    private void resolveStartEndBorders(List gridUnits) {
+        for (int pos = 1; pos <= gridUnits.size(); pos++) {
+            GridUnit starting = (GridUnit)gridUnits.get(pos - 1);
+         
+            //Border resolution
+            if (table.isSeparateBorderModel()) {
+                starting.assignBorderForSeparateBorderModel();
+            } else {
+                //Neighbouring grid unit at start edge 
+                GridUnit start = null;
+                int find = pos - 1;
+                while (find >= 1) {
+                    GridUnit candidate = (GridUnit)gridUnits.get(find - 1);
+                    if (candidate.isLastGridUnitColSpan()) {
+                        start = candidate;
+                        break;
+                    }
+                    find--;
+                }
+                
+                //Ending grid unit for current cell
+                GridUnit ending = null;
+                if (starting.getCell() != null) {
+                    pos += starting.getCell().getNumberColumnsSpanned() - 1;
+                }
+                ending = (GridUnit)gridUnits.get(pos - 1);
+                
+                //Neighbouring grid unit at end edge 
+                GridUnit end = null;
+                find = pos + 1;
+                while (find <= gridUnits.size()) {
+                    GridUnit candidate = (GridUnit)gridUnits.get(find - 1);
+                    if (candidate.isPrimary()) {
+                        end = candidate;
+                        break;
+                    }
+                    find++;
+                }
+                //CommonBorderPaddingBackground borders = new CommonBorderPaddingBackground();
+                starting.resolveBorder(start, 
+                        CommonBorderPaddingBackground.START);
+                //starting.setBorders(borders);
+                /*
+                if (starting != ending) {
+                    borders = new CommonBorderPaddingBackground();
+                }*/
+                ending.resolveBorder(end, 
+                        CommonBorderPaddingBackground.END);
+                //ending.setBorders(borders);
+                //Only start and end borders here, before and after during layout
+                //TODO resolve before and after borders during layout
+            }
+        }
+    }
+
+}
diff --git a/src/java/org/apache/fop/layoutmgr/table/TableStepper.java b/src/java/org/apache/fop/layoutmgr/table/TableStepper.java
new file mode 100644 (file)
index 0000000..9600053
--- /dev/null
@@ -0,0 +1,418 @@
+/*
+ * Copyright 2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr.table;
+
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
+import org.apache.fop.layoutmgr.ElementListUtils;
+import org.apache.fop.layoutmgr.KnuthBox;
+import org.apache.fop.layoutmgr.KnuthElement;
+import org.apache.fop.layoutmgr.KnuthPenalty;
+import org.apache.fop.layoutmgr.table.TableContentLayoutManager.GridUnitPart;
+import org.apache.fop.layoutmgr.table.TableContentLayoutManager.TableContentPosition;
+import org.apache.fop.layoutmgr.table.TableContentLayoutManager.TableHFPenaltyPosition;
+
+/**
+ * This class processes row groups to create combined element lists for tables.
+ */
+public class TableStepper {
+
+    /** Logger **/
+    private static Log log = LogFactory.getLog(TableStepper.class);
+
+    private TableContentLayoutManager tclm;
+    
+    private EffRow[] rowGroup;
+    private int totalHeight;
+    private int activeRow;
+    private List[] elementLists;
+    private int[] startRow;
+    private int[] start;
+    private int[] end;
+    private int[] widths;
+    private int[] baseWidth;
+    private int[] borderBefore;
+    private int[] borderAfter;
+    private boolean rowBacktrackForLastStep;
+    
+    /**
+     * Main constructor
+     * @param tclm The parent TableContentLayoutManager
+     */
+    public TableStepper(TableContentLayoutManager tclm) {
+        this.tclm = tclm;
+        this.activeRow = 0;
+    }
+    
+    private void setup(int columnCount) {
+        elementLists = new List[columnCount];
+        startRow = new int[columnCount];
+        start = new int[columnCount];
+        end = new int[columnCount];
+        widths = new int[columnCount];
+        baseWidth = new int[columnCount];
+        borderBefore = new int[columnCount];
+        borderAfter = new int[columnCount];
+        Arrays.fill(end, -1);
+    }
+    
+    private EffRow getActiveRow() {
+        return rowGroup[activeRow];
+    }
+    
+    private GridUnit getActiveGridUnit(int column) {
+        return getActiveRow().getGridUnit(column);
+    }
+    
+    private PrimaryGridUnit getActivePrimaryGridUnit(int column) {
+        return getActiveGridUnit(column).getPrimary();
+    }
+    
+    private void calcTotalHeight() {
+        totalHeight = 0;
+        for (int i = 0; i < rowGroup.length; i++) {
+            totalHeight += rowGroup[i].getHeight().opt;
+        }
+        log.debug("totalHeight=" + totalHeight);
+    }
+    
+    private int getMaxRemainingHeight() {
+        int maxW = 0;
+        if (!rowBacktrackForLastStep) {
+            for (int i = 0; i < widths.length; i++) {
+                if (elementLists[i] == null) {
+                    continue;
+                }
+                if (getActivePrimaryGridUnit(i).getCell().getNumberRowsSpanned() > 1) {
+                    continue;
+                }
+                int len = widths[i]; 
+                if (len > 0) {
+                    len += borderBefore[i] + borderAfter[i]; 
+                }
+                if (len == rowGroup[activeRow].getHeight().opt) {
+                    //row is filled
+                    maxW = 0;
+                    break;
+                }
+                maxW = Math.max(maxW, rowGroup[activeRow].getHeight().opt - len);
+            }
+        }
+        for (int i = activeRow + 1; i < rowGroup.length; i++) {
+            maxW += rowGroup[i].getHeight().opt;
+        }
+        //log.debug("maxRemainingHeight=" + maxW);
+        return maxW;
+    }
+
+    private void setupElementList(int column) {
+        GridUnit gu = getActiveGridUnit(column);
+        EffRow row = getActiveRow();
+        if (gu.isPrimary() && !gu.isEmpty()) {
+            PrimaryGridUnit pgu = (PrimaryGridUnit)gu;
+            if (row.getExplicitHeight().min > 0) {
+                boolean contentsSmaller = ElementListUtils.removeLegalBreaks(
+                        pgu.getElements(), row.getExplicitHeight());
+                if (contentsSmaller) {
+                    List list = new java.util.ArrayList(1);
+                    list.add(new KnuthBoxCellWithBPD(
+                            row.getExplicitHeight().opt, pgu));
+                    elementLists[column] = list;
+                } else {
+                    //Copy elements (LinkedList) to array lists to improve 
+                    //element access performance
+                    elementLists[column] = new java.util.ArrayList(pgu.getElements());
+                }
+            } else {
+                //Copy elements (LinkedList) to array lists to improve 
+                //element access performance
+                elementLists[column] = new java.util.ArrayList(pgu.getElements());
+            }
+            if (isSeparateBorderModel()) {
+                borderBefore[column] = pgu.getBorders().getBorderBeforeWidth(false);
+            } else {
+                borderBefore[column] = pgu.getBorders().getBorderBeforeWidth(false) / 2;
+            }
+            start[column] = 0;
+            end[column] = -1;
+            widths[column] = 0;
+            startRow[column] = activeRow;
+        }
+    }
+    
+    private void initializeElementLists() {
+        for (int i = 0; i < start.length; i++) {
+            setupElementList(i);
+        }
+    }
+
+    /**
+     * Creates the combined element list for a row group.
+     * @param rowGroup the row group
+     * @param maxColumnCount the maximum number of columns to expect
+     * @param bodyType Indicates what type of body is processed (boder, header or footer)
+     * @return the combined element list
+     */
+    public LinkedList getCombinedKnuthElementsForRowGroup( 
+            EffRow[] rowGroup, int maxColumnCount, int bodyType) {
+        this.rowGroup = rowGroup;
+        setup(maxColumnCount);
+        initializeElementLists();
+        calcTotalHeight();
+        
+        int laststep = 0;
+        int step;
+        int addedBoxLen = 0;
+        LinkedList returnList = new LinkedList();
+        while ((step = getNextStep(laststep)) > 0) {
+            if (rowBacktrackForLastStep) {
+                //Even though we've already switched to the next row, we have to 
+                //calculate as if we were still on the previous row
+                activeRow--;
+            }
+            int increase = step - laststep;
+            int penaltyLen = step + getMaxRemainingHeight() - totalHeight;
+            int boxLen = step - addedBoxLen - penaltyLen;
+            addedBoxLen += boxLen;
+            
+            //Put all involved grid units into a list
+            List gridUnitParts = new java.util.ArrayList(maxColumnCount);
+            for (int i = 0; i < start.length; i++) {
+                if (end[i] >= start[i]) {
+                    PrimaryGridUnit pgu = getActivePrimaryGridUnit(i);
+                    if (start[i] == 0 && end[i] == 0 
+                            && elementLists[i].size() == 1
+                            && elementLists[i].get(0) instanceof KnuthBoxCellWithBPD) {
+                        //Special case: Cell with fixed BPD
+                        gridUnitParts.add(new GridUnitPart(pgu, 
+                                0, pgu.getElements().size() - 1));
+                    } else {
+                        gridUnitParts.add(new GridUnitPart(pgu, start[i], end[i]));
+                    }
+                }
+            }
+            //log.debug(">>> guPARTS: " + gridUnitParts);
+            
+            //Create elements for step
+            int effPenaltyLen = penaltyLen;
+            if (isSeparateBorderModel()) {
+                CommonBorderPaddingBackground borders 
+                    = getTableLM().getTable().getCommonBorderPaddingBackground(); 
+                effPenaltyLen += borders.getBorderBeforeWidth(false); 
+                effPenaltyLen += borders.getBorderAfterWidth(false); 
+            }
+            TableContentPosition tcpos = new TableContentPosition(getTableLM(), 
+                    gridUnitParts, getActiveRow());
+            log.debug(" - " + rowBacktrackForLastStep + " - " + activeRow + " - " + tcpos);
+            returnList.add(new KnuthBox(boxLen, tcpos, false));
+            TableHFPenaltyPosition penaltyPos = new TableHFPenaltyPosition(getTableLM());
+            if (bodyType == TableRowIterator.BODY) {
+                if (!getTableLM().getTable().omitHeaderAtBreak()) {
+                    effPenaltyLen += tclm.getHeaderNetHeight();
+                    penaltyPos.headerElements = tclm.getHeaderElements();
+                }
+                if (!getTableLM().getTable().omitFooterAtBreak()) {
+                    effPenaltyLen += tclm.getFooterNetHeight();
+                    penaltyPos.footerElements = tclm.getFooterElements();
+                }
+            }
+            returnList.add(new KnuthPenalty(effPenaltyLen, 0, false, penaltyPos, false));
+
+            log.debug("step=" + step + " (+" + increase + ")"
+                    + " box=" + boxLen 
+                    + " penalty=" + penaltyLen
+                    + " effPenalty=" + effPenaltyLen);
+            
+            laststep = step;
+            if (rowBacktrackForLastStep) {
+                //If row was set to previous, restore now
+                activeRow++;
+            }
+        }
+        return returnList;
+    }
+    
+    private int getNextStep(int lastStep) {
+        int[] backupWidths = new int[start.length];
+        System.arraycopy(widths, 0, backupWidths, 0, backupWidths.length);
+
+        //set starting points
+        int rowPendingIndicator = 0;
+        for (int i = 0; i < start.length; i++) {
+            if (elementLists[i] == null) {
+                continue;
+            }
+            if (end[i] < elementLists[i].size()) {
+                start[i] = end[i] + 1;
+                if (end[i] + 1 < elementLists[i].size() 
+                        && getActivePrimaryGridUnit(i).isLastGridUnitRowSpan()) {
+                    rowPendingIndicator++;
+                }
+            } else {
+                start[i] = -1; //end of list reached
+                end[i] = -1;
+            }
+        }
+
+        if (rowPendingIndicator == 0) {
+            if (activeRow < rowGroup.length - 1) {
+                activeRow++;
+                log.debug("===> new row: " + activeRow);
+                initializeElementLists();
+                for (int i = 0; i < backupWidths.length; i++) {
+                    if (end[i] < 0) {
+                        backupWidths[i] = 0;
+                    }
+                }
+            }
+        }
+
+        //Get next possible sequence for each cell
+        int seqCount = 0;
+        for (int i = 0; i < start.length; i++) {
+            if (elementLists[i] == null) {
+                continue;
+            }
+            while (end[i] + 1 < elementLists[i].size()) {
+                end[i]++;
+                KnuthElement el = (KnuthElement)elementLists[i].get(end[i]);
+                if (el.isPenalty()) {
+                    if (el.getP() < KnuthElement.INFINITE) {
+                        //First legal break point
+                        break;
+                    }
+                } else if (el.isGlue()) {
+                    KnuthElement prev = (KnuthElement)elementLists[i].get(end[i] - 1);
+                    if (prev.isBox()) {
+                        //Second legal break point
+                        break;
+                    }
+                    widths[i] += el.getW();
+                } else {
+                    widths[i] += el.getW();
+                }
+            }
+            if (end[i] < start[i]) {
+                widths[i] = backupWidths[i];
+            } else {
+                seqCount++;
+            }
+            //log.debug("part " + start[i] + "-" + end[i] + " " + widths[i]);
+            if (end[i] + 1 >= elementLists[i].size()) {
+                //element list for this cell is finished
+                if (isSeparateBorderModel()) {
+                    borderAfter[i] = getActivePrimaryGridUnit(i)
+                            .getBorders().getBorderAfterWidth(false);
+                } else {
+                    borderAfter[i] = getActivePrimaryGridUnit(i).getHalfMaxAfterBorderWidth();
+                }
+            } else {
+                //element list for this cell is not finished
+                if (isSeparateBorderModel()) {
+                    borderAfter[i] = getActivePrimaryGridUnit(i)
+                            .getBorders().getBorderAfterWidth(false);
+                } else {
+                    //TODO fix me!
+                    borderAfter[i] = getActivePrimaryGridUnit(i).getHalfMaxAfterBorderWidth();
+                }
+            }
+            log.debug("borders before=" + borderBefore[i] + " after=" + borderAfter[i]);
+        }
+        if (seqCount == 0) {
+            return 0;
+        }
+
+        //Determine smallest possible step
+        int minStep = Integer.MAX_VALUE;
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < widths.length; i++) {
+            baseWidth[i] = 0;
+            for (int prevRow = 0; prevRow < startRow[i]; prevRow++) {
+                baseWidth[i] += rowGroup[prevRow].getHeight().opt;
+            }
+            baseWidth[i] += borderBefore[i] + borderAfter[i];
+            if (end[i] >= start[i]) {
+                int len = baseWidth[i] + widths[i];
+                sb.append(len + " ");
+                minStep = Math.min(len, minStep);
+            }
+        }
+        log.debug("candidate steps: " + sb);
+
+        //Check for constellations that would result in overlapping borders
+        /*
+        for (int i = 0; i < widths.length; i++) {
+            
+        }*/
+        
+        //Reset bigger-than-minimum sequences
+        rowBacktrackForLastStep = false;
+        for (int i = 0; i < widths.length; i++) {
+            int len = baseWidth[i] + widths[i];
+            if (len > minStep) {
+                widths[i] = backupWidths[i];
+                end[i] = start[i] - 1;
+                if (baseWidth[i] + widths[i] > minStep) {
+                    log.debug("Meeeeep!");
+                    rowBacktrackForLastStep = true;
+                }
+            }
+        }
+        if (log.isDebugEnabled()) {
+            /*StringBuffer*/ sb = new StringBuffer();
+            for (int i = 0; i < widths.length; i++) {
+                if (end[i] >= start[i]) {
+                    sb.append(i + ": " + start[i] + "-" + end[i] + "(" + widths[i] + "), ");
+                } else {
+                    sb.append(i + ": skip, ");
+                }
+            }
+            log.debug(sb.toString());
+        }
+
+        return minStep;
+    }
+    
+    
+    /** @return true if the table uses the separate border model. */
+    private boolean isSeparateBorderModel() {
+        return getTableLM().getTable().isSeparateBorderModel();
+    }
+
+    /** @return the table layout manager */
+    private TableLayoutManager getTableLM() {
+        return this.tclm.getTableLM();
+    }
+
+    private class KnuthBoxCellWithBPD extends KnuthBox {
+        
+        private PrimaryGridUnit pgu;
+        
+        public KnuthBoxCellWithBPD(int w, PrimaryGridUnit pgu) {
+            super(w, null, true);
+            this.pgu = pgu;
+        }
+    }
+    
+}
index 2492ab129b1c6e8b8b5fc3c863774af81bbda7fe..2bbd89fadd2b8bd1016c665bad3e8d50f4bd0b3d 100644 (file)
@@ -259,16 +259,16 @@ public abstract class AbstractRenderer
             currentBPPosition = 0;
             currentIPPosition = 0;
 
-            RegionReference region = port.getRegion();
+            RegionReference regionReference = port.getRegionReference();
             handleRegionTraits(port);
 
             //  shouldn't the viewport have the CTM
-            startVParea(region.getCTM());
+            startVParea(regionReference.getCTM());
             // do after starting viewport area
-            if (region.getRegionClass() == FO_REGION_BODY) {
-                renderBodyRegion((BodyRegion) region);
+            if (regionReference.getRegionClass() == FO_REGION_BODY) {
+                renderBodyRegion((BodyRegion) regionReference);
             } else {
-                renderRegion(region);
+                renderRegion(regionReference);
             }
             endVParea();
         }
@@ -374,19 +374,24 @@ public abstract class AbstractRenderer
 
         Span span = null;
         List spans = mr.getSpans();
+        int saveBPPos = currentBPPosition;
         for (int count = 0; count < spans.size(); count++) {
             span = (Span) spans.get(count);
             int offset = (mr.getWidth()
-                    - (span.getColumnCount() - 1) * mr.getColumnGap())
-                    / span.getColumnCount() + mr.getColumnGap();
+                    - (mr.getColumnCount() - 1) * mr.getColumnGap())
+                    / mr.getColumnCount() + mr.getColumnGap();
             for (int c = 0; c < span.getColumnCount(); c++) {
                 NormalFlow flow = (NormalFlow) span.getNormalFlow(c);
 
-                renderFlow(flow);
-                currentIPPosition += offset;
+                if (flow != null) {
+                    currentBPPosition = saveBPPos;
+                    renderFlow(flow);
+                    currentIPPosition += flow.getIPD();
+                    currentIPPosition += offset;
+                }
             }
             currentIPPosition = saveIPPos;
-            currentBPPosition += span.getHeight();
+            currentBPPosition = saveBPPos + span.getHeight();
         }
     }
 
index e7e4e8613753bfbf2b737477f223a9c70be847a7..daefe87f49feaf8858a79f418f1bda93c51ad8d7 100644 (file)
@@ -517,7 +517,7 @@ public class PDFRenderer extends PrintRenderer {
         float width = (float)(viewArea.getWidth() / 1000f);
         float height = (float)(viewArea.getHeight() / 1000f);
 
-        if (region.getRegion().getRegionClass() == FO_REGION_BODY) {
+        if (region.getRegionReference().getRegionClass() == FO_REGION_BODY) {
             currentBPPosition = region.getBorderAndPaddingWidthBefore();
             currentIPPosition = region.getBorderAndPaddingWidthStart();
         }
@@ -655,18 +655,18 @@ public class PDFRenderer extends PrintRenderer {
             }
         }
 
-        boolean b[] = new boolean[] {
+        boolean[] b = new boolean[] {
             (bpsBefore != null), (bpsEnd != null), 
             (bpsAfter != null), (bpsStart != null)};
         if (!b[0] && !b[1] && !b[2] && !b[3]) {
             return;
         }
-        float bw[] = new float[] {
+        float[] bw = new float[] {
             (b[0] ? bpsBefore.width / 1000f : 0.0f),
             (b[1] ? bpsEnd.width / 1000f : 0.0f),
             (b[2] ? bpsAfter.width / 1000f : 0.0f),
             (b[3] ? bpsStart.width / 1000f : 0.0f)};
-        float clipw[] = new float[] {
+        float[] clipw = new float[] {
             BorderProps.getClippedWidth(bpsBefore) / 1000f,    
             BorderProps.getClippedWidth(bpsEnd) / 1000f,    
             BorderProps.getClippedWidth(bpsAfter) / 1000f,    
@@ -678,7 +678,7 @@ public class PDFRenderer extends PrintRenderer {
         width -= clipw[3];
         width -= clipw[1];
         
-        boolean slant[] = new boolean[] {
+        boolean[] slant = new boolean[] {
             (b[3] && b[0]), (b[0] && b[1]), (b[1] && b[2]), (b[2] && b[3])};
         if (bpsBefore != null) {
             endTextObject();
@@ -710,7 +710,8 @@ public class PDFRenderer extends PrintRenderer {
             lineTo(sx2, innery);
             closePath();
             clip();
-            drawBorderLine(sx1a, outery, ex1a, innery, true, true, bpsBefore.style, bpsBefore.color);
+            drawBorderLine(sx1a, outery, ex1a, innery, true, true, 
+                    bpsBefore.style, bpsBefore.color);
             restoreGraphicsState();
         }
         if (bpsEnd != null) {
@@ -765,7 +766,7 @@ public class PDFRenderer extends PrintRenderer {
                 if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) {
                     sx1a -= clipw[3];
                 }
-                if (bpsStart != null && bpsStart.mode == BorderProps.COLLAPSE_OUTER) {
+                if (bpsEnd != null && bpsEnd.mode == BorderProps.COLLAPSE_OUTER) {
                     ex1a += clipw[1];
                 }
                 lineTo(ex1a, outery);
@@ -785,7 +786,7 @@ public class PDFRenderer extends PrintRenderer {
             float sy1 = starty;
             float sy2 = (slant[0] ? sy1 + bw[0] - clipw[0] : sy1);
             float ey1 = sy1 + height;
-            float ey2 = (slant[3] ? ey1 - bw[2] + clipw[2]: ey1);
+            float ey2 = (slant[3] ? ey1 - bw[2] + clipw[2] : ey1);
             float outerx = startx - clipw[3];
             float clipx = outerx + clipw[3];
             float innerx = outerx + bw[3];
@@ -801,8 +802,8 @@ public class PDFRenderer extends PrintRenderer {
                 if (bpsAfter != null && bpsAfter.mode == BorderProps.COLLAPSE_OUTER) {
                     ey1a += clipw[2];
                 }
-                lineTo(outerx, sy1a);
                 lineTo(outerx, ey1a);
+                lineTo(outerx, sy1a);
             }
             lineTo(clipx, sy1);
             lineTo(innerx, sy2);
index 8fccab4bd21921d84ec0863eef8d81c84bb46e6f..74e993c4ce046ccf8f721c35391bc24ab7721cc4 100644 (file)
@@ -378,7 +378,7 @@ public class XMLRenderer extends AbstractRenderer {
             addTraitAttributes(port);
             addAttribute("rect", port.getViewArea());
             startElement("regionViewport", atts);
-            RegionReference region = port.getRegion();
+            RegionReference region = port.getRegionReference();
             atts.clear();
             addAreaAttributes(region);
             addTraitAttributes(region);
index d4dee26533b6ceba46e99321c851fa2167a16a73..b0129f632772e7673c7ad653d8c023b5caba3c91 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 1999-2004 The Apache Software Foundation.
+ * Copyright 1999-2005 The Apache Software Foundation.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
  
 package org.apache.fop.traits;
 
+import org.apache.fop.datatypes.KeepValue;
 import org.apache.fop.fo.Constants;
 
 /**
@@ -29,6 +30,13 @@ public class LayoutProps {
 
     public int breakBefore; // enum constant BreakBefore.xxx
     public int breakAfter; // enum constant BreakAfter.xxx
+    public KeepValue keepWithPrevious;  /*LF*/
+    public KeepValue keepWithNext;      /*LF*/
+    public KeepValue keepTogether;      /*LF*/
+    public int orphans;                 /*LF*/
+    public int widows;                  /*LF*/
+    public int blockProgressionUnit;    /*LF*/
+    public int lineStackingStrategy;    /*LF*/
     public boolean bIsSpan;
     public SpaceVal spaceBefore;
     public SpaceVal spaceAfter;
index d0a5d8734368222793b442d5010f3da19eafa3a7..760e0697ea651d479b1de8e9a265844cf02db502 100644 (file)
       <fo:page-sequence master-reference="normal" white-space-collapse="true">
         <fo:flow flow-name="xsl-region-body">
           <fo:block>line1</fo:block>
-          <fo:block break-before="column">line2</fo:block>
+          <fo:block break-before="column">line2 break-before="column"</fo:block>
           <fo:block>line3</fo:block>
-          <fo:block break-before="page">line4</fo:block>
+          <fo:block break-before="page">line4 break-before="page"</fo:block>
           <fo:block>line5</fo:block>
-          <fo:block break-before="even-page">line6</fo:block>
+          <fo:block break-before="even-page">line6 break-before="even-page"</fo:block>
           <fo:block>line7</fo:block>
-          <fo:block break-before="even-page">line8</fo:block>
+          <fo:block break-before="even-page">line8 break-before="even-page"</fo:block>
           <fo:block>line9</fo:block>
-          <fo:block break-before="odd-page">line10</fo:block>
+          <fo:block break-before="odd-page">line10 break-before="odd-page"</fo:block>
           <fo:block>line11</fo:block>
-          <fo:block break-before="odd-page">line12</fo:block>
+          <fo:block break-before="odd-page">line12 break-before="odd-page"</fo:block>
           <fo:block>line13</fo:block>
         </fo:flow>
       </fo:page-sequence>
     </fo:root>
   </fo>
   <checks>
-    <eval expected="1" xpath="//lineArea[. = 'line1']/ancestor::pageViewport/@nr"/>
-    <eval expected="2" xpath="//lineArea[. = 'line2']/ancestor::pageViewport/@nr"/>
-    <eval expected="2" xpath="//lineArea[. = 'line3']/ancestor::pageViewport/@nr"/>
-    <eval expected="3" xpath="//lineArea[. = 'line4']/ancestor::pageViewport/@nr"/>
-    <eval expected="3" xpath="//lineArea[. = 'line5']/ancestor::pageViewport/@nr"/>
-    <eval expected="4" xpath="//lineArea[. = 'line6']/ancestor::pageViewport/@nr"/>
-    <eval expected="4" xpath="//lineArea[. = 'line7']/ancestor::pageViewport/@nr"/>
-    <eval expected="6" xpath="//lineArea[. = 'line8']/ancestor::pageViewport/@nr"/>
-    <eval expected="6" xpath="//lineArea[. = 'line9']/ancestor::pageViewport/@nr"/>
-    <eval expected="7" xpath="//lineArea[. = 'line10']/ancestor::pageViewport/@nr"/>
-    <eval expected="7" xpath="//lineArea[. = 'line11']/ancestor::pageViewport/@nr"/>
-    <eval expected="9" xpath="//lineArea[. = 'line12']/ancestor::pageViewport/@nr"/>
-    <eval expected="9" xpath="//lineArea[. = 'line13']/ancestor::pageViewport/@nr"/>
+    <eval expected="1" xpath="//lineArea[starts-with(., 'line1')]/ancestor::pageViewport/@nr"/>
+    <eval expected="2" xpath="//lineArea[starts-with(., 'line2')]/ancestor::pageViewport/@nr"/>
+    <eval expected="2" xpath="//lineArea[starts-with(., 'line3')]/ancestor::pageViewport/@nr"/>
+    <eval expected="3" xpath="//lineArea[starts-with(., 'line4')]/ancestor::pageViewport/@nr"/>
+    <eval expected="3" xpath="//lineArea[starts-with(., 'line5')]/ancestor::pageViewport/@nr"/>
+    <eval expected="4" xpath="//lineArea[starts-with(., 'line6')]/ancestor::pageViewport/@nr"/>
+    <eval expected="4" xpath="//lineArea[starts-with(., 'line7')]/ancestor::pageViewport/@nr"/>
+    <eval expected="6" xpath="//lineArea[starts-with(., 'line8')]/ancestor::pageViewport/@nr"/>
+    <eval expected="6" xpath="//lineArea[starts-with(., 'line9')]/ancestor::pageViewport/@nr"/>
+    <eval expected="7" xpath="//lineArea[starts-with(., 'line10')]/ancestor::pageViewport/@nr"/>
+    <eval expected="7" xpath="//lineArea[starts-with(., 'line11')]/ancestor::pageViewport/@nr"/>
+    <eval expected="9" xpath="//lineArea[starts-with(., 'line12')]/ancestor::pageViewport/@nr"/>
+    <eval expected="9" xpath="//lineArea[starts-with(., 'line13')]/ancestor::pageViewport/@nr"/>
   </checks>
 </testcase>
index 943152afdea50dd7a7b025870f9654ccf1ff9039..beab46358219accc0d8a410c7e79c314cdf995fb 100644 (file)
       <fo:page-sequence master-reference="normal" white-space-collapse="true">
         <fo:flow flow-name="xsl-region-body">
           <fo:block break-after="column">line1</fo:block>
-          <fo:block>line2</fo:block>
+          <fo:block>line2, last block had break-after="column"</fo:block>
           <fo:block break-after="page">line3</fo:block>
-          <fo:block>line4</fo:block>
+          <fo:block>line4, last block had break-after="page"</fo:block>
           <fo:block break-after="even-page">line5</fo:block>
-          <fo:block>line6</fo:block>
+          <fo:block>line6, last block had break-after="even-page"</fo:block>
           <fo:block break-after="even-page">line7</fo:block>
-          <fo:block>line8</fo:block>
+          <fo:block>line8, last block had break-after="even-page"</fo:block>
           <fo:block break-after="odd-page">line9</fo:block>
-          <fo:block>line10</fo:block>
+          <fo:block>line10, last block had break-after="odd-page"</fo:block>
           <fo:block break-after="odd-page">line11</fo:block>
-          <fo:block>line12</fo:block>
+          <fo:block>line12, last block had break-after="odd-page"</fo:block>
           <fo:block>line13</fo:block>
         </fo:flow>
       </fo:page-sequence>
     </fo:root>
   </fo>
   <checks>
-    <eval expected="1" xpath="//lineArea[. = 'line1']/ancestor::pageViewport/@nr"/>
-    <eval expected="2" xpath="//lineArea[. = 'line2']/ancestor::pageViewport/@nr"/>
-    <eval expected="2" xpath="//lineArea[. = 'line3']/ancestor::pageViewport/@nr"/>
-    <eval expected="3" xpath="//lineArea[. = 'line4']/ancestor::pageViewport/@nr"/>
-    <eval expected="3" xpath="//lineArea[. = 'line5']/ancestor::pageViewport/@nr"/>
-    <eval expected="4" xpath="//lineArea[. = 'line6']/ancestor::pageViewport/@nr"/>
-    <eval expected="4" xpath="//lineArea[. = 'line7']/ancestor::pageViewport/@nr"/>
-    <eval expected="6" xpath="//lineArea[. = 'line8']/ancestor::pageViewport/@nr"/>
-    <eval expected="6" xpath="//lineArea[. = 'line9']/ancestor::pageViewport/@nr"/>
-    <eval expected="7" xpath="//lineArea[. = 'line10']/ancestor::pageViewport/@nr"/>
-    <eval expected="7" xpath="//lineArea[. = 'line11']/ancestor::pageViewport/@nr"/>
-    <eval expected="9" xpath="//lineArea[. = 'line12']/ancestor::pageViewport/@nr"/>
-    <eval expected="9" xpath="//lineArea[. = 'line13']/ancestor::pageViewport/@nr"/>
+    <eval expected="1" xpath="//lineArea[starts-with(., 'line1')]/ancestor::pageViewport/@nr"/>
+    <eval expected="2" xpath="//lineArea[starts-with(., 'line2')]/ancestor::pageViewport/@nr"/>
+    <eval expected="2" xpath="//lineArea[starts-with(., 'line3')]/ancestor::pageViewport/@nr"/>
+    <eval expected="3" xpath="//lineArea[starts-with(., 'line4')]/ancestor::pageViewport/@nr"/>
+    <eval expected="3" xpath="//lineArea[starts-with(., 'line5')]/ancestor::pageViewport/@nr"/>
+    <eval expected="4" xpath="//lineArea[starts-with(., 'line6')]/ancestor::pageViewport/@nr"/>
+    <eval expected="4" xpath="//lineArea[starts-with(., 'line7')]/ancestor::pageViewport/@nr"/>
+    <eval expected="6" xpath="//lineArea[starts-with(., 'line8')]/ancestor::pageViewport/@nr"/>
+    <eval expected="6" xpath="//lineArea[starts-with(., 'line9')]/ancestor::pageViewport/@nr"/>
+    <eval expected="7" xpath="//lineArea[starts-with(., 'line10')]/ancestor::pageViewport/@nr"/>
+    <eval expected="7" xpath="//lineArea[starts-with(., 'line11')]/ancestor::pageViewport/@nr"/>
+    <eval expected="9" xpath="//lineArea[starts-with(., 'line12')]/ancestor::pageViewport/@nr"/>
+    <eval expected="9" xpath="//lineArea[starts-with(., 'line13')]/ancestor::pageViewport/@nr"/>
   </checks>
 </testcase>
diff --git a/test/layoutengine/testcases/keep-together1.xml b/test/layoutengine/testcases/keep-together1.xml
new file mode 100644 (file)
index 0000000..aa3c808
--- /dev/null
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright 2005 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+  <info>
+    <p>
+      This test checks keep-together.
+    </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" xmlns:svg="http://www.w3.org/2000/svg" white-space-collapse="true" 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/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="normal">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block>block1</fo:block>
+          <fo:block>block2</fo:block>
+          <fo:block keep-together.within-page="always">block3 
+            <fo:inline font-style="italic">
+              The quick brown fox jumps over the lazy dog.
+              The quick brown fox jumps over the lazy dog.
+              The quick brown fox jumps over the lazy dog.
+            </fo:inline>
+          </fo:block>
+          <fo:block>block4</fo:block>
+          <fo:block>block5</fo:block>
+          <fo:block keep-together.within-page="always">
+            <fo:block>block6</fo:block>
+            <fo:block>block7</fo:block>
+            <fo:block>block8</fo:block>
+            <fo:block>block9</fo:block>
+          </fo:block>
+          <fo:block>block10</fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+      <fo:page-sequence master-reference="normal">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block>block11</fo:block>
+          <fo:block>block12</fo:block>
+          <fo:block keep-together.within-column="always">block13 
+            <fo:inline font-style="italic">
+              The quick brown fox jumps over the lazy dog.
+              The quick brown fox jumps over the lazy dog.
+              The quick brown fox jumps over the lazy dog.
+            </fo:inline>
+          </fo:block>
+          <fo:block>block14</fo:block>
+          <fo:block>block15</fo:block>
+          <fo:block keep-together.within-column="always">
+            <fo:block>block16</fo:block>
+            <fo:block>block17</fo:block>
+            <fo:block>block18</fo:block>
+            <fo:block>block19</fo:block>
+          </fo:block>
+          <fo:block>block20</fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+      <fo:page-sequence master-reference="normal">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block>block21</fo:block>
+          <fo:block>block22</fo:block>
+          <fo:block-container keep-together.within-page="always">
+            <fo:block>block23</fo:block>
+            <fo:block>block24</fo:block>
+            <fo:block>block25</fo:block>
+          </fo:block-container>
+          <fo:block>block26</fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+      <fo:page-sequence master-reference="normal">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block>block31</fo:block>
+          <fo:block>block32</fo:block>
+          <fo:block keep-together.within-page="always">
+            <fo:block-container>
+              <fo:block>block33</fo:block>
+            </fo:block-container>
+            <fo:block>block34</fo:block>
+            <fo:block>block35</fo:block>
+          </fo:block>
+          <fo:block>block36</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="2" xpath="//lineArea[starts-with(., 'block3')]/ancestor::pageViewport/@nr"/>
+    <eval expected="2" xpath="//lineArea[starts-with(., 'block4')]/ancestor::pageViewport/@nr"/>
+    <eval expected="3" xpath="//lineArea[starts-with(., 'block5')]/ancestor::pageViewport/@nr"/>
+    <eval expected="4" xpath="//lineArea[starts-with(., 'block6')]/ancestor::pageViewport/@nr"/>
+    <eval expected="4" 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="6" xpath="//lineArea[starts-with(., 'block11')]/ancestor::pageViewport/@nr"/>
+    <eval expected="6" xpath="//lineArea[starts-with(., 'block12')]/ancestor::pageViewport/@nr"/>
+    <eval expected="7" xpath="//lineArea[starts-with(., 'block13')]/ancestor::pageViewport/@nr"/>
+    <eval expected="7" xpath="//lineArea[starts-with(., 'block14')]/ancestor::pageViewport/@nr"/>
+    <eval expected="8" xpath="//lineArea[starts-with(., 'block15')]/ancestor::pageViewport/@nr"/>
+    <eval expected="9" xpath="//lineArea[starts-with(., 'block16')]/ancestor::pageViewport/@nr"/>
+    <eval expected="9" xpath="//lineArea[starts-with(., 'block17')]/ancestor::pageViewport/@nr"/>
+    <eval expected="9" xpath="//lineArea[starts-with(., 'block18')]/ancestor::pageViewport/@nr"/>
+    <eval expected="9" xpath="//lineArea[starts-with(., 'block19')]/ancestor::pageViewport/@nr"/>
+    <eval expected="10" xpath="//lineArea[starts-with(., 'block20')]/ancestor::pageViewport/@nr"/>
+    
+    <eval expected="11" xpath="//lineArea[starts-with(., 'block21')]/ancestor::pageViewport/@nr"/>
+    <eval expected="11" xpath="//lineArea[starts-with(., 'block22')]/ancestor::pageViewport/@nr"/>
+    <eval expected="12" xpath="//lineArea[starts-with(., 'block23')]/ancestor::pageViewport/@nr"/>
+    <eval expected="12" xpath="//lineArea[starts-with(., 'block24')]/ancestor::pageViewport/@nr"/>
+    <eval expected="12" xpath="//lineArea[starts-with(., 'block25')]/ancestor::pageViewport/@nr"/>
+    <eval expected="12" xpath="//lineArea[starts-with(., 'block26')]/ancestor::pageViewport/@nr"/>
+
+    <eval expected="13" xpath="//lineArea[starts-with(., 'block31')]/ancestor::pageViewport/@nr"/>
+    <eval expected="13" xpath="//lineArea[starts-with(., 'block32')]/ancestor::pageViewport/@nr"/>
+    <eval expected="14" xpath="//lineArea[starts-with(., 'block33')]/ancestor::pageViewport/@nr"/>
+    <eval expected="14" xpath="//lineArea[starts-with(., 'block34')]/ancestor::pageViewport/@nr"/>
+    <eval expected="14" xpath="//lineArea[starts-with(., 'block35')]/ancestor::pageViewport/@nr"/>
+    <eval expected="14" xpath="//lineArea[starts-with(., 'block36')]/ancestor::pageViewport/@nr"/>
+  </checks>
+</testcase>
diff --git a/test/layoutengine/testcases/keep-with-next1.xml b/test/layoutengine/testcases/keep-with-next1.xml
new file mode 100644 (file)
index 0000000..120c2c8
--- /dev/null
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright 2005 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+  <info>
+    <p>
+      This test checks keep-with-next.
+    </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" xmlns:svg="http://www.w3.org/2000/svg" white-space-collapse="true" 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/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="normal">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block>block1</fo:block>
+          <fo:block>block2</fo:block>
+          <fo:block keep-with-next.within-page="always">block3</fo:block>
+          <fo:block keep-with-next.within-page="always">block4</fo:block>
+          <fo:block>block5</fo:block>
+          <fo:block keep-with-next.within-page="always">
+            <fo:block>block6</fo:block>
+            <fo:block>block7</fo:block>
+            <fo:block>block8</fo:block>
+            <fo:block>block9</fo:block>
+            <fo:block>block10</fo:block>
+          </fo:block>
+          <fo:block>block11</fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+      <fo:page-sequence master-reference="normal">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block>block21</fo:block>
+          <fo:block>block22</fo:block>
+          <fo:block keep-with-next.within-column="always">block23</fo:block>
+          <fo:block keep-with-next.within-column="always">block24</fo:block>
+          <fo:block>block25</fo:block>
+          <fo:block keep-with-next.within-column="always">
+            <fo:block>block26</fo:block>
+            <fo:block>block27</fo:block>
+            <fo:block>block28</fo:block>
+            <fo:block>block29</fo:block>
+            <fo:block>block30</fo:block>
+          </fo:block>
+          <fo:block>block31</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="2" xpath="//lineArea[starts-with(., 'block3')]/ancestor::pageViewport/@nr"/>
+    <eval expected="2" xpath="//lineArea[starts-with(., 'block4')]/ancestor::pageViewport/@nr"/>
+    <eval expected="2" xpath="//lineArea[starts-with(., 'block5')]/ancestor::pageViewport/@nr"/>
+    <eval expected="2" xpath="//lineArea[starts-with(., 'block6')]/ancestor::pageViewport/@nr"/>
+    <eval expected="3" xpath="//lineArea[starts-with(., 'block7')]/ancestor::pageViewport/@nr"/>
+    <eval expected="3" xpath="//lineArea[starts-with(., 'block8')]/ancestor::pageViewport/@nr"/>
+    <eval expected="3" xpath="//lineArea[starts-with(., 'block9')]/ancestor::pageViewport/@nr"/>
+    <eval expected="4" xpath="//lineArea[starts-with(., 'block10')]/ancestor::pageViewport/@nr"/>
+    <eval expected="4" xpath="//lineArea[starts-with(., 'block11')]/ancestor::pageViewport/@nr"/>
+
+    <eval expected="5" xpath="//lineArea[starts-with(., 'block21')]/ancestor::pageViewport/@nr"/>
+    <eval expected="5" xpath="//lineArea[starts-with(., 'block22')]/ancestor::pageViewport/@nr"/>
+    <eval expected="6" xpath="//lineArea[starts-with(., 'block23')]/ancestor::pageViewport/@nr"/>
+    <eval expected="6" xpath="//lineArea[starts-with(., 'block24')]/ancestor::pageViewport/@nr"/>
+    <eval expected="6" xpath="//lineArea[starts-with(., 'block25')]/ancestor::pageViewport/@nr"/>
+    <eval expected="6" xpath="//lineArea[starts-with(., 'block26')]/ancestor::pageViewport/@nr"/>
+    <eval expected="7" xpath="//lineArea[starts-with(., 'block27')]/ancestor::pageViewport/@nr"/>
+    <eval expected="7" xpath="//lineArea[starts-with(., 'block28')]/ancestor::pageViewport/@nr"/>
+    <eval expected="7" xpath="//lineArea[starts-with(., 'block29')]/ancestor::pageViewport/@nr"/>
+    <eval expected="8" xpath="//lineArea[starts-with(., 'block30')]/ancestor::pageViewport/@nr"/>
+    <eval expected="8" xpath="//lineArea[starts-with(., 'block31')]/ancestor::pageViewport/@nr"/>
+  </checks>
+</testcase>
diff --git a/test/layoutengine/testcases/keep-with-previous1.xml b/test/layoutengine/testcases/keep-with-previous1.xml
new file mode 100644 (file)
index 0000000..8dd2978
--- /dev/null
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright 2005 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+  <info>
+    <p>
+      This test checks keep-with-previous.
+    </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" xmlns:svg="http://www.w3.org/2000/svg" white-space-collapse="true" 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/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="normal">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block>block1</fo:block>
+          <fo:block>block2</fo:block>
+          <fo:block>block3</fo:block>
+          <fo:block keep-with-previous.within-page="always">block4</fo:block>
+          <fo:block keep-with-previous.within-page="always">block5</fo:block>
+          <fo:block>
+            <fo:block>block6</fo:block>
+            <fo:block>block7</fo:block>
+            <fo:block>block8</fo:block>
+            <fo:block>block9</fo:block>
+            <fo:block>block10</fo:block>
+          </fo:block>
+          <fo:block keep-with-previous.within-page="always">block11</fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+      <fo:page-sequence master-reference="normal">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block>block21</fo:block>
+          <fo:block>block22</fo:block>
+          <fo:block>block23</fo:block>
+          <fo:block keep-with-previous.within-column="always">block24</fo:block>
+          <fo:block keep-with-previous.within-column="always">block25</fo:block>
+          <fo:block>
+            <fo:block>block26</fo:block>
+            <fo:block>block27</fo:block>
+            <fo:block>block28</fo:block>
+            <fo:block>block29</fo:block>
+            <fo:block>block30</fo:block>
+          </fo:block>
+          <fo:block keep-with-previous.within-column="always">block31</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="2" xpath="//lineArea[starts-with(., 'block3')]/ancestor::pageViewport/@nr"/>
+    <eval expected="2" xpath="//lineArea[starts-with(., 'block4')]/ancestor::pageViewport/@nr"/>
+    <eval expected="2" xpath="//lineArea[starts-with(., 'block5')]/ancestor::pageViewport/@nr"/>
+    <eval expected="2" xpath="//lineArea[starts-with(., 'block6')]/ancestor::pageViewport/@nr"/>
+    <eval expected="3" xpath="//lineArea[starts-with(., 'block7')]/ancestor::pageViewport/@nr"/>
+    <eval expected="3" xpath="//lineArea[starts-with(., 'block8')]/ancestor::pageViewport/@nr"/>
+    <eval expected="3" xpath="//lineArea[starts-with(., 'block9')]/ancestor::pageViewport/@nr"/>
+    <eval expected="4" xpath="//lineArea[starts-with(., 'block10')]/ancestor::pageViewport/@nr"/>
+    <eval expected="4" xpath="//lineArea[starts-with(., 'block11')]/ancestor::pageViewport/@nr"/>
+
+    <eval expected="5" xpath="//lineArea[starts-with(., 'block21')]/ancestor::pageViewport/@nr"/>
+    <eval expected="5" xpath="//lineArea[starts-with(., 'block22')]/ancestor::pageViewport/@nr"/>
+    <eval expected="6" xpath="//lineArea[starts-with(., 'block23')]/ancestor::pageViewport/@nr"/>
+    <eval expected="6" xpath="//lineArea[starts-with(., 'block24')]/ancestor::pageViewport/@nr"/>
+    <eval expected="6" xpath="//lineArea[starts-with(., 'block25')]/ancestor::pageViewport/@nr"/>
+    <eval expected="6" xpath="//lineArea[starts-with(., 'block26')]/ancestor::pageViewport/@nr"/>
+    <eval expected="7" xpath="//lineArea[starts-with(., 'block27')]/ancestor::pageViewport/@nr"/>
+    <eval expected="7" xpath="//lineArea[starts-with(., 'block28')]/ancestor::pageViewport/@nr"/>
+    <eval expected="7" xpath="//lineArea[starts-with(., 'block29')]/ancestor::pageViewport/@nr"/>
+    <eval expected="8" xpath="//lineArea[starts-with(., 'block30')]/ancestor::pageViewport/@nr"/>
+    <eval expected="8" xpath="//lineArea[starts-with(., 'block31')]/ancestor::pageViewport/@nr"/>
+  </checks>
+</testcase>
diff --git a/test/layoutengine/testcases/multi-column1.xml b/test/layoutengine/testcases/multi-column1.xml
new file mode 100644 (file)
index 0000000..af17412
--- /dev/null
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright 2005 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+  <info>
+    <p>
+      This test checks multi-column documents.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="normal" page-width="360pt" page-height="4in">
+          <fo:region-body column-count="2" column-gap="20pt"/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="normal" white-space-collapse="true">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block>
+            <fo:block>line1</fo:block>
+            <fo:block>line2</fo:block>
+            <fo:block background-color="orange">line3</fo:block>
+            <fo:block>line4</fo:block>
+            <fo:block>line5</fo:block>
+            <fo:block>line6</fo:block>
+            <fo:block>line7</fo:block>
+            <fo:block>line8</fo:block>
+            <fo:block>line9</fo:block>
+            <fo:block>line10</fo:block>
+            <fo:block>line11</fo:block>
+            <fo:block>line12</fo:block>
+            <fo:block>line13</fo:block>
+            <fo:block>line14</fo:block>
+            <fo:block>line15</fo:block>
+            <fo:block>line16</fo:block>
+            <fo:block>line17</fo:block>
+            <fo:block>line18</fo:block>
+            <fo:block>line19</fo:block>
+            <fo:block>line20</fo:block>
+            <fo:block>line21</fo:block>
+            <fo:block>line22</fo:block>
+            <fo:block>line23</fo:block>
+            <fo:block>line24</fo:block>
+            <fo:block>line25</fo:block>
+            <fo:block>line26</fo:block>
+            <fo:block>line27</fo:block>
+            <fo:block>line28</fo:block>
+            <fo:block>line29</fo:block>
+            <fo:block>line30</fo:block>
+          </fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="1" xpath="count(//pageViewport)"/>
+  </checks>
+</testcase>
diff --git a/test/layoutengine/testcases/multi-column2.xml b/test/layoutengine/testcases/multi-column2.xml
new file mode 100644 (file)
index 0000000..a05d515
--- /dev/null
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright 2005 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+  <info>
+    <p>
+      This test checks multi-column documents.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="normal" page-width="320pt" page-height="4in">
+          <fo:region-body column-count="3" column-gap="10pt"/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="normal" white-space-collapse="true">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block>
+            <fo:block>line1</fo:block>
+            <fo:block>line2</fo:block>
+            <fo:block>line3</fo:block>
+            <fo:block>line4</fo:block>
+            <fo:block>line5</fo:block>
+            <fo:block>line6</fo:block>
+            <fo:block>line7</fo:block>
+            <fo:block>line8</fo:block>
+            <fo:block>line9</fo:block>
+            <fo:block>line10</fo:block>
+            <fo:block>line11</fo:block>
+            <fo:block>line12</fo:block>
+            <fo:block>line13</fo:block>
+          </fo:block>
+          <fo:block span="all">
+            <fo:block>This line is spanned over all columns.</fo:block>
+            <fo:block>This line is spanned over all columns.</fo:block>
+            <fo:block>This line is spanned over all columns.</fo:block>
+            <fo:block>This line is spanned over all columns.</fo:block>
+          </fo:block>
+          <fo:block>
+            <fo:block>line1</fo:block>
+            <fo:block>line2</fo:block>
+            <fo:block>line3</fo:block>
+            <fo:block>line4</fo:block>
+            <fo:block>line5</fo:block>
+            <fo:block>line6</fo:block>
+            <fo:block>line7</fo:block>
+            <fo:block>line8</fo:block>
+            <fo:block>line9</fo:block>
+            <fo:block>line10</fo:block>
+            <fo:block>line11</fo:block>
+            <fo:block>line12</fo:block>
+            <fo:block>line13</fo:block>
+          </fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="1" xpath="count(//pageViewport)"/>
+  </checks>
+</testcase>
index 1eda8648d3779e8e26fd2323157328fb8bc119e4..a0fd0b7616b95fda353be17b98519b6af9fb0631 100644 (file)
@@ -29,8 +29,8 @@
           <fo:region-before background-color="lightgray" extent="20pt"/>
         </fo:simple-page-master>
         <fo:simple-page-master master-name="master2" page-width="5in" page-height="5in" margin-left="10pt" margin-top="20pt" margin-right="30pt" margin-bottom="40pt" background-color="rgb(100%, 90%, 90%)" reference-orientation="90">
-          <fo:region-body background-color="rgb(100%, 80%, 80%)" margin-top="20pt"/>
-          <fo:region-before background-color="lightgray" extent="20pt" margin-left="50pt"/>
+          <fo:region-body background-color="rgb(100%, 80%, 80%)" margin-top="35pt"/>
+          <fo:region-before background-color="lightgray" extent="20pt" margin-left="50pt" overflow="hidden"/>
         </fo:simple-page-master>
       </fo:layout-master-set>
       <fo:page-sequence master-reference="master1" white-space-collapse="true">
         </fo:flow>
       </fo:page-sequence>
       <fo:page-sequence master-reference="master2" white-space-collapse="true">
-        <fo:static-content flow-name="xsl-region-before">
-          <fo:block>region-before</fo:block>
+        <fo:static-content flow-name="xsl-region-before" widows="0" orphans="0">
+          <fo:block>region-before1</fo:block>
+          <fo:block>region-before2</fo:block>
+          <fo:block>region-before3</fo:block>
         </fo:static-content>
         <fo:flow flow-name="xsl-region-body">
           <fo:block>testing margin with reference-orientation="90"</fo:block>
@@ -66,8 +68,8 @@
     <eval expected="20000" xpath="//pageViewport[2]/page/regionViewport[1]/regionBefore/@bpda"/>
     <eval expected="300000" xpath="//pageViewport[2]/page/regionViewport[1]/regionBefore/block[1]/@ipda"/>
     <eval expected="14400" xpath="//pageViewport[2]/page/regionViewport[1]/regionBefore/block[1]/@bpda"/>
-    <eval expected="30000 20000 300000 300000" xpath="//pageViewport[2]/page/regionViewport[2]/@rect"/>
+    <eval expected="45000 20000 285000 300000" xpath="//pageViewport[2]/page/regionViewport[2]/@rect"/>
     <eval expected="300000" xpath="//pageViewport[2]/page/regionViewport[2]/regionBody/@ipda"/>
-    <eval expected="300000" xpath="//pageViewport[2]/page/regionViewport[2]/regionBody/@bpda"/>
+    <eval expected="285000" xpath="//pageViewport[2]/page/regionViewport[2]/regionBody/@bpda"/>
   </checks>
 </testcase>
diff --git a/test/layoutengine/testcases/page-master4.xml b/test/layoutengine/testcases/page-master4.xml
new file mode 100644 (file)
index 0000000..0aed299
--- /dev/null
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Copyright 2005 The Apache Software Foundation
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+  <info>
+    <p>
+      This test checks page-masters (all regions).
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="master1" page-width="5in" page-height="5in" background-color="rgb(100%, 90%, 90%)">
+          <fo:region-body background-color="rgb(100%, 100%, 80%)" margin="30pt" display-align="fill"/>
+          <fo:region-before background-color="rgb(100%, 80%, 80%)" extent="20pt"/>
+          <fo:region-after background-color="rgb(80%, 80%, 80%)" extent="20pt" precedence="true" display-align="after"/>
+          <fo:region-start background-color="rgb(80%, 100%, 80%)" extent="20pt"/>
+          <fo:region-end background-color="rgb(80%, 80%, 100%)" extent="20pt" reference-orientation="270"/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="master1" white-space-collapse="true" orphans="0" widows="0">
+        <fo:static-content flow-name="xsl-region-before">
+          <fo:block>region-before1</fo:block>
+          <fo:block>region-before2</fo:block>
+          <fo:block>region-before3</fo:block>
+        </fo:static-content>
+        <fo:static-content flow-name="xsl-region-after">
+          <fo:block font-size="50%">region-after1 with precedence and display-align="after"</fo:block>
+        </fo:static-content>
+        <fo:static-content flow-name="xsl-region-start">
+          <fo:block>region-start1</fo:block>
+          <fo:block>region-start2</fo:block>
+          <fo:block>region-start3</fo:block>
+        </fo:static-content>
+        <fo:static-content flow-name="xsl-region-end">
+          <fo:block>region-end1</fo:block>
+          <fo:block>region-end2</fo:block>
+          <fo:block>region-end3</fo:block>
+        </fo:static-content>
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block>testing all-regions1</fo:block>
+          <fo:block>testing all-regions2</fo:block>
+          <fo:block>testing all-regions3</fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+  </checks>
+</testcase>