]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
The RowBorder3 example fully works now except for the empty cells currently not being...
authorJeremias Maerki <jeremias@apache.org>
Wed, 11 May 2005 15:13:49 +0000 (15:13 +0000)
committerJeremias Maerki <jeremias@apache.org>
Wed, 11 May 2005 15:13:49 +0000 (15:13 +0000)
EffRow is now in its own file.
Stepping algorithm is now extracted into the new class TableStepper.
Element generation now calculates with borders (separate border model only at this time) and headers/footers.

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

src/java/org/apache/fop/layoutmgr/table/Cell.java
src/java/org/apache/fop/layoutmgr/table/EffRow.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java
src/java/org/apache/fop/layoutmgr/table/TableRowIterator.java
src/java/org/apache/fop/layoutmgr/table/TableStepper.java [new file with mode: 0644]

index d899116c8fa770c702e96737158610903b9622d9..ca8475ba5f7e4823394f9d82c7b4530a46073e9a 100644 (file)
@@ -42,6 +42,7 @@ 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;
@@ -91,6 +92,10 @@ public class Cell extends BlockStackingLayoutManager implements BlockLevelLayout
         return this.fobj;
     }
     
+    private boolean isSeparateBorderModel() {
+        return fobj.isSeparateBorderModel();
+    }
+    
     /**
      * @see org.apache.fop.layoutmgr.AbstractLayoutManager#initProperties()
      */
@@ -99,7 +104,7 @@ public class Cell extends BlockStackingLayoutManager implements BlockLevelLayout
         borderAndPaddingBPD = 0;
         borderAndPaddingBPD += fobj.getCommonBorderPaddingBackground().getBorderBeforeWidth(false);
         borderAndPaddingBPD += fobj.getCommonBorderPaddingBackground().getBorderAfterWidth(false);
-        if (!fobj.isSeparateBorderModel()) {
+        if (!isSeparateBorderModel()) {
             borderAndPaddingBPD /= 2;
         }
         borderAndPaddingBPD += fobj.getCommonBorderPaddingBackground().getPaddingBefore(false);
@@ -135,7 +140,7 @@ public class Cell extends BlockStackingLayoutManager implements BlockLevelLayout
         endBorderWidth += startEndBorderWidths[1];
         iIndents += startBorderWidth;
         iIndents += endBorderWidth;
-        if (!fobj.isSeparateBorderModel()) {
+        if (!isSeparateBorderModel()) {
             iIndents /= 2;
         }
         iIndents += fobj.getCommonBorderPaddingBackground().getPaddingStart(false);
@@ -155,7 +160,7 @@ public class Cell extends BlockStackingLayoutManager implements BlockLevelLayout
         referenceIPD = context.getRefIPD(); 
         cellIPD = referenceIPD;
         cellIPD -= getIPIndents();
-        if (fobj.isSeparateBorderModel()) {
+        if (isSeparateBorderModel()) {
             int borderSep = fobj.getBorderSeparation().getLengthPair()
                     .getIPD().getLength().getValue();
             cellIPD -= borderSep;
@@ -396,7 +401,12 @@ public class Cell extends BlockStackingLayoutManager implements BlockLevelLayout
 
     private int getContentHeight(int rowHeight, GridUnit gu) {
         int bpd = rowHeight;
-        bpd -= gu.getPrimary().getHalfMaxBorderWidth();
+        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);
@@ -422,14 +432,13 @@ public class Cell extends BlockStackingLayoutManager implements BlockLevelLayout
             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[] {
                     gridUnit.getFlag(GridUnit.FIRST_IN_TABLE), 
                     gridUnit.getFlag(GridUnit.LAST_IN_TABLE),
@@ -457,10 +466,15 @@ public class Cell extends BlockStackingLayoutManager implements BlockLevelLayout
                         block.setPositioning(Block.ABSOLUTE);
 
                         int bpd = getContentHeight(rowHeight, gu);
-                        bpd += gridUnit.getHalfMaxBeforeBorderWidth() 
-                                - (gu.getBorders().getBorderBeforeWidth(false) / 2);
-                        bpd += gridUnit.getHalfMaxAfterBorderWidth() 
-                                - (gu.getBorders().getBorderAfterWidth(false) / 2);
+                        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;
@@ -471,7 +485,7 @@ public class Cell extends BlockStackingLayoutManager implements BlockLevelLayout
                         block.setIPD(ipd);
                         block.setXOffset(dx + borderStartWidth);
                         int halfCollapsingBorderHeight = 0;
-                        if (!fobj.isSeparateBorderModel()) {
+                        if (!isSeparateBorderModel()) {
                             halfCollapsingBorderHeight += 
                                 gu.getBorders().getBorderBeforeWidth(false) / 2;
                         }
@@ -530,26 +544,28 @@ public class Cell extends BlockStackingLayoutManager implements BlockLevelLayout
             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()) {
+            int borderAdjust = 0;
+            if (!isSeparateBorderModel()) {
                 if (gridUnit.hasSpanning()) {
-                    halfCollapsingBorderHeight -= gridUnit.getHalfMaxBeforeBorderWidth();
+                    borderAdjust -= gridUnit.getHalfMaxBeforeBorderWidth();
                 } else {
-                    halfCollapsingBorderHeight += gridUnit.getHalfMaxBeforeBorderWidth();
+                    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();
 
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..6580cba
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * 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 = new MinOptMax(0);
+    
+    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 height) {
+        this.height = height;
+    }
+    
+    public List getGridUnits() {
+        return gridUnits;
+    }
+    
+    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(gridUnits.size()).append(" gu");
+        sb.append("}");
+        return sb.toString();
+    }
+}
\ No newline at end of file
index e306f64a61e6c10f63aa15486cc4e3a53120aa62..3ca40b0874baaca8542f1828ebf8bbdf66a4d154 100644 (file)
@@ -23,6 +23,7 @@ import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.ListIterator;
+import java.util.Map;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -89,6 +90,11 @@ public class TableContentLayoutManager {
         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
      */
@@ -96,21 +102,41 @@ public class TableContentLayoutManager {
         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());
+        log.debug("==> Columns: " + getTableLM().getColumns());
         KnuthBox headerAsFirst = null;
         KnuthBox headerAsSecondToLast = null;
         KnuthBox footerAsLast = null;
         if (headerIter != null) {
             this.headerList = getKnuthElementsForRowIterator(
-                    headerIter, context, alignment, true);
+                    headerIter, context, alignment, TableRowIterator.HEADER);
             removeLegalBreaks(this.headerList);
             this.headerNetHeight = calcCellHeightFromContents(this.headerList);
             if (log.isDebugEnabled()) {
-                log.debug("Header: " + headerNetHeight + " - " + this.headerList);
+                log.debug("==> Header: " + headerNetHeight + " - " + this.headerList);
             }
             TableHeaderFooterPosition pos = new TableHeaderFooterPosition(
                     getTableLM(), true, this.headerList);
@@ -126,11 +152,11 @@ public class TableContentLayoutManager {
         }
         if (footerIter != null) {
             this.footerList = getKnuthElementsForRowIterator(
-                    footerIter, context, alignment, true);
+                    footerIter, context, alignment, TableRowIterator.FOOTER);
             removeLegalBreaks(this.footerList);
             this.footerNetHeight = calcCellHeightFromContents(this.footerList);
             if (log.isDebugEnabled()) {
-                log.debug("Footer: " + footerNetHeight + " - " + this.footerList);
+                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
@@ -142,7 +168,7 @@ public class TableContentLayoutManager {
             }
         }
         LinkedList returnList = getKnuthElementsForRowIterator(
-                trIter, context, alignment, false);
+                trIter, context, alignment, TableRowIterator.BODY);
         if (headerAsFirst != null) {
             returnList.add(0, headerAsFirst);
         } else if (headerAsSecondToLast != null) {
@@ -181,16 +207,18 @@ public class TableContentLayoutManager {
      * @param iter TableRowIterator instance to fetch rows from
      * @param context Active LayoutContext
      * @param alignment alignment indicator
-     * @param isHeaderFooter true if currently handling headers/footers
+     * @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, boolean isHeaderFooter) {
+            LayoutContext context, int alignment, int bodyType) {
         LinkedList returnList = new LinkedList();
-        TableRowIterator.EffRow[] rowGroup = null;
+        EffRow[] rowGroup = null;
         while ((rowGroup = iter.getNextRowGroup()) != null) {
-            resolveNormalBeforeAfterBordersForRowGroup(rowGroup, iter);
-            createElementsForRowGroup(context, alignment, isHeaderFooter, 
+            if (!isSeparateBorderModel()) {
+                resolveNormalBeforeAfterBordersForRowGroup(rowGroup, iter);
+            }
+            createElementsForRowGroup(context, alignment, bodyType, 
                         returnList, rowGroup);
         }
         
@@ -206,12 +234,12 @@ public class TableContentLayoutManager {
      * Resolves normal borders for a row group.
      * @param iter Table row iterator to operate on
      */
-    private void resolveNormalBeforeAfterBordersForRowGroup(TableRowIterator.EffRow[] rowGroup, 
+    private void resolveNormalBeforeAfterBordersForRowGroup(EffRow[] rowGroup, 
             TableRowIterator iter) {
         for (int rgi = 0; rgi < rowGroup.length; rgi++) {
-            TableRowIterator.EffRow row = rowGroup[rgi];
-            TableRowIterator.EffRow prev = iter.getCachedRow(row.getIndex() - 1);
-            TableRowIterator.EffRow next = iter.getCachedRow(row.getIndex() + 1);
+            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();
@@ -314,16 +342,17 @@ public class TableContentLayoutManager {
      * Creates Knuth elements for a row group (see TableRowIterator.getNextRowGroup()).
      * @param context Active LayoutContext
      * @param alignment alignment indicator
-     * @param isHeaderFooter true if currently processing headers/footers
+     * @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, 
-            boolean isHeaderFooter, LinkedList returnList, 
-            TableRowIterator.EffRow[] rowGroup) {
+            int bodyType, LinkedList returnList, 
+            EffRow[] rowGroup) {
         log.debug("Handling row group with " + rowGroup.length + " rows...");
         MinOptMax[] rowHeights = new MinOptMax[rowGroup.length];
-        TableRowIterator.EffRow row;
+        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];
@@ -334,6 +363,7 @@ public class TableContentLayoutManager {
             int minContentHeight = 0;
             int maxCellHeight = 0;
             for (int j = 0; j < row.getGridUnits().size(); j++) {
+                maxColumnCount = Math.max(maxColumnCount, row.getGridUnits().size());
                 GridUnit gu = (GridUnit)row.getGridUnits().get(j);
                 if ((gu.isPrimary() || (gu.getColSpanIndex() == 0 && gu.isLastGridUnitRowSpan())) 
                         && !gu.isEmpty()) {
@@ -343,7 +373,7 @@ public class TableContentLayoutManager {
                         primary.getCellLM().setParent(tableLM);
                      
                         //Determine the table-row if any
-                        if (tableRow == null) {
+                        if (tableRow == null && primary.getRow() != null) {
                             tableRow = primary.getRow();
                             
                             //Check for bpd on row, see CSS21, 17.5.3 Table height algorithms
@@ -387,13 +417,19 @@ public class TableContentLayoutManager {
                         }
                         effCellContentHeight = Math.max(effCellContentHeight, 
                                 primary.getContentLength());
-                        int halfMaxBorderWidths = primary.getHalfMaxBorderWidth();
+                        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 + halfMaxBorderWidths;
+                        int effRowHeight = effCellContentHeight + padding + borderWidths;
                         for (int previous = 0; previous < gu.getRowSpanIndex(); previous++) {
                             effRowHeight -= rowHeights[rgi - previous - 1].opt;
                         }
@@ -414,185 +450,27 @@ public class TableContentLayoutManager {
             
             PrimaryGridUnit[] pguArray = new PrimaryGridUnit[pgus.size()];
             pguArray = (PrimaryGridUnit[])pgus.toArray(pguArray);
-            
+
+            /*
             LinkedList returnedList = getCombinedKnuthElementsForRow(pguArray, row, 
                     isHeaderFooter);
             if (returnedList != null) {
                 returnList.addAll(returnedList);
-            }
+            }*/
         }
         if (log.isDebugEnabled()) {
             log.debug("rowGroup:");
-            int totalHeight = 0;
             for (int i = 0; i < rowHeights.length; i++) {
-                totalHeight += rowHeights[i].opt;
                 log.debug("  " + rowHeights[i]);
             }
-            log.debug("  totalHeigth=" + totalHeight);
         }
-    }
-
-    private LinkedList getCombinedKnuthElementsForRow(PrimaryGridUnit[] pguArray, 
-            TableRowIterator.EffRow row, boolean isHeaderFooter) {
-        List[] elementLists = new List[pguArray.length];
-        for (int i = 0; i < pguArray.length; i++) {
-            if (pguArray[i].hasBPD()) {
-                List list = new java.util.ArrayList(1);
-                list.add(new KnuthBoxCellWithBPD(
-                        pguArray[i].getEffectiveContentLength(), pguArray[i]));
-                elementLists[i] = list;
-            } else {
-                //Copy elements (LinkedList) to array lists to improve element access performance
-                elementLists[i] = new java.util.ArrayList(pguArray[i].getElements());
-            }
+        TableStepper stepper = new TableStepper(this);
+        LinkedList returnedList = stepper.getCombinedKnuthElementsForRowGroup(
+                rowGroup, maxColumnCount, bodyType);
+        if (returnedList != null) {
+            returnList.addAll(returnedList);
         }
-        int[] index = new int[pguArray.length];
-        int[] start = new int[pguArray.length];
-        int[] end = new int[pguArray.length];
-        int[] widths = new int[pguArray.length];
-        int[] fullWidths = new int[pguArray.length];
-        Arrays.fill(end, -1);
         
-        int totalHeight = 0;
-        for (int i = 0; i < pguArray.length; i++) {
-            fullWidths[i] = pguArray[i].getContentLength();
-            totalHeight = Math.max(totalHeight, fullWidths[i]);
-        }
-        int laststep = 0;
-        int step;
-        int addedBoxLen = 0;
-        LinkedList returnList = new LinkedList();
-        while ((step = getNextStep(laststep, elementLists, index, start, end, 
-                widths, fullWidths)) > 0) {
-            int increase = step - laststep;
-            int penaltyLen = step + getMaxRemainingHeight(fullWidths, widths) - totalHeight;
-            int effPenaltyLen = penaltyLen;
-            int boxLen = step - addedBoxLen - penaltyLen;
-            addedBoxLen += boxLen;
-            
-            log.debug("step=" + step + " (+" + increase + ")"
-                    + " box=" + boxLen 
-                    + " penalty=" + penaltyLen
-                    + " effPenalty=" + effPenaltyLen);
-            
-            //Put all involved grid units into a list
-            List gridUnitParts = new java.util.ArrayList(pguArray.length);
-            for (int i = 0; i < pguArray.length; i++) {
-                if (end[i] >= start[i]) {
-                    if (start[i] == 0 && end[i] == 0 
-                            && elementLists[i].size() == 1
-                            && elementLists[i].get(0) instanceof KnuthBoxCellWithBPD) {
-                        gridUnitParts.add(new GridUnitPart(pguArray[i], 
-                                0, pguArray[i].getElements().size() - 1));
-                    } else {
-                        gridUnitParts.add(new GridUnitPart(pguArray[i], start[i], end[i]));
-                    }
-                }
-            }
-            
-            //Create elements for step
-            TableContentPosition tcpos = new TableContentPosition(getTableLM(), 
-                    gridUnitParts, row);
-            returnList.add(new KnuthBox(boxLen, tcpos, false));
-            TableHFPenaltyPosition penaltyPos = new TableHFPenaltyPosition(getTableLM());
-            if (!isHeaderFooter) {
-                if (!getTableLM().getTable().omitHeaderAtBreak()) {
-                    penaltyLen += this.headerNetHeight;
-                    penaltyPos.headerElements = this.headerList;
-                }
-                if (!getTableLM().getTable().omitFooterAtBreak()) {
-                    penaltyLen += this.footerNetHeight;
-                    penaltyPos.footerElements = this.footerList;
-                }
-            }
-            returnList.add(new KnuthPenalty(penaltyLen, 0, false, penaltyPos, false));
-            laststep = step;
-        }
-        return returnList;
-    }
-
-    private int getMaxRemainingHeight(int[] fullWidths, int[] widths) {
-        int maxW = 0;
-        for (int i = 0; i < fullWidths.length; i++) {
-            maxW = Math.max(maxW, fullWidths[i] - widths[i]);
-        }
-        return maxW;
-    }
-    
-    private int getNextStep(int laststep, List[] elementLists, int[] index, 
-            int[] start, int[] end, int[] widths, int[] fullWidths) {
-        int[] backupWidths = new int[start.length];
-        System.arraycopy(widths, 0, backupWidths, 0, backupWidths.length);
-        //set starting points
-        for (int i = 0; i < start.length; i++) {
-            if (end[i] < elementLists[i].size()) {
-                start[i] = end[i] + 1;
-            } else {
-                start[i] = -1; //end of list reached
-                end[i] = -1;
-            }
-        }
-        
-        //Get next possible sequence for each cell
-        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;
-                    }
-                    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 (seqCount == 0) {
-            return 0;
-        }
-        
-        //Determine smallest possible step
-        int minStep = Integer.MAX_VALUE;
-        for (int i = 0; i < widths.length; i++) {
-            if (end[i] >= start[i]) {
-                minStep = Math.min(widths[i], minStep);
-            }
-        }
-
-        //Reset bigger-than-minimum sequences
-        for (int i = 0; i < widths.length; i++) {
-            if (widths[i] > minStep) {
-                widths[i] = backupWidths[i];
-                end[i] = start[i] - 1;
-            }
-        }
-        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;
     }
 
     private int calcCellHeightFromContents(List elems, int start, int end) {
@@ -606,7 +484,7 @@ public class TableContentLayoutManager {
             } else if (el.isGlue()) {
                 len += el.getW();
             } else {
-                log.debug("Ignoring penalty: " + el);
+                //log.debug("Ignoring penalty: " + el);
                 //ignore penalties
             }
             count--;
@@ -631,6 +509,7 @@ public class TableContentLayoutManager {
         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();
@@ -651,13 +530,10 @@ public class TableContentLayoutManager {
                             log.debug("Ignoring position: " + containedPos);
                         }
                     }
+                    painter.addAreasAndFlushRow(true);
                 } else {
                     //Positions for footers are simply added at the end
-                    PositionIterator iter = new KnuthPossPosIter(thfpos.nestedElements);
-                    while (iter.hasNext()) {
-                        Position containedPos = (Position)iter.next();
-                        positions.add(containedPos);
-                    }
+                    footerElements = thfpos.nestedElements;
                 }
             } else if (pos instanceof TableHFPenaltyPosition) {
                 //ignore for now, see special handling below if break is at a penalty
@@ -683,14 +559,10 @@ public class TableContentLayoutManager {
                         log.debug("Ignoring position: " + containedPos);
                     }
                 }
+                painter.addAreasAndFlushRow(true);
             }
             if (penaltyPos.footerElements != null) {
-                //Positions for footers are simply added at the end
-                PositionIterator iter = new KnuthPossPosIter(penaltyPos.footerElements);
-                while (iter.hasNext()) {
-                    Position containedPos = (Position)iter.next();
-                    positions.add(containedPos);
-                }
+                footerElements = penaltyPos.footerElements; 
             }
         }
 
@@ -706,9 +578,24 @@ public class TableContentLayoutManager {
                 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);
+        }
         
-        //Calculate the height of the row
-        int maxLen = painter.addAreasAndFlushRow(true);
+        painter.notifyEndOfSequence();
         this.usedBPD += painter.getAccumulatedBPD();
     }
    
@@ -718,9 +605,12 @@ public class TableContentLayoutManager {
         private int colCount = getColumns().getColumnCount();
         private int yoffset = 0;
         private int accumulatedBPD = 0;
-        private TableRowIterator.EffRow lastRow = null;
+        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];
@@ -730,17 +620,24 @@ public class TableContentLayoutManager {
         
         public RowPainter(LayoutContext layoutContext) {
             this.layoutContext = layoutContext;
+            Arrays.fill(firstRow, -1);
         }
         
         public int getAccumulatedBPD() {
             return this.accumulatedBPD;
         }
         
-        private void handleTableContentPosition(TableContentPosition tcpos) {
+        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) {
-                yoffset += lastRow.getHeight().opt;
-                this.accumulatedBPD += lastRow.getHeight().opt;
+                addAreasAndFlushRow(false);
+                yoffset += lastRowHeight;
+                this.accumulatedBPD += lastRowHeight;
             }
             lastRow = tcpos.row;
             Iterator partIter = tcpos.gridUnitParts.iterator();
@@ -761,81 +658,98 @@ public class TableContentLayoutManager {
                     rowFO = gridUnits[colIndex].getRow();
                 }
             }
-            
-            //Calculate the height of the row
-            int maxLen = addAreasAndFlushRow(false);
-            lastRowHeight = tcpos.row.getHeight().opt;
         }
         
-        private int addAreasAndFlushRow(boolean finalFlush) {
-            int maxLen = 0;
+        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) 
-                        && (finalFlush || (end[i] == gridUnits[i].getElements().size() - 1))) {
+                        && (forcedFlush || (end[i] == gridUnits[i].getElements().size() - 1))) {
                     log.debug("getting len for " + i + " " 
                             + start[i] + "-" + end[i]);
+                    readyCount++;
                     int len = calcCellHeightFromContents(
                             gridUnits[i].getElements(), start[i], end[i]);
                     partLength[i] = len;
                     log.debug("len of part: " + len);
-                    maxLen = Math.max(maxLen, len);
-                    //maxLen = Math.max(maxLen, getExplicitCellHeight(gridUnits[i]));
+                    
+                    //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, lastRowHeight, layoutContext.getRefIPD(), yoffset);
             for (int i = 0; i < gridUnits.length; i++) {
                 if ((gridUnits[i] != null) 
-                        && (finalFlush || (end[i] == gridUnits[i].getElements().size() - 1))) {
+                        && (forcedFlush || (end[i] == gridUnits[i].getElements().size() - 1))) {
                     if (log.isDebugEnabled()) {
-                        log.debug((finalFlush ? "final " : "") + "flushing..." + i + " " 
+                        log.debug((forcedFlush ? "FORCED " : "") + "flushing..." + i + " " 
                                 + start[i] + "-" + end[i]);
                     }
                     addAreasForCell(gridUnits[i], start[i], end[i], 
                             layoutContext, lastRow, yoffset, 
-                            partLength[i], lastRow.getHeight().opt);
+                            partLength[i], actualRowHeight);
                     gridUnits[i] = null;
                     start[i] = 0;
                     end[i] = 0;
+                    partLength[i] = 0;
                 }
             }
-            if (finalFlush) {
-                this.accumulatedBPD += lastRowHeight; //for last row
-            }
-            return maxLen;
+            return actualRowHeight;
         }
 
-    }
-
-    /*
-    private int getExplicitCellHeight(PrimaryGridUnit pgu) {
-        int len = 0;
-        if (!pgu.getCell().getBlockProgressionDimension().getOptimum().isAuto()) {
-            len = pgu.getCell().getBlockProgressionDimension()
-                    .getOptimum().getLength().getValue();
+        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);
         }
-        if (pgu.getRow() != null 
-                && !pgu.getRow().getBlockProgressionDimension().getOptimum().isAuto()) {
-            len = Math.max(len, pgu.getRow().getBlockProgressionDimension()
-                    .getOptimum().getLength().getValue());
-        }
-        return len;
-    }*/
-    
-    private void addAreasForCell(PrimaryGridUnit gu, int start, int end, 
-            LayoutContext layoutContext, TableRowIterator.EffRow row, 
-            int yoffset, int contentHeight, int rowHeight) {
-        Cell cellLM = gu.getCellLM();
-        cellLM.setXOffset(getXOffsetOfGridUnit(gu));
-        cellLM.setYOffset(yoffset);
-        cellLM.setContentHeight(contentHeight);
-        cellLM.setRowHeight(rowHeight);
-        //cellLM.setRowHeight(row.getHeight().opt);
-        cellLM.addAreas(new KnuthPossPosIter(gu.getElements(), 
-                start, end + 1), layoutContext);
+        
     }
-    
+
     /**
      * Get the area for a row for background.
      * @param row the table-row object or null
@@ -878,7 +792,7 @@ public class TableContentLayoutManager {
         return this.usedBPD;
     }
     
-    private class GridUnitPart {
+    protected static class GridUnitPart {
         
         protected PrimaryGridUnit pgu;
         protected int start;
@@ -900,13 +814,13 @@ public class TableContentLayoutManager {
         
     }
     
-    public class TableContentPosition extends Position {
+    public static class TableContentPosition extends Position {
 
         protected List gridUnitParts;
-        protected TableRowIterator.EffRow row;
+        protected EffRow row;
         
         protected TableContentPosition(LayoutManager lm, List gridUnitParts, 
-                TableRowIterator.EffRow row) {
+                EffRow row) {
             super(lm);
             this.gridUnitParts = gridUnitParts;
             this.row = row;
@@ -921,7 +835,7 @@ public class TableContentLayoutManager {
         }
     }
     
-    public class TableHeaderFooterPosition extends Position {
+    public static class TableHeaderFooterPosition extends Position {
         
         protected boolean header;
         protected List nestedElements;
@@ -944,7 +858,7 @@ public class TableContentLayoutManager {
         }
     }
 
-    public class TableHFPenaltyPosition extends Position {
+    public static class TableHFPenaltyPosition extends Position {
         
         protected List headerElements;
         protected List footerElements;
@@ -974,6 +888,6 @@ public class TableContentLayoutManager {
             super(w, null, true);
             this.pgu = pgu;
         }
-}
+    }
 
 }
index edc1f85de5ea743455452e4563bd8edce4e06500..23b7032b2b54f14ba80e9256c58931efb953bdbf 100644 (file)
@@ -211,9 +211,10 @@ public class TableRowIterator {
         Object node = childInBodyIterator.next();
         this.currentRow.clear();
         this.currentRowIndex++;
+        TableRow rowFO = null;
         if (node instanceof TableRow) {
-            TableRow row = (TableRow)node;
-            ListIterator cellIterator = row.getChildNodes();
+            rowFO = (TableRow)node;
+            ListIterator cellIterator = rowFO.getChildNodes();
             while (cellIterator.hasNext()) {
                 this.currentRow.add(cellIterator.next());
             }
@@ -236,7 +237,7 @@ public class TableRowIterator {
         } else {
             throw new IllegalStateException("Illegal class found: " + node.getClass().getName());
         }
-        EffRow gridUnits = buildGridRow(this.currentRow);
+        EffRow gridUnits = buildGridRow(this.currentRow, rowFO);
         if (firstInBody) {
             gridUnits.setFlagForAllGridUnits(GridUnit.FIRST_IN_BODY, true);
         }
@@ -264,11 +265,10 @@ public class TableRowIterator {
         }
     }
     
-    private EffRow buildGridRow(List cells) {
-        EffRow row = new EffRow(this.currentRowIndex);
+    private EffRow buildGridRow(List cells, TableRow rowFO) {
+        EffRow row = new EffRow(this.currentRowIndex, type);
         List gridUnits = row.getGridUnits();
         
-        TableRow rowFO = null;
         TableBody bodyFO = null;
         
         //Create all row-spanned grid units based on information from the last row
@@ -282,6 +282,7 @@ public class TableRowIterator {
                     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()) {
@@ -348,9 +349,6 @@ public class TableRowIterator {
             }
             
             //Gather info for empty grid units (used later)
-            if (rowFO == null) {
-                rowFO = gu.getRow();
-            }
             if (bodyFO == null) {
                 bodyFO = gu.getBody();
             }
@@ -437,51 +435,5 @@ public class TableRowIterator {
             }
         }
     }
-    
-    public class EffRow {
-        
-        private List gridUnits = new java.util.ArrayList();
-        private int index;
-        private MinOptMax height = new MinOptMax(0);
-        
-        public EffRow(int index) {
-            this.index = index;
-            this.height = height;
-        }
-        
-        public int getIndex() {
-            return this.index;
-        }
-        
-        public MinOptMax getHeight() {
-            return this.height;
-        }
-        
-        public void setHeight(MinOptMax height) {
-            this.height = height;
-        }
-        
-        public List getGridUnits() {
-            return gridUnits;
-        }
-        
-        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);
-            sb.append(", ").append(height);
-            sb.append(", ").append(gridUnits.size()).append(" gu");
-            sb.append("}");
-            return sb.toString();
-        }
-    }
 
 }
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..8a53513
--- /dev/null
@@ -0,0 +1,413 @@
+/*
+ * 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.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 (GridUnit)getActiveRow().getGridUnits().get(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);
+        if (gu.isPrimary() && !gu.isEmpty()) {
+            PrimaryGridUnit pgu = (PrimaryGridUnit)gu;
+            if (pgu.hasBPD()) {
+                List list = new java.util.ArrayList(1);
+                list.add(new KnuthBoxCellWithBPD(
+                        pgu.getEffectiveContentLength(), pgu));
+                elementLists[column] = list;
+            } 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) {
+                        gridUnitParts.add(new GridUnitPart(pgu, 
+                                0, pgu.getElements().size() - 1));
+                    } else {
+                        gridUnitParts.add(new GridUnitPart(pgu, start[i], end[i]));
+                    }
+                } else {
+                    /*
+                    if ((end[i] >= 0)
+                            || (elementLists[i] != null && end[i] < 0)) {
+                        PrimaryGridUnit pgu = getActivePrimaryGridUnit(i);
+                        log.debug(">>>>>" + start[i] + "-" + end[i]);
+                        gridUnitParts.add(new GridUnitPart(pgu, 0, -1));
+                    }*/
+                }
+            }
+            //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++) {
+                    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;
+            if (end[i] >= start[i]) {
+                for (int prevRow = 0; prevRow < startRow[i]; prevRow++) {
+                    baseWidth[i] += rowGroup[prevRow].getHeight().opt;
+                }
+                baseWidth[i] += borderBefore[i] + borderAfter[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;
+        }
+    }
+    
+}