]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
First step at introducing a layout manager for a row-group
authorVincent Hennebert <vhennebert@apache.org>
Thu, 2 Aug 2007 15:37:17 +0000 (15:37 +0000)
committerVincent Hennebert <vhennebert@apache.org>
Thu, 2 Aug 2007 15:37:17 +0000 (15:37 +0000)
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@562140 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java [new file with mode: 0644]
src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java

diff --git a/src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java
new file mode 100644 (file)
index 0000000..c7db197
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* $Id$ */
+
+package org.apache.fop.layoutmgr.table;
+
+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.Constants;
+import org.apache.fop.fo.FONode;
+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.BreakElement;
+import org.apache.fop.layoutmgr.ElementListObserver;
+import org.apache.fop.layoutmgr.ElementListUtils;
+import org.apache.fop.layoutmgr.KnuthElement;
+import org.apache.fop.layoutmgr.KnuthPenalty;
+import org.apache.fop.layoutmgr.LayoutContext;
+import org.apache.fop.layoutmgr.ListElement;
+import org.apache.fop.layoutmgr.MinOptMaxUtil;
+import org.apache.fop.layoutmgr.Position;
+import org.apache.fop.traits.MinOptMax;
+
+class RowGroupLayoutManager {
+
+    private static Log log = LogFactory.getLog(TableContentLayoutManager.class);
+
+    private EffRow[] rowGroup;
+
+    private TableLayoutManager tableLM;
+
+    private TableRowIterator bodyIter;
+    private TableRowIterator headerIter;
+    private TableRowIterator footerIter;
+    private TableRowIterator thisIter;
+    private TableStepper tableStepper;
+
+    RowGroupLayoutManager(TableLayoutManager tableLM, EffRow[] rowGroup, TableRowIterator bodyIter,
+            TableRowIterator headerIter, TableRowIterator footerIter, TableRowIterator thisIter,
+            TableStepper tableStepper) {
+        this.tableLM = tableLM;
+        this.rowGroup = rowGroup;
+        this.bodyIter = bodyIter;
+        this.headerIter = headerIter;
+        this.footerIter = footerIter;
+        this.thisIter = thisIter;
+        this.tableStepper = tableStepper;
+    }
+
+    public LinkedList getNextKnuthElements(LayoutContext context, int alignment, int bodyType) {
+        LinkedList returnList = new LinkedList();
+            //Check for break-before on the table-row at the start of the row group
+            TableRow rowFO = rowGroup[0].getTableRow(); 
+            if (rowFO != null && rowFO.getBreakBefore() != Constants.EN_AUTO) {
+                log.info("break-before found");
+                if (returnList.size() > 0) {
+                    ListElement last = (ListElement)returnList.getLast();
+                    if (last.isPenalty()) {
+                        KnuthPenalty pen = (KnuthPenalty)last;
+                        pen.setP(-KnuthPenalty.INFINITE);
+                        pen.setBreakClass(rowFO.getBreakBefore());
+                    } else {//if (last instanceof BreakElement) { // TODO vh: seems the only possibility
+                        BreakElement breakPoss = (BreakElement) last;
+                        breakPoss.setPenaltyValue(-KnuthPenalty.INFINITE);
+                        breakPoss.setBreakClass(rowFO.getBreakBefore());
+                    }
+                } else {
+                    returnList.add(new BreakElement(new Position(tableLM),
+                            0, -KnuthPenalty.INFINITE, rowFO.getBreakBefore(), context));
+                }
+            }
+            
+            //Border resolution
+            if (!tableLM.getTable().isSeparateBorderModel()) {
+                resolveNormalBeforeAfterBordersForRowGroup();
+            }
+
+            //Reset keep-with-next when remaining inside the table.
+            //The context flag is only used to propagate keep-with-next to the outside.
+            //The clearing is ok here because createElementsForRowGroup already handles
+            //the keep when inside a table.
+            context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING, false);
+            
+            //Element list creation
+            createElementsForRowGroup(context, alignment, bodyType, 
+                        returnList, rowGroup);
+            
+            //Handle keeps
+            if (context.isKeepWithNextPending()) {
+                log.debug("child LM (row group) signals pending keep-with-next");
+            }
+            if (context.isKeepWithPreviousPending()) {
+                log.debug("child LM (row group) signals pending keep-with-previous");
+                if (returnList.size() > 0) {
+                    //Modify last penalty
+                    ListElement last = (ListElement)returnList.getLast();
+                    if (last.isPenalty()) {
+                        BreakElement breakPoss = (BreakElement)last;
+                        //Only honor keep if there's no forced break
+                        if (!breakPoss.isForcedBreak()) {
+                            breakPoss.setPenaltyValue(KnuthPenalty.INFINITE);
+                        }
+                    }
+                }
+            }
+            
+            //Check for break-after on the table-row at the end of the row group
+            rowFO = rowGroup[rowGroup.length - 1].getTableRow(); 
+            if (rowFO != null && rowFO.getBreakAfter() != Constants.EN_AUTO) {
+                if (returnList.size() > 0) {
+                    ListElement last = (ListElement)returnList.getLast();
+                    if (last instanceof KnuthPenalty) {
+                        KnuthPenalty pen = (KnuthPenalty)last;
+                        pen.setP(-KnuthPenalty.INFINITE);
+                        pen.setBreakClass(rowFO.getBreakAfter());
+                    } else if (last instanceof BreakElement) {
+                        BreakElement breakPoss = (BreakElement)last;
+                        breakPoss.setPenaltyValue(-KnuthPenalty.INFINITE);
+                        breakPoss.setBreakClass(rowFO.getBreakAfter());
+                    }
+                }
+            }
+        return returnList;
+    }
+
+    /**
+     * Resolves normal borders for a row group.
+     * @param iter Table row iterator to operate on
+     */
+    private void resolveNormalBeforeAfterBordersForRowGroup() {
+        for (int rgi = 0; rgi < rowGroup.length; rgi++) {
+            EffRow row = rowGroup[rgi];
+            EffRow prevRow = thisIter.getPrecedingRow(row);
+            EffRow nextRow = thisIter.getFollowingRow(row);
+            if ((prevRow == null) && (thisIter == bodyIter) && (headerIter != null)) {
+                prevRow = headerIter.getLastRow();
+            }
+            if ((nextRow == null) && (thisIter == headerIter)) {
+                nextRow = bodyIter.getFirstRow();
+            }
+            if ((nextRow == null) && (thisIter == bodyIter) && (footerIter != null)) {
+                nextRow = footerIter.getFirstRow();
+            }
+            if ((prevRow == null) && (thisIter == footerIter)) {
+                //TODO This could be bad for memory consumption because it already causes the
+                //whole body iterator to be prefetched!
+                prevRow = bodyIter.getLastRow();
+            }
+            log.debug("prevRow-row-nextRow: " + prevRow + " - " + row + " - " + nextRow);
+            
+            //Determine the grid units necessary for getting all the borders right
+            int guCount = row.getGridUnits().size();
+            if (prevRow != null) {
+                guCount = Math.max(guCount, prevRow.getGridUnits().size());
+            }
+            if (nextRow != null) {
+                guCount = Math.max(guCount, nextRow.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 is untested!
+                int pos = row.getGridUnits().size() + i;
+                row.getGridUnits().add(new EmptyGridUnit(gu.getRow(), 
+                        tableLM.getColumns().getColumn(pos + 1), gu.getBody(), 
+                        pos));
+            }
+            
+            //Now resolve normal borders
+            if (tableLM.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 (prevRow != null && i < prevRow.getGridUnits().size()) {
+                        other = prevRow.getGridUnit(i);
+                    } else {
+                        other = null;
+                    }
+                    if (other == null 
+                            || other.isEmpty() 
+                            || gu.isEmpty() 
+                            || gu.getPrimary() != other.getPrimary()) {
+                        if ((thisIter == bodyIter)
+                                && gu.getFlag(GridUnit.FIRST_IN_TABLE)
+                                && (headerIter == null)) {
+                            flags |= CollapsingBorderModel.VERTICAL_START_END_OF_TABLE;
+                        }
+                        if ((thisIter == headerIter)
+                                && gu.getFlag(GridUnit.FIRST_IN_TABLE)) {
+                            flags |= CollapsingBorderModel.VERTICAL_START_END_OF_TABLE;
+                        }
+                        gu.resolveBorder(other, 
+                                CommonBorderPaddingBackground.BEFORE, flags);
+                    }
+                    
+                    flags = 0;
+                    if (nextRow != null && i < nextRow.getGridUnits().size()) {
+                        other = nextRow.getGridUnit(i);
+                    } else {
+                        other = null;
+                    }
+                    if (other == null 
+                            || other.isEmpty() 
+                            || gu.isEmpty() 
+                            || gu.getPrimary() != other.getPrimary()) {
+                        if ((thisIter == bodyIter)
+                                && gu.getFlag(GridUnit.LAST_IN_TABLE)
+                                && (footerIter == null)) {
+                            flags |= CollapsingBorderModel.VERTICAL_START_END_OF_TABLE;
+                        }
+                        if ((thisIter == 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;
+            // The row's minimum content height; 0 if the row's height is auto, otherwise
+            // the .minimum component of the explicitely specified value
+            int minContentHeight = 0;
+            int maxCellHeight = 0;
+            int effRowContentHeight = 0;
+            for (int j = 0; j < row.getGridUnits().size(); j++) {
+//                assert maxColumnCount == 0 || maxColumnCount == row.getGridUnits().size(); // TODO vh
+                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(tableLM).isAuto()) {
+                                minContentHeight = Math.max(
+                                        minContentHeight, 
+                                        bpd.getMinimum(
+                                                tableLM).getLength().getValue(tableLM));
+                            }
+                            MinOptMaxUtil.restrict(explicitRowHeights[rgi], bpd, tableLM);
+                            
+                        }
+
+                        //Calculate width of cell
+                        int spanWidth = 0;
+                        for (int i = primary.getStartCol(); 
+                                i < primary.getStartCol() 
+                                        + primary.getCell().getNumberColumnsSpanned();
+                                i++) {
+                            if (tableLM.getColumns().getColumn(i + 1) != null) {
+                                spanWidth += tableLM.getColumns().getColumn(i + 1)
+                                    .getColumnWidth().getValue(tableLM);
+                            }
+                        }
+                        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);
+                        //Temporary? Multiple calls in case of break conditions.
+                        //TODO Revisit when table layout is restartable
+                        while (!primary.getCellLM().isFinished()) {
+                            LinkedList additionalElems = primary.getCellLM().getNextKnuthElements(
+                                    childLC, alignment);
+                            elems.addAll(additionalElems);
+                        }
+                        ElementListObserver.observe(elems, "table-cell", primary.getCell().getId());
+
+                        if ((elems.size() > 0) 
+                                && ((KnuthElement)elems.getLast()).isForcedBreak()) {
+                            // a descendant of this block has break-after
+                            log.debug("Descendant of table-cell signals break: " 
+                                    + primary.getCellLM().isFinished());
+                        }
+                        
+                        primary.setElements(elems);
+                        
+                        if (childLC.isKeepWithNextPending()) {
+                            log.debug("child LM signals pending keep-with-next");
+                            primary.setFlag(GridUnit.KEEP_WITH_NEXT_PENDING, true);
+                        }
+                        if (childLC.isKeepWithPreviousPending()) {
+                            log.debug("child LM signals pending keep-with-previous");
+                            primary.setFlag(GridUnit.KEEP_WITH_PREVIOUS_PENDING, true);
+                        }
+                    }
+
+                    
+                    //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(tableLM).isAuto()) {
+                            effCellContentHeight = Math.max(
+                                effCellContentHeight,
+                                bpd.getMinimum(tableLM).getLength().getValue(tableLM));
+                        }
+                        if (!bpd.getOptimum(tableLM).isAuto()) {
+                            effCellContentHeight = Math.max(
+                                effCellContentHeight,
+                                bpd.getOptimum(tableLM).getLength().getValue(tableLM));
+                        }
+                        if (gu.getRowSpanIndex() == 0) {
+                            //TODO ATM only non-row-spanned cells are taken for this
+                            MinOptMaxUtil.restrict(explicitRowHeights[rgi], bpd, tableLM);
+                        }
+                        effCellContentHeight = Math.max(effCellContentHeight, 
+                                primary.getContentLength());
+                        
+                        int borderWidths;
+                        if (tableLM.getTable().isSeparateBorderModel()) {
+                            borderWidths = primary.getBorders().getBorderBeforeWidth(false)
+                                    + primary.getBorders().getBorderAfterWidth(false);
+                        } else {
+                            borderWidths = primary.getHalfMaxBorderWidth();
+                        }
+                        int padding = 0;
+                        effRowContentHeight = Math.max(effRowContentHeight,
+                                effCellContentHeight);
+                        CommonBorderPaddingBackground cbpb 
+                            = primary.getCell().getCommonBorderPaddingBackground(); 
+                        padding += cbpb.getPaddingBefore(false, primary.getCellLM());
+                        padding += cbpb.getPaddingAfter(false, primary.getCellLM());
+                        int effRowHeight = effCellContentHeight 
+                                + padding + borderWidths
+                                + 2 * tableLM.getHalfBorderSeparationBPD();
+                        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);
+                        }
+                    }
+                    
+                    if (gu.isPrimary()) {
+                        pgus.add(primary);
+                    }
+                }
+            }
+
+            row.setHeight(rowHeights[rgi]);
+            row.setExplicitHeight(explicitRowHeights[rgi]);
+            if (effRowContentHeight > row.getExplicitHeight().max) {
+                log.warn(FONode.decorateWithContextInfo(
+                        "The contents of row " + (row.getIndex() + 1) 
+                        + " are taller than they should be (there is a"
+                        + " block-progression-dimension or height constraint on the indicated row)."
+                        + " Due to its contents the row grows"
+                        + " to " + effRowContentHeight + " millipoints, but the row shouldn't get"
+                        + " any taller than " + row.getExplicitHeight() + " millipoints.", 
+                        row.getTableRow()));
+            }
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("rowGroup:");
+            for (int i = 0; i < rowHeights.length; i++) {
+                log.debug("  height=" + rowHeights[i] + " explicit=" + explicitRowHeights[i]);
+            }
+        }
+        LinkedList returnedList = tableStepper.getCombinedKnuthElementsForRowGroup(
+                context, rowGroup, maxColumnCount, bodyType);
+        if (returnedList != null) {
+            returnList.addAll(returnedList);
+        }
+        
+    }
+}
index 675e5ce95c1750e0224733c4feaf7447a7ba23ac..37742437cf828ad258f30b8716b853d88ffe9bb9 100644 (file)
@@ -215,77 +215,9 @@ public class TableContentLayoutManager implements PercentBaseContext {
         LinkedList returnList = new LinkedList();
         EffRow[] rowGroup = null;
         while ((rowGroup = iter.getNextRowGroup()) != null) {
-            //Check for break-before on the table-row at the start of the row group
-            TableRow rowFO = rowGroup[0].getTableRow(); 
-            if (rowFO != null && rowFO.getBreakBefore() != Constants.EN_AUTO) {
-                log.info("break-before found");
-                if (returnList.size() > 0) {
-                    ListElement last = (ListElement)returnList.getLast();
-                    if (last.isPenalty()) {
-                        KnuthPenalty pen = (KnuthPenalty)last;
-                        pen.setP(-KnuthPenalty.INFINITE);
-                        pen.setBreakClass(rowFO.getBreakBefore());
-                    } else {//if (last instanceof BreakElement) { // TODO vh: seems the only possibility
-                        BreakElement breakPoss = (BreakElement) last;
-                        breakPoss.setPenaltyValue(-KnuthPenalty.INFINITE);
-                        breakPoss.setBreakClass(rowFO.getBreakBefore());
-                    }
-                } else {
-                    returnList.add(new BreakElement(new Position(getTableLM()),
-                            0, -KnuthPenalty.INFINITE, rowFO.getBreakBefore(), context));
-                }
-            }
-            
-            //Border resolution
-            if (!isSeparateBorderModel()) {
-                resolveNormalBeforeAfterBordersForRowGroup(rowGroup, iter);
-            }
-
-            //Reset keep-with-next when remaining inside the table.
-            //The context flag is only used to propagate keep-with-next to the outside.
-            //The clearing is ok here because createElementsForRowGroup already handles
-            //the keep when inside a table.
-            context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING, false);
-            
-            //Element list creation
-            createElementsForRowGroup(context, alignment, bodyType, 
-                        returnList, rowGroup);
-            
-            //Handle keeps
-            if (context.isKeepWithNextPending()) {
-                log.debug("child LM (row group) signals pending keep-with-next");
-            }
-            if (context.isKeepWithPreviousPending()) {
-                log.debug("child LM (row group) signals pending keep-with-previous");
-                if (returnList.size() > 0) {
-                    //Modify last penalty
-                    ListElement last = (ListElement)returnList.getLast();
-                    if (last.isPenalty()) {
-                        BreakElement breakPoss = (BreakElement)last;
-                        //Only honor keep if there's no forced break
-                        if (!breakPoss.isForcedBreak()) {
-                            breakPoss.setPenaltyValue(KnuthPenalty.INFINITE);
-                        }
-                    }
-                }
-            }
-            
-            //Check for break-after on the table-row at the end of the row group
-            rowFO = rowGroup[rowGroup.length - 1].getTableRow(); 
-            if (rowFO != null && rowFO.getBreakAfter() != Constants.EN_AUTO) {
-                if (returnList.size() > 0) {
-                    ListElement last = (ListElement)returnList.getLast();
-                    if (last instanceof KnuthPenalty) {
-                        KnuthPenalty pen = (KnuthPenalty)last;
-                        pen.setP(-KnuthPenalty.INFINITE);
-                        pen.setBreakClass(rowFO.getBreakAfter());
-                    } else if (last instanceof BreakElement) {
-                        BreakElement breakPoss = (BreakElement)last;
-                        breakPoss.setPenaltyValue(-KnuthPenalty.INFINITE);
-                        breakPoss.setBreakClass(rowFO.getBreakAfter());
-                    }
-                }
-            }
+            returnList.addAll(new RowGroupLayoutManager(getTableLM(), rowGroup, bodyIter,
+                    headerIter, footerIter, iter, stepper).getNextKnuthElements(context, alignment,
+                    bodyType));
         }
         
         if (returnList.size() > 0) {
@@ -313,296 +245,6 @@ public class TableContentLayoutManager implements PercentBaseContext {
         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 prevRow = iter.getPrecedingRow(row);
-            EffRow nextRow = iter.getFollowingRow(row);
-            if ((prevRow == null) && (iter == this.bodyIter) && (this.headerIter != null)) {
-                prevRow = this.headerIter.getLastRow();
-            }
-            if ((nextRow == null) && (iter == this.headerIter)) {
-                nextRow = this.bodyIter.getFirstRow();
-            }
-            if ((nextRow == null) && (iter == this.bodyIter) && (this.footerIter != null)) {
-                nextRow = this.footerIter.getFirstRow();
-            }
-            if ((prevRow == null) && (iter == this.footerIter)) {
-                //TODO This could be bad for memory consumption because it already causes the
-                //whole body iterator to be prefetched!
-                prevRow = this.bodyIter.getLastRow();
-            }
-            log.debug("prevRow-row-nextRow: " + prevRow + " - " + row + " - " + nextRow);
-            
-            //Determine the grid units necessary for getting all the borders right
-            int guCount = row.getGridUnits().size();
-            if (prevRow != null) {
-                guCount = Math.max(guCount, prevRow.getGridUnits().size());
-            }
-            if (nextRow != null) {
-                guCount = Math.max(guCount, nextRow.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 is 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 (prevRow != null && i < prevRow.getGridUnits().size()) {
-                        other = prevRow.getGridUnit(i);
-                    } else {
-                        other = null;
-                    }
-                    if (other == null 
-                            || other.isEmpty() 
-                            || gu.isEmpty() 
-                            || gu.getPrimary() != other.getPrimary()) {
-                        if ((iter == this.bodyIter)
-                                && 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 (nextRow != null && i < nextRow.getGridUnits().size()) {
-                        other = nextRow.getGridUnit(i);
-                    } else {
-                        other = null;
-                    }
-                    if (other == null 
-                            || other.isEmpty() 
-                            || gu.isEmpty() 
-                            || gu.getPrimary() != other.getPrimary()) {
-                        if ((iter == this.bodyIter)
-                                && 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;
-            // The row's minimum content height; 0 if the row's height is auto, otherwise
-            // the .minimum component of the explicitely specified value
-            int minContentHeight = 0;
-            int maxCellHeight = 0;
-            int effRowContentHeight = 0;
-            for (int j = 0; j < row.getGridUnits().size(); j++) {
-//                assert maxColumnCount == 0 || maxColumnCount == row.getGridUnits().size(); // TODO vh
-                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(getTableLM());
-                     
-                        //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(getTableLM()).isAuto()) {
-                                minContentHeight = Math.max(
-                                        minContentHeight, 
-                                        bpd.getMinimum(
-                                                getTableLM()).getLength().getValue(getTableLM()));
-                            }
-                            MinOptMaxUtil.restrict(explicitRowHeights[rgi], bpd, getTableLM());
-                            
-                        }
-
-                        //Calculate width of cell
-                        int spanWidth = 0;
-                        for (int i = primary.getStartCol(); 
-                                i < primary.getStartCol() 
-                                        + primary.getCell().getNumberColumnsSpanned();
-                                i++) {
-                            if (getTableLM().getColumns().getColumn(i + 1) != null) {
-                                spanWidth += getTableLM().getColumns().getColumn(i + 1)
-                                    .getColumnWidth().getValue(getTableLM());
-                            }
-                        }
-                        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);
-                        //Temporary? Multiple calls in case of break conditions.
-                        //TODO Revisit when table layout is restartable
-                        while (!primary.getCellLM().isFinished()) {
-                            LinkedList additionalElems = primary.getCellLM().getNextKnuthElements(
-                                    childLC, alignment);
-                            elems.addAll(additionalElems);
-                        }
-                        ElementListObserver.observe(elems, "table-cell", primary.getCell().getId());
-
-                        if ((elems.size() > 0) 
-                                && ((KnuthElement)elems.getLast()).isForcedBreak()) {
-                            // a descendant of this block has break-after
-                            log.debug("Descendant of table-cell signals break: " 
-                                    + primary.getCellLM().isFinished());
-                        }
-                        
-                        primary.setElements(elems);
-                        
-                        if (childLC.isKeepWithNextPending()) {
-                            log.debug("child LM signals pending keep-with-next");
-                            primary.setFlag(GridUnit.KEEP_WITH_NEXT_PENDING, true);
-                        }
-                        if (childLC.isKeepWithPreviousPending()) {
-                            log.debug("child LM signals pending keep-with-previous");
-                            primary.setFlag(GridUnit.KEEP_WITH_PREVIOUS_PENDING, true);
-                        }
-                    }
-
-                    
-                    //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(getTableLM()).isAuto()) {
-                            effCellContentHeight = Math.max(
-                                effCellContentHeight,
-                                bpd.getMinimum(getTableLM()).getLength().getValue(getTableLM()));
-                        }
-                        if (!bpd.getOptimum(getTableLM()).isAuto()) {
-                            effCellContentHeight = Math.max(
-                                effCellContentHeight,
-                                bpd.getOptimum(getTableLM()).getLength().getValue(getTableLM()));
-                        }
-                        if (gu.getRowSpanIndex() == 0) {
-                            //TODO ATM only non-row-spanned cells are taken for this
-                            MinOptMaxUtil.restrict(explicitRowHeights[rgi], bpd, tableLM);
-                        }
-                        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;
-                        effRowContentHeight = Math.max(effRowContentHeight,
-                                effCellContentHeight);
-                        CommonBorderPaddingBackground cbpb 
-                            = primary.getCell().getCommonBorderPaddingBackground(); 
-                        padding += cbpb.getPaddingBefore(false, primary.getCellLM());
-                        padding += cbpb.getPaddingAfter(false, primary.getCellLM());
-                        int effRowHeight = effCellContentHeight 
-                                + padding + borderWidths
-                                + 2 * getTableLM().getHalfBorderSeparationBPD();
-                        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);
-                        }
-                    }
-                    
-                    if (gu.isPrimary()) {
-                        pgus.add(primary);
-                    }
-                }
-            }
-
-            row.setHeight(rowHeights[rgi]);
-            row.setExplicitHeight(explicitRowHeights[rgi]);
-            if (effRowContentHeight > row.getExplicitHeight().max) {
-                log.warn(FONode.decorateWithContextInfo(
-                        "The contents of row " + (row.getIndex() + 1) 
-                        + " are taller than they should be (there is a"
-                        + " block-progression-dimension or height constraint on the indicated row)."
-                        + " Due to its contents the row grows"
-                        + " to " + effRowContentHeight + " millipoints, but the row shouldn't get"
-                        + " any taller than " + row.getExplicitHeight() + " millipoints.", 
-                        row.getTableRow()));
-            }
-        }
-        if (log.isDebugEnabled()) {
-            log.debug("rowGroup:");
-            for (int i = 0; i < rowHeights.length; i++) {
-                log.debug("  height=" + rowHeights[i] + " explicit=" + explicitRowHeights[i]);
-            }
-        }
-        LinkedList returnedList = this.stepper.getCombinedKnuthElementsForRowGroup(
-                context, rowGroup, maxColumnCount, bodyType);
-        if (returnedList != null) {
-            returnList.addAll(returnedList);
-        }
-        
-    }
-
     /**
      * Retuns the X offset of the given grid unit.
      * @param gu the grid unit