aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVincent Hennebert <vhennebert@apache.org>2007-08-02 15:37:17 +0000
committerVincent Hennebert <vhennebert@apache.org>2007-08-02 15:37:17 +0000
commitbeb66bc635565b61796d281d09fdd7835d18e299 (patch)
tree72088f454c9e9186d7c9a103d8fa47a42072a788
parent9a101c3b38a7cd8a9f83c9cabdba26068de044a6 (diff)
downloadxmlgraphics-fop-beb66bc635565b61796d281d09fdd7835d18e299.tar.gz
xmlgraphics-fop-beb66bc635565b61796d281d09fdd7835d18e299.zip
First step at introducing a layout manager for a row-group
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@562140 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java433
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java364
2 files changed, 436 insertions, 361 deletions
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
index 000000000..c7db197a3
--- /dev/null
+++ b/src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java
@@ -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);
+ }
+
+ }
+}
diff --git a/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java
index 675e5ce95..37742437c 100644
--- a/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java
@@ -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) {
@@ -314,296 +246,6 @@ public class TableContentLayoutManager implements PercentBaseContext {
}
/**
- * 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
* @return the requested X offset