import org.apache.fop.layoutmgr.KnuthBox;
import org.apache.fop.layoutmgr.KnuthElement;
import org.apache.fop.layoutmgr.KnuthPenalty;
+import org.apache.fop.layoutmgr.MinOptMaxUtil;
+import org.apache.fop.traits.MinOptMax;
/**
* A cell playing in the construction of steps for a row-group.
}
}
+ // TODO to be removed along with the RowPainter#computeContentLength method
+ /** See {@link ActiveCell#handleExplicitHeight(MinOptMax, MinOptMax)}. */
+ private static class FillerPenalty extends KnuthPenalty {
+
+ private int contentLength;
+
+ FillerPenalty(KnuthPenalty p, int length) {
+ super(length, p.getP(), p.isFlagged(), p.getBreakClass(),
+ p.getPosition(), p.isAuxiliary());
+ contentLength = p.getW();
+ }
+
+ FillerPenalty(int length) {
+ super(length, 0, false, null, true);
+ contentLength = 0;
+ }
+ }
+
+ /** See {@link ActiveCell#handleExplicitHeight(MinOptMax, MinOptMax)}. */
+ private static class FillerBox extends KnuthBox {
+ FillerBox(int length) {
+ super(length, null, true);
+ }
+ }
+
+ /**
+ * Returns the actual length of the content represented by the given element. In the
+ * case where this element is used as a filler to match a row's fixed height, the
+ * value returned by the getW() method will be higher than the actual content.
+ *
+ * @param el an element
+ * @return the actual content length corresponding to the element
+ */
+ static int getElementContentLength(KnuthElement el) {
+ if (el instanceof FillerPenalty) {
+ return ((FillerPenalty) el).contentLength;
+ } else if (el instanceof FillerBox) {
+ return 0;
+ } else {
+ return el.getW();
+ }
+ }
+
ActiveCell(PrimaryGridUnit pgu, EffRow row, int rowIndex, int previousRowsLength,
TableLayoutManager tableLM) {
this.pgu = pgu;
+ pgu.getBeforeBorderWidth(0, ConditionalBorder.REST);
bpAfterNormal = paddingAfterNormal + pgu.getAfterBorderWidth(ConditionalBorder.NORMAL);
bpAfterTrailing = paddingAfterTrailing + pgu.getAfterBorderWidth(0, ConditionalBorder.REST);
- boolean makeBoxForWholeRow = false;
- if (row.getExplicitHeight().min > 0) {
- boolean contentsSmaller = ElementListUtils.removeLegalBreaks(
- pgu.getElements(), row.getExplicitHeight());
- if (contentsSmaller) {
- makeBoxForWholeRow = true;
- }
- }
- if (makeBoxForWholeRow) {
- elementList = new java.util.ArrayList(1);
- int height = row.getHeight().opt;
- height -= bpBeforeNormal + bpAfterNormal;
- elementList.add(new KnuthBoxCellWithBPD(height));
- } else {
- elementList = pgu.getElements();
- }
+ elementList = pgu.getElements();
+ handleExplicitHeight(
+ MinOptMaxUtil.toMinOptMax(pgu.getCell().getBlockProgressionDimension(), tableLM),
+ row.getExplicitHeight());
knuthIter = elementList.listIterator();
includedLength = -1; // Avoid troubles with cells having content of zero length
totalLength = previousRowsLength + ElementListUtils.calcContentLength(elementList);
}
}
+ /**
+ * Modifies the cell's element list by putting filler elements, so that the cell's or
+ * row's explicit height is always reached.
+ *
+ * TODO this will work properly only for the first break. Then the limitation
+ * explained on http://wiki.apache.org/xmlgraphics-fop/TableLayout/KnownProblems
+ * occurs. The list of elements needs to be re-adjusted after each break.
+ */
+ private void handleExplicitHeight(MinOptMax cellBPD, MinOptMax rowBPD) {
+ int minBPD = Math.max(cellBPD.min, rowBPD.min);
+ if (minBPD > 0) {
+ ListIterator iter = elementList.listIterator();
+ int cumulateLength = 0;
+ boolean prevIsBox = false;
+ while (iter.hasNext() && cumulateLength < minBPD) {
+ KnuthElement el = (KnuthElement) iter.next();
+ if (el.isBox()) {
+ prevIsBox = true;
+ cumulateLength += el.getW();
+ } else if (el.isGlue()) {
+ if (prevIsBox) {
+ elementList.add(iter.nextIndex() - 1,
+ new FillerPenalty(minBPD - cumulateLength));
+ }
+ prevIsBox = false;
+ cumulateLength += el.getW();
+ } else {
+ prevIsBox = false;
+ if (cumulateLength + el.getW() < minBPD) {
+ iter.set(new FillerPenalty((KnuthPenalty) el, minBPD - cumulateLength));
+ }
+ }
+ }
+ }
+ int optBPD = Math.max(minBPD, Math.max(cellBPD.opt, rowBPD.opt));
+ if (pgu.getContentLength() < optBPD) {
+ elementList.add(new FillerBox(optBPD - pgu.getContentLength()));
+ }
+ }
+
PrimaryGridUnit getPrimaryGridUnit() {
return pgu;
}
return new CellPart(pgu, nextStep.start, previousStep.end, lastCellPart,
0, 0, previousStep.penaltyLength,
bpBeforeNormal, bpBeforeFirst, bpAfterNormal, bpAfterTrailing);
- } else if (nextStep.start == 0 && nextStep.end == 0
- && elementList.size() == 1
- && elementList.get(0) instanceof KnuthBoxCellWithBPD) {
- //Special case: Cell with fixed BPD
- return new CellPart(pgu, 0, pgu.getElements().size() - 1, lastCellPart,
- nextStep.condBeforeContentLength, length, nextStep.penaltyLength,
- bpBeforeNormal, bpBeforeFirst, bpAfterNormal, bpAfterTrailing);
} else {
return new CellPart(pgu, nextStep.start, nextStep.end, lastCellPart,
nextStep.condBeforeContentLength, length, nextStep.penaltyLength,
public String toString() {
return "Cell " + (pgu.getRowIndex() + 1) + "." + (pgu.getColIndex() + 1);
}
-
-
- /**
- * Marker class denoting table cells fitting in just one box (no legal break inside).
- */
- private static class KnuthBoxCellWithBPD extends KnuthBox {
-
- public KnuthBoxCellWithBPD(int w) {
- super(w, null, true);
- }
- }
}
package org.apache.fop.layoutmgr.table;
+import java.util.Iterator;
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.table.EffRow;
import org.apache.fop.fo.flow.table.GridUnit;
import org.apache.fop.fo.flow.table.PrimaryGridUnit;
+import org.apache.fop.fo.flow.table.TableColumn;
import org.apache.fop.fo.flow.table.TableRow;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.LengthRangeProperty;
private void createElementsForRowGroup(LayoutContext context, int alignment,
int bodyType, LinkedList returnList) {
log.debug("Handling row group with " + rowGroup.length + " rows...");
+ EffRow row;
+ for (int rgi = 0; rgi < rowGroup.length; rgi++) {
+ row = rowGroup[rgi];
+ for (Iterator iter = row.getGridUnits().iterator(); iter.hasNext();) {
+ GridUnit gu = (GridUnit) iter.next();
+ if (gu.isPrimary()) {
+ PrimaryGridUnit primary = gu.getPrimary();
+ // TODO a new LM must be created for every new static-content
+ primary.createCellLM();
+ primary.getCellLM().setParent(tableLM);
+ //Calculate width of cell
+ int spanWidth = 0;
+ Iterator colIter = tableLM.getTable().getColumns().listIterator(
+ primary.getColIndex());
+ for (int i = 0, c = primary.getCell().getNumberColumnsSpanned(); i < c; i++) {
+ spanWidth += ((TableColumn) colIter.next()).getColumnWidth().getValue(
+ tableLM);
+ }
+ LayoutContext childLC = new LayoutContext(0);
+ childLC.setStackLimitBP(context.getStackLimitBP()); //necessary?
+ childLC.setRefIPD(spanWidth);
+
+ //Get the element list for the cell contents
+ LinkedList elems = primary.getCellLM().getNextKnuthElements(
+ childLC, alignment);
+ ElementListObserver.observe(elems, "table-cell", primary.getCell().getId());
+ primary.setElements(elems);
+ }
+ }
+ }
+ computeRowHeights();
+ LinkedList elements = tableStepper.getCombinedKnuthElementsForRowGroup(context,
+ rowGroup, bodyType);
+ returnList.addAll(elements);
+ }
+
+ /**
+ * Calculate the heights of the rows in the row group, see CSS21, 17.5.3 Table height
+ * algorithms.
+ *
+ * TODO this method will need to be adapted once clarification has been made by the
+ * W3C regarding whether borders or border-separation must be included or not
+ */
+ private void computeRowHeights() {
+ log.debug("rowGroup:");
MinOptMax[] rowHeights = new MinOptMax[rowGroup.length];
- MinOptMax[] explicitRowHeights = new MinOptMax[rowGroup.length];
EffRow row;
- 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 explicitly specified value
- int minRowBPD = 0;
// The BPD of the biggest cell in the row
- int maxCellBPD = 0;
- for (int j = 0; j < row.getGridUnits().size(); j++) {
- GridUnit gu = row.getGridUnit(j);
- if ((gu.isPrimary() || (gu.getColSpanIndex() == 0 && gu.isLastGridUnitRowSpan()))
- && !gu.isEmpty()) {
+// int maxCellBPD = 0;
+ MinOptMax explicitRowHeight;
+ TableRow tableRowFO = rowGroup[rgi].getTableRow();
+ if (tableRowFO == null) {
+ rowHeights[rgi] = new MinOptMax(0, 0, Integer.MAX_VALUE);
+ explicitRowHeight = new MinOptMax(0, 0, Integer.MAX_VALUE);
+ } else {
+ LengthRangeProperty rowBPD = tableRowFO.getBlockProgressionDimension();
+ rowHeights[rgi] = MinOptMaxUtil.toMinOptMax(rowBPD, tableLM);
+ explicitRowHeight = MinOptMaxUtil.toMinOptMax(rowBPD, tableLM);
+ }
+ for (Iterator iter = row.getGridUnits().iterator(); iter.hasNext();) {
+ GridUnit gu = (GridUnit) iter.next();
+ if (!gu.isEmpty() && gu.getColSpanIndex() == 0 && gu.isLastGridUnitRowSpan()) {
PrimaryGridUnit primary = gu.getPrimary();
-
- if (gu.isPrimary()) {
- // TODO a new LM must be created for every new static-content
- primary.createCellLM();
- 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 rowBPD = tableRow.getBlockProgressionDimension();
- if (!rowBPD.getMinimum(tableLM).isAuto()) {
- minRowBPD = Math.max(minRowBPD,
- rowBPD.getMinimum(tableLM).getLength().getValue(tableLM));
- }
- MinOptMaxUtil.restrict(explicitRowHeights[rgi], rowBPD, tableLM);
-
- }
-
- //Calculate width of cell
- int spanWidth = 0;
- for (int i = primary.getColIndex();
- i < primary.getColIndex()
- + 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.setStackLimitBP(context.getStackLimitBP()); //necessary?
- childLC.setRefIPD(spanWidth);
-
- //Get the element list for the cell contents
- LinkedList elems = primary.getCellLM().getNextKnuthElements(
- childLC, alignment);
- ElementListObserver.observe(elems, "table-cell", primary.getCell().getId());
- primary.setElements(elems);
+ int effectiveCellBPD = 0;
+ LengthRangeProperty cellBPD = primary.getCell().getBlockProgressionDimension();
+ if (!cellBPD.getMinimum(tableLM).isAuto()) {
+ effectiveCellBPD = cellBPD.getMinimum(tableLM).getLength()
+ .getValue(tableLM);
}
-
- //Calculate height of row, see CSS21, 17.5.3 Table height algorithms
- if (gu.isLastGridUnitRowSpan()) {
- // The effective cell's bpd, after taking into account bpd
- // (possibly explicitly) set on the row or on the cell, and the
- // cell's content length
- int effectiveCellBPD = minRowBPD;
- LengthRangeProperty cellBPD = primary.getCell()
- .getBlockProgressionDimension();
- if (!cellBPD.getMinimum(tableLM).isAuto()) {
- effectiveCellBPD = Math.max(effectiveCellBPD,
- cellBPD.getMinimum(tableLM).getLength().getValue(tableLM));
- }
- if (!cellBPD.getOptimum(tableLM).isAuto()) {
- effectiveCellBPD = Math.max(effectiveCellBPD,
- cellBPD.getOptimum(tableLM).getLength().getValue(tableLM));
- }
- if (gu.getRowSpanIndex() == 0) {
- //TODO ATM only non-row-spanned cells are taken for this
- MinOptMaxUtil.restrict(explicitRowHeights[rgi], cellBPD, tableLM);
- }
- effectiveCellBPD = Math.max(effectiveCellBPD,
- primary.getContentLength());
-
- int borderWidths = primary.getBeforeAfterBorderWidth();
- int padding = 0;
- maxCellBPD = Math.max(maxCellBPD, effectiveCellBPD);
- CommonBorderPaddingBackground cbpb
- = primary.getCell().getCommonBorderPaddingBackground();
- padding += cbpb.getPaddingBefore(false, primary.getCellLM());
- padding += cbpb.getPaddingAfter(false, primary.getCellLM());
- int effRowHeight = effectiveCellBPD
- + padding + borderWidths;
- for (int previous = 0; previous < gu.getRowSpanIndex(); previous++) {
- effRowHeight -= rowHeights[rgi - previous - 1].opt;
- }
- if (effRowHeight > rowHeights[rgi].min) {
- //This is the new height of the (grid) row
- MinOptMaxUtil.extendMinimum(rowHeights[rgi], effRowHeight, false);
- }
+ if (!cellBPD.getOptimum(tableLM).isAuto()) {
+ effectiveCellBPD = cellBPD.getOptimum(tableLM).getLength()
+ .getValue(tableLM);
}
-
- if (gu.isPrimary()) {
- pgus.add(primary);
+ if (gu.getRowSpanIndex() == 0) {
+ effectiveCellBPD = Math.max(effectiveCellBPD, explicitRowHeight.opt);
+ }
+ effectiveCellBPD = Math.max(effectiveCellBPD, primary.getContentLength());
+ int borderWidths = primary.getBeforeAfterBorderWidth();
+ int padding = 0;
+ CommonBorderPaddingBackground cbpb = primary.getCell()
+ .getCommonBorderPaddingBackground();
+ padding += cbpb.getPaddingBefore(false, primary.getCellLM());
+ padding += cbpb.getPaddingAfter(false, primary.getCellLM());
+ int effRowHeight = effectiveCellBPD + padding + borderWidths;
+ for (int prev = rgi - 1; prev >= rgi - gu.getRowSpanIndex(); prev--) {
+ effRowHeight -= rowHeights[prev].opt;
+ }
+ if (effRowHeight > rowHeights[rgi].min) {
+ // This is the new height of the (grid) row
+ MinOptMaxUtil.extendMinimum(rowHeights[rgi], effRowHeight);
}
}
}
row.setHeight(rowHeights[rgi]);
- row.setExplicitHeight(explicitRowHeights[rgi]);
- if (maxCellBPD > 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 " + maxCellBPD + " 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]);
+ row.setExplicitHeight(explicitRowHeight);
+ // TODO re-enable and improve after clarification
+// if (maxCellBPD > 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 " + maxCellBPD + " millipoints, but the row shouldn't get"
+// + " any taller than " + row.getExplicitHeight() + " millipoints.",
+// row.getTableRow()));
+// }
+ if (log.isDebugEnabled()) {
+ log.debug(" height=" + rowHeights[rgi] + " explicit=" + explicitRowHeight);
}
}
- LinkedList elements = tableStepper.getCombinedKnuthElementsForRowGroup(context,
- rowGroup, bodyType);
- returnList.addAll(elements);
}
}