From 77c8566a57472e33049c8e02dd753dd79944e2ad Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Wed, 5 Mar 2008 18:22:06 +0000 Subject: [PATCH] - Fixed the rendering of the area of a table corresponding to border-separation, which must be filled with the background of the table, and not the rows. - Improved conformance: even if a table-cell spans several rows its background must correspond to the first row spanned. - Added support for background on fo:table-column and fo:table-header/footer/body TODO more testcases needed, especially tables with collapsing border model and tables broken over pages git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@633961 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/layoutmgr/TraitSetter.java | 86 +++- .../fop/layoutmgr/table/RowPainter.java | 93 ++++- .../table/TableCellLayoutManager.java | 85 ++-- .../table/TableContentLayoutManager.java | 164 +++----- .../layoutmgr/table/TableContentPosition.java | 5 + .../layoutmgr/table/TableLayoutManager.java | 69 ++- status.xml | 11 + .../table-cell_conditional-spaces_2.xml | 64 +-- .../table-footer_omit-footer-at-break.xml | 38 +- .../table-header_omit-header-at-break.xml | 34 +- .../table-header_table-footer_1.xml | 40 +- .../table-header_table-footer_2.xml | 46 +- .../table-row_background-image.xml | 20 +- .../standard-testcases/table-row_height.xml | 34 +- .../standard-testcases/table_backgrounds.xml | 392 ++++++++++++++++++ 15 files changed, 930 insertions(+), 251 deletions(-) create mode 100644 test/layoutengine/standard-testcases/table_backgrounds.xml diff --git a/src/java/org/apache/fop/layoutmgr/TraitSetter.java b/src/java/org/apache/fop/layoutmgr/TraitSetter.java index 841a94705..189212eb8 100644 --- a/src/java/org/apache/fop/layoutmgr/TraitSetter.java +++ b/src/java/org/apache/fop/layoutmgr/TraitSetter.java @@ -280,6 +280,90 @@ public class TraitSetter { } } + /** + * Add background to an area. This method is mainly used by table-related layout + * managers to add background for column, body or row. Since the area corresponding to + * border-separation must be filled with the table's background, for every cell an + * additional area with the same dimensions is created to hold the background for the + * corresponding column/body/row. An additional shift must then be added to + * background-position-horizontal/vertical to ensure the background images are + * correctly placed. Indeed the placement of images must be made WRT the + * column/body/row and not the cell. + * + *

Note: The area's IPD and BPD must be set before calling this method.

+ * + *

TODO the regular + * {@link #addBackground(Area, CommonBorderPaddingBackground, PercentBaseContext)} + * method should be used instead, and a means to retrieve the original area's + * dimensions must be found.

+ * + *

TODO the placement of images in the x- or y-direction will be incorrect if + * background-repeat is set for that direction.

+ * + * @param area the area to set the traits on + * @param backProps the background properties + * @param context Property evaluation context + * @param ipdShift horizontal shift to affect to the background, in addition to the + * value of the background-position-horizontal property + * @param bpdShift vertical shift to affect to the background, in addition to the + * value of the background-position-vertical property + * @param referenceIPD value to use as a reference for percentage calculation + * @param referenceBPD value to use as a reference for percentage calculation + */ + public static void addBackground(Area area, + CommonBorderPaddingBackground backProps, + PercentBaseContext context, + int ipdShift, int bpdShift, int referenceIPD, int referenceBPD) { + if (!backProps.hasBackground()) { + return; + } + Trait.Background back = new Trait.Background(); + back.setColor(backProps.backgroundColor); + + if (backProps.getImageInfo() != null) { + back.setURL(backProps.backgroundImage); + back.setImageInfo(backProps.getImageInfo()); + back.setRepeat(backProps.backgroundRepeat); + if (backProps.backgroundPositionHorizontal != null) { + if (back.getRepeat() == Constants.EN_NOREPEAT + || back.getRepeat() == Constants.EN_REPEATY) { + if (area.getIPD() > 0) { + PercentBaseContext refContext = new SimplePercentBaseContext(context, + LengthBase.IMAGE_BACKGROUND_POSITION_HORIZONTAL, + (referenceIPD - back.getImageInfo().getSize().getWidthMpt())); + + back.setHoriz(ipdShift + + backProps.backgroundPositionHorizontal.getValue(refContext)); + } else { + // TODO Area IPD has to be set for this to work + log.warn("Horizontal background image positioning ignored" + + " because the IPD was not set on the area." + + " (Yes, it's a bug in FOP)"); + } + } + } + if (backProps.backgroundPositionVertical != null) { + if (back.getRepeat() == Constants.EN_NOREPEAT + || back.getRepeat() == Constants.EN_REPEATX) { + if (area.getBPD() > 0) { + PercentBaseContext refContext = new SimplePercentBaseContext(context, + LengthBase.IMAGE_BACKGROUND_POSITION_VERTICAL, + (referenceBPD - back.getImageInfo().getSize().getHeightMpt())); + back.setVertical(bpdShift + + backProps.backgroundPositionVertical.getValue(refContext)); + } else { + // TODO Area BPD has to be set for this to work + log.warn("Vertical background image positioning ignored" + + " because the BPD was not set on the area." + + " (Yes, it's a bug in FOP)"); + } + } + } + } + + area.addTrait(Trait.BACKGROUND, back); + } + /** * Add background to an area. * Layout managers that create areas with a background can use this to @@ -312,7 +396,7 @@ public class TraitSetter { back.setHoriz(backProps.backgroundPositionHorizontal.getValue( new SimplePercentBaseContext(context, LengthBase.IMAGE_BACKGROUND_POSITION_HORIZONTAL, - (width - back.getImageInfo().getSize().getHeightMpt()) + (width - back.getImageInfo().getSize().getWidthMpt()) ) )); } else { diff --git a/src/java/org/apache/fop/layoutmgr/table/RowPainter.java b/src/java/org/apache/fop/layoutmgr/table/RowPainter.java index 022ff0589..61f6b3aec 100644 --- a/src/java/org/apache/fop/layoutmgr/table/RowPainter.java +++ b/src/java/org/apache/fop/layoutmgr/table/RowPainter.java @@ -26,22 +26,23 @@ import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.fop.area.Block; import org.apache.fop.fo.flow.table.ConditionalBorder; 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.TableRow; +import org.apache.fop.fo.flow.table.TableBody; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.layoutmgr.ElementListUtils; import org.apache.fop.layoutmgr.KnuthElement; import org.apache.fop.layoutmgr.KnuthPossPosIter; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.SpaceResolver; +import org.apache.fop.layoutmgr.TraitSetter; import org.apache.fop.traits.BorderProps; class RowPainter { private static Log log = LogFactory.getLog(RowPainter.class); - /** The fo:table-row containing the currently handled grid rows. */ - private TableRow rowFO = null; private int colCount; private int currentRowOffset = 0; /** Currently handled row (= last encountered row). */ @@ -71,6 +72,13 @@ class RowPainter { private CellPart[] firstCellParts; private CellPart[] lastCellParts; + /** y-offset of the current table part. */ + private int tablePartOffset = 0; + /** See {@link RowPainter#registerPartBackgroundArea(Block)}. */ + private CommonBorderPaddingBackground tablePartBackground; + /** See {@link RowPainter#registerPartBackgroundArea(Block)}. */ + private List tablePartBackgroundAreas; + private TableContentLayoutManager tclm; RowPainter(TableContentLayoutManager tclm, LayoutContext layoutContext) { @@ -85,6 +93,44 @@ class RowPainter { this.firstRowOnPageIndex = -1; } + void startTablePart(TableBody tablePart) { + CommonBorderPaddingBackground background = tablePart.getCommonBorderPaddingBackground(); + if (background.hasBackground()) { + tablePartBackground = background; + if (tablePartBackgroundAreas == null) { + tablePartBackgroundAreas = new ArrayList(); + } + } + tablePartOffset = currentRowOffset; + } + + /** + * Signals that the end of the current table part is reached. + * + * @param lastInBody true if the part is the last table-body element to be displayed + * on the current page. In which case all the cells must be flushed even if they + * aren't finished, plus the proper collapsed borders must be selected (trailing + * instead of normal, or rest if the cell is unfinished) + * @param lastOnPage true if the part is the last to be displayed on the current page. + * In which case collapsed after borders for the cells on the last row must be drawn + * in the outer mode + */ + void endTablePart(boolean lastInBody, boolean lastOnPage) { + addAreasAndFlushRow(lastInBody, lastOnPage); + + if (tablePartBackground != null) { + TableLayoutManager tableLM = tclm.getTableLM(); + for (Iterator iter = tablePartBackgroundAreas.iterator(); iter.hasNext();) { + Block backgroundArea = (Block) iter.next(); + TraitSetter.addBackground(backgroundArea, tablePartBackground, tableLM, + -backgroundArea.getXOffset(), tablePartOffset - backgroundArea.getYOffset(), + tableLM.getContentAreaIPD(), currentRowOffset - tablePartOffset); + } + tablePartBackground = null; + tablePartBackgroundAreas.clear(); + } + } + int getAccumulatedBPD() { return currentRowOffset; } @@ -108,7 +154,6 @@ class RowPainter { currentRow = row; } } - rowFO = currentRow.getTableRow(); if (firstRowIndex < 0) { firstRowIndex = currentRow.getIndex(); if (firstRowOnPageIndex < 0) { @@ -148,7 +193,7 @@ class RowPainter { * displayed on the current page. In which case collapsed after borders must be drawn * in the outer mode */ - void addAreasAndFlushRow(boolean lastInPart, boolean lastOnPage) { + private void addAreasAndFlushRow(boolean lastInPart, boolean lastOnPage) { if (log.isDebugEnabled()) { log.debug("Remembering yoffset for row " + currentRow.getIndex() + ": " + currentRowOffset); @@ -182,8 +227,6 @@ class RowPainter { } // Then add areas for cells finishing on the current row - tclm.addRowBackgroundArea(rowFO, actualRowHeight, layoutContext.getRefIPD(), - currentRowOffset); for (int i = 0; i < colCount; i++) { GridUnit currentGU = currentRow.getGridUnit(i); if (!currentGU.isEmpty() && currentGU.getColSpanIndex() == 0 @@ -278,8 +321,20 @@ class RowPainter { * Determine the index of the first row of this cell that will be displayed on the * current page. */ - int startRowIndex = Math.max(pgu.getRowIndex(), firstRowIndex); int currentRowIndex = currentRow.getIndex(); + int startRowIndex; + int firstRowHeight; + if (pgu.getRowIndex() >= firstRowIndex) { + startRowIndex = pgu.getRowIndex(); + if (startRowIndex < currentRowIndex) { + firstRowHeight = getRowOffset(startRowIndex + 1) - getRowOffset(startRowIndex); + } else { + firstRowHeight = rowHeight; + } + } else { + startRowIndex = firstRowIndex; + firstRowHeight = 0; + } /* * In collapsing-border model, if the cell spans over several columns/rows then @@ -319,7 +374,25 @@ class RowPainter { cellLM.addAreas(new KnuthPossPosIter(pgu.getElements(), startPos, endPos + 1), layoutContext, spannedGridRowHeights, startRowIndex - pgu.getRowIndex(), currentRowIndex - pgu.getRowIndex(), borderBeforeWhich, borderAfterWhich, - startRowIndex == firstRowOnPageIndex, lastOnPage); + startRowIndex == firstRowOnPageIndex, lastOnPage, this, firstRowHeight); + } + + + /** + * Registers the given area, that will be used to render the part of + * table-header/footer/body background covered by a table-cell. If percentages are + * used to place the background image, the final bpd of the (fraction of) table part + * that will be rendered on the current page must be known. The traits can't then be + * set when the areas for the cell are created since at that moment this bpd is yet + * unknown. So they will instead be set in + * {@link #addAreasAndFlushRow(boolean, boolean)}. + * + * @param backgroundArea the block of the cell's dimensions that will hold the part + * background + */ + void registerPartBackgroundArea(Block backgroundArea) { + tclm.getTableLM().addBackgroundArea(backgroundArea); + tablePartBackgroundAreas.add(backgroundArea); } /** @@ -357,11 +430,13 @@ class RowPainter { } // TODO get rid of that + /** Signals that the first table-body instance has started. */ void startBody() { Arrays.fill(firstCellOnPage, true); } // TODO get rid of that + /** Signals that the last table-body instance has ended. */ void endBody() { Arrays.fill(firstCellOnPage, false); } diff --git a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java index 289785d68..8e92a233b 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableCellLayoutManager.java @@ -27,12 +27,14 @@ import org.apache.fop.area.Area; import org.apache.fop.area.Block; import org.apache.fop.area.Trait; import org.apache.fop.datatypes.PercentBaseContext; -import org.apache.fop.fo.FONode; import org.apache.fop.fo.flow.table.ConditionalBorder; import org.apache.fop.fo.flow.table.GridUnit; import org.apache.fop.fo.flow.table.PrimaryGridUnit; import org.apache.fop.fo.flow.table.Table; +import org.apache.fop.fo.flow.table.TableBody; import org.apache.fop.fo.flow.table.TableCell; +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.CommonBorderPaddingBackground.BorderInfo; import org.apache.fop.layoutmgr.AreaAdditionUtil; @@ -71,7 +73,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager private int xoffset; private int yoffset; private int cellIPD; - private int rowHeight; + private int totalHeight; private int usedBPD; private int borderAndPaddingBPD; private boolean emptyCell = true; @@ -116,11 +118,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager * @return the table owning this cell */ public Table getTable() { - FONode node = fobj.getParent(); - while (!(node instanceof Table)) { - node = node.getParent(); - } - return (Table)node; + return getTableCell().getTable(); } @@ -303,7 +301,7 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager * @param h the height of cell */ public void setTotalHeight(int h) { - rowHeight = h; + totalHeight = h; } /** @@ -330,6 +328,10 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager * which case collapsed before borders must be drawn in the outer mode * @param lastOnPage true if the cell will be the very last one on the page, in which * case collapsed after borders must be drawn in the outer mode + * @param painter painter + * @param firstRowHeight height of the first row spanned by this cell (may be zero if + * this row is placed on a previous page). Used to calculate the placement of the + * row's background image if any */ public void addAreas(PositionIterator parentIter, LayoutContext layoutContext, @@ -339,13 +341,24 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager int borderBeforeWhich, int borderAfterWhich, boolean firstOnPage, - boolean lastOnPage) { + boolean lastOnPage, + RowPainter painter, + int firstRowHeight) { getParentArea(null); getPSLM().addIDToPage(getTableCell().getId()); int borderBeforeWidth = primaryGridUnit.getBeforeBorderWidth(startRow, borderBeforeWhich); int borderAfterWidth = primaryGridUnit.getAfterBorderWidth(endRow, borderAfterWhich); + + CommonBorderPaddingBackground padding = primaryGridUnit.getCell() + .getCommonBorderPaddingBackground(); + int cellBPD = totalHeight - borderBeforeWidth - borderAfterWidth; + cellBPD -= padding.getPaddingBefore(borderBeforeWhich == ConditionalBorder.REST, this); + cellBPD -= padding.getPaddingAfter(borderAfterWhich == ConditionalBorder.REST, this); + + addBackgroundAreas(painter, firstRowHeight, borderBeforeWidth, cellBPD); + if (isSeparateBorderModel()) { if (!emptyCell || getTableCell().showEmptyCells()) { if (borderBeforeWidth > 0) { @@ -437,18 +450,12 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager } } - CommonBorderPaddingBackground padding = primaryGridUnit.getCell() - .getCommonBorderPaddingBackground(); TraitSetter.addPadding(curBlockArea, padding, borderBeforeWhich == ConditionalBorder.REST, borderAfterWhich == ConditionalBorder.REST, false, false, this); - int cellBPD = rowHeight - borderBeforeWidth - borderAfterWidth; - cellBPD -= padding.getPaddingBefore(borderBeforeWhich == ConditionalBorder.REST, this); - cellBPD -= padding.getPaddingAfter(borderAfterWhich == ConditionalBorder.REST, this); - //Handle display-align if (usedBPD < cellBPD) { if (getTableCell().getDisplayAlign() == EN_CENTER) { @@ -468,16 +475,9 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager curBlockArea.setBPD(cellBPD); // Add background after we know the BPD - if (isSeparateBorderModel()) { - if (!emptyCell || getTableCell().showEmptyCells()) { - TraitSetter.addBackground(curBlockArea, - getTableCell().getCommonBorderPaddingBackground(), - this); - } - } else { + if (!isSeparateBorderModel() || !emptyCell || getTableCell().showEmptyCells()) { TraitSetter.addBackground(curBlockArea, - getTableCell().getCommonBorderPaddingBackground(), - this); + getTableCell().getCommonBorderPaddingBackground(), this); } flush(); @@ -485,6 +485,32 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager curBlockArea = null; } + /** Adds background areas for the column, body and row, if any. */ + private void addBackgroundAreas(RowPainter painter, int firstRowHeight, int borderBeforeWidth, + int cellBPD) { + TableColumn column = getTable().getColumn(primaryGridUnit.getColIndex()); + if (column.getCommonBorderPaddingBackground().hasBackground()) { + Block colBackgroundArea = getBackgroundArea(cellBPD, borderBeforeWidth); + ((TableLayoutManager) parentLM).registerColumnBackgroundArea(column, colBackgroundArea, + -startIndent); + } + + TableBody body = primaryGridUnit.getTableBody(); + if (body.getCommonBorderPaddingBackground().hasBackground()) { + painter.registerPartBackgroundArea(getBackgroundArea(cellBPD, borderBeforeWidth)); + } + + TableRow row = primaryGridUnit.getRow(); + if (row != null && row.getCommonBorderPaddingBackground().hasBackground()) { + Block rowBackgroundArea = getBackgroundArea(cellBPD, borderBeforeWidth); + ((TableLayoutManager) parentLM).addBackgroundArea(rowBackgroundArea); + TraitSetter.addBackground(rowBackgroundArea, row.getCommonBorderPaddingBackground(), + (TableLayoutManager) parentLM, + -xoffset - startIndent, -borderBeforeWidth, + parentLM.getContentAreaIPD(), firstRowHeight); + } + } + private void addBorder(Block[][] blocks, int i, int j, Integer side, BorderInfo border, boolean outer) { if (blocks[i][j] == null) { @@ -513,6 +539,17 @@ public class TableCellLayoutManager extends BlockStackingLayoutManager block.setBPD(block.getBPD() + amount); } + private Block getBackgroundArea(int bpd, int borderBeforeWidth) { + Block block = new Block(); + TraitSetter.setProducerID(block, getTable().getId()); + block.setPositioning(Block.ABSOLUTE); + block.setIPD(cellIPD); + block.setBPD(bpd); + block.setXOffset(xoffset + startIndent); + block.setYOffset(yoffset + borderBeforeWidth); + return block; + } + /** * Return an Area which can contain the passed childArea. The childArea * may not yet have any content, but it has essential traits set. diff --git a/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java index 5fad1e4c5..7cdeb79d5 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableContentLayoutManager.java @@ -27,8 +27,6 @@ import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.fop.area.Block; -import org.apache.fop.area.Trait; import org.apache.fop.datatypes.PercentBaseContext; import org.apache.fop.fo.Constants; import org.apache.fop.fo.FObj; @@ -36,7 +34,6 @@ import org.apache.fop.fo.flow.table.EffRow; import org.apache.fop.fo.flow.table.PrimaryGridUnit; import org.apache.fop.fo.flow.table.Table; import org.apache.fop.fo.flow.table.TableBody; -import org.apache.fop.fo.flow.table.TableRow; import org.apache.fop.layoutmgr.BreakElement; import org.apache.fop.layoutmgr.ElementListUtils; import org.apache.fop.layoutmgr.KnuthBox; @@ -46,7 +43,6 @@ import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.ListElement; import org.apache.fop.layoutmgr.Position; import org.apache.fop.layoutmgr.PositionIterator; -import org.apache.fop.layoutmgr.TraitSetter; import org.apache.fop.layoutmgr.SpaceResolver.SpaceHandlingBreakPosition; import org.apache.fop.util.BreakUtil; @@ -293,7 +289,7 @@ public class TableContentLayoutManager implements PercentBaseContext { this.usedBPD = 0; RowPainter painter = new RowPainter(this, layoutContext); - List positions = new java.util.ArrayList(); + List tablePositions = new ArrayList(); List headerElements = null; List footerElements = null; Position firstPos = null; @@ -330,7 +326,7 @@ public class TableContentLayoutManager implements PercentBaseContext { //ignore for now, see special handling below if break is at a penalty //Only if the last position in this part/page us such a position it will be used } else if (pos instanceof TableContentPosition) { - positions.add(pos); + tablePositions.add(pos); } else { if (log.isDebugEnabled()) { log.debug("Ignoring position: " + pos); @@ -359,21 +355,23 @@ public class TableContentLayoutManager implements PercentBaseContext { if (headerElements != null) { //header positions for the last part are the second-to-last element and need to //be handled first before all other TableContentPositions - PositionIterator nestedIter = new KnuthPossPosIter(headerElements); - iterateAndPaintPositions(nestedIter, painter, false); + addHeaderFooterAreas(headerElements, tableLM.getTable().getTableHeader(), painter, + false); } - //Iterate over all steps - Iterator posIter = positions.iterator(); - painter.startBody(); - // Here we are sure that posIter iterates only over TableContentPosition instances - iterateAndPaintPositions(posIter, painter, footerElements == null); - painter.endBody(); + if (tablePositions.isEmpty()) { + // TODO make sure this actually never happens + log.error("tablePositions empty." + + " Please send your FO file to fop-users@xmlgraphics.apache.org"); + } else { + // Here we are sure that posIter iterates only over TableContentPosition instances + addBodyAreas(tablePositions.iterator(), painter, footerElements == null); + } if (footerElements != null) { //Positions for footers are simply added at the end - PositionIterator nestedIter = new KnuthPossPosIter(footerElements); - iterateAndPaintPositions(nestedIter, painter, true); + addHeaderFooterAreas(footerElements, tableLM.getTable().getTableFooter(), painter, + true); } this.usedBPD += painter.getAccumulatedBPD(); @@ -384,105 +382,73 @@ public class TableContentLayoutManager implements PercentBaseContext { } } + private void addHeaderFooterAreas(List elements, TableBody part, RowPainter painter, + boolean lastOnPage) { + List lst = new ArrayList(elements.size()); + for (Iterator iter = new KnuthPossPosIter(elements); iter.hasNext();) { + Position pos = (Position) iter.next(); + /* + * Unlike for the body the Positions associated to the glues generated by + * TableStepper haven't been removed yet. + */ + if (pos instanceof TableContentPosition) { + lst.add((TableContentPosition) pos); + } + } + addTablePartAreas(lst, painter, part, true, true, true, lastOnPage); + } + /** - * Iterates over a part of the table (header, footer, body) and paints the related - * elements. + * Iterates over the positions corresponding to the table's body (which may contain + * several table-body elements!) and adds the corresponding areas. * - * @param iterator iterator over Position elements. Those positions correspond to the - * elements of the table present on the current page + * @param iterator iterator over TableContentPosition elements. Those positions + * correspond to the elements of the body present on the current page * @param painter - * @param lastOnPage true if the corresponding part will be the last on the page - * (either body or footer, obviously) + * @param lastOnPage true if the table has no footer (then the last line of the table + * that will be present on the page belongs to the body) */ - private void iterateAndPaintPositions(Iterator iterator, RowPainter painter, + private void addBodyAreas(Iterator iterator, RowPainter painter, boolean lastOnPage) { + painter.startBody(); List lst = new ArrayList(); - boolean firstPos = false; - TableBody body = null; + TableContentPosition pos = (TableContentPosition) iterator.next(); + boolean isFirstPos = pos.getFlag(TableContentPosition.FIRST_IN_ROWGROUP) + && pos.getRow().getFlag(EffRow.FIRST_IN_PART); + TableBody body = pos.getTableBody(); + lst.add(pos); while (iterator.hasNext()) { - Position pos = (Position)iterator.next(); - if (pos instanceof TableContentPosition) { - TableContentPosition tcpos = (TableContentPosition)pos; - lst.add(tcpos); - CellPart part = (CellPart)tcpos.cellParts.get(0); - if (body == null) { - body = part.pgu.getTableBody(); - } - if (tcpos.getFlag(TableContentPosition.FIRST_IN_ROWGROUP) - && tcpos.getRow().getFlag(EffRow.FIRST_IN_PART)) { - firstPos = true; - - } - if (tcpos.getFlag(TableContentPosition.LAST_IN_ROWGROUP) - && tcpos.getRow().getFlag(EffRow.LAST_IN_PART)) { - log.trace("LAST_IN_ROWGROUP + LAST_IN_PART"); - handleMarkersAndPositions(lst, body, firstPos, true, painter); - //reset - firstPos = false; - body = null; - lst.clear(); - } + pos = (TableContentPosition) iterator.next(); + if (pos.getTableBody() != body) { + addTablePartAreas(lst, painter, body, isFirstPos, true, false, false); + isFirstPos = true; + lst.clear(); + body = pos.getTableBody(); } + lst.add(pos); } - if (body != null) { - // Entering this block means that the end of the current table-part hasn't - // been reached (otherwise it would have been caught by the test above). So - // lastPos is necessarily false - handleMarkersAndPositions(lst, body, firstPos, false, painter); - } - painter.addAreasAndFlushRow(true, lastOnPage); - } - - private void handleMarkersAndPositions(List positions, TableBody body, boolean firstPos, - boolean lastPos, RowPainter painter) { - getTableLM().getCurrentPV().addMarkers(body.getMarkers(), - true, firstPos, lastPos); - int size = positions.size(); - for (int i = 0; i < size; i++) { - painter.handleTableContentPosition((TableContentPosition)positions.get(i)); - } - getTableLM().getCurrentPV().addMarkers(body.getMarkers(), - false, firstPos, lastPos); + boolean isLastPos = pos.getFlag(TableContentPosition.LAST_IN_ROWGROUP) + && pos.getRow().getFlag(EffRow.LAST_IN_PART); + addTablePartAreas(lst, painter, body, isFirstPos, isLastPos, true, lastOnPage); + painter.endBody(); } /** - * Get the area for a row for background. - * @param row the table-row object or null - * @return the row area or null if there's no background to paint + * Adds the areas corresponding to a single fo:table-header/footer/body element. */ - Block getRowArea(TableRow row) { - if (row == null || !row.getCommonBorderPaddingBackground().hasBackground()) { - return null; - } else { - Block block = new Block(); - block.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE); - block.setPositioning(Block.ABSOLUTE); - return block; + private void addTablePartAreas(List positions, RowPainter painter, TableBody body, + boolean isFirstPos, boolean isLastPos, boolean lastInBody, boolean lastOnPage) { + getTableLM().getCurrentPV().addMarkers(body.getMarkers(), + true, isFirstPos, isLastPos); + painter.startTablePart(body); + for (Iterator iter = positions.iterator(); iter.hasNext();) { + painter.handleTableContentPosition((TableContentPosition) iter.next()); } + getTableLM().getCurrentPV().addMarkers(body.getMarkers(), + false, isFirstPos, isLastPos); + painter.endTablePart(lastInBody, lastOnPage); } - /** - * Adds the area for the row background if any. - * @param row row for which to generate the background - * @param bpd block-progression-dimension of the row - * @param ipd inline-progression-dimension of the row - * @param yoffset Y offset at which to paint - */ - void addRowBackgroundArea(TableRow row, int bpd, int ipd, int yoffset) { - //Add row background if any - Block rowBackground = getRowArea(row); - if (rowBackground != null) { - rowBackground.setBPD(bpd); - rowBackground.setIPD(ipd); - rowBackground.setXOffset(this.startXOffset); - rowBackground.setYOffset(yoffset); - getTableLM().addChildArea(rowBackground); - TraitSetter.addBackground(rowBackground, - row.getCommonBorderPaddingBackground(), getTableLM()); - } - } - - /** * Sets the overall starting x-offset. Used for proper placement of cells. * @param startXOffset starting x-offset (table's start-indent) diff --git a/src/java/org/apache/fop/layoutmgr/table/TableContentPosition.java b/src/java/org/apache/fop/layoutmgr/table/TableContentPosition.java index e702c58a9..db34764b1 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableContentPosition.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableContentPosition.java @@ -22,6 +22,7 @@ package org.apache.fop.layoutmgr.table; import java.util.List; import org.apache.fop.fo.flow.table.EffRow; +import org.apache.fop.fo.flow.table.TableBody; import org.apache.fop.layoutmgr.LayoutManager; import org.apache.fop.layoutmgr.Position; @@ -79,6 +80,10 @@ class TableContentPosition extends Position { return row; } + TableBody getTableBody() { + return ((CellPart) cellParts.get(0)).pgu.getTableBody(); + } + /** * Returns a flag for this GridUnit. * @param which the requested flag diff --git a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java index 1cbc3e50a..0b3967643 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java @@ -19,8 +19,10 @@ package org.apache.fop.layoutmgr.table; +import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; +import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -79,7 +81,27 @@ public class TableLayoutManager extends BlockStackingLayoutManager private int halfBorderSeparationBPD; private int halfBorderSeparationIPD; - + + /** See {@link TableLayoutManager#registerColumnBackgroundArea(TableColumn, Block, int)}. */ + private List columnBackgroundAreas; + + /** + * Temporary holder of column background informations for a table-cell's area. + * + * @see TableLayoutManager#registerColumnBackgroundArea(TableColumn, Block, int) + */ + private static final class ColumnBackgroundInfo { + private TableColumn column; + private Block backgroundArea; + private int xShift; + + private ColumnBackgroundInfo(TableColumn column, Block backgroundArea, int xShift) { + this.column = column; + this.backgroundArea = backgroundArea; + this.xShift = xShift; + } + } + /** * Create a new table layout manager. * @param node the table FO @@ -263,7 +285,30 @@ public class TableLayoutManager extends BlockStackingLayoutManager resetSpaces(); return returnList; } - + + /** + * Registers the given area, that will be used to render the part of column background + * covered by a table-cell. If percentages are used to place the background image, the + * final bpd of the (fraction of) table that will be rendered on the current page must + * be known. The traits can't then be set when the areas for the cell are created + * since at that moment this bpd is yet unknown. So they will instead be set in + * TableLM's {@link #addAreas(PositionIterator, LayoutContext)} method. + * + * @param column the table-column element from which the cell gets background + * informations + * @param backgroundArea the block of the cell's dimensions that will hold the column + * background + * @param xShift additional amount by which the image must be shifted to be correctly + * placed (to counterbalance the cell's start border) + */ + void registerColumnBackgroundArea(TableColumn column, Block backgroundArea, int xShift) { + addBackgroundArea(backgroundArea); + if (columnBackgroundAreas == null) { + columnBackgroundAreas = new ArrayList(); + } + columnBackgroundAreas.add(new ColumnBackgroundInfo(column, backgroundArea, xShift)); + } + /** * The table area is a reference area that contains areas for * columns, bodies, rows and the contents are in cells. @@ -298,6 +343,17 @@ public class TableLayoutManager extends BlockStackingLayoutManager curBlockArea.setBPD(tableHeight); + if (columnBackgroundAreas != null) { + for (Iterator iter = columnBackgroundAreas.iterator(); iter.hasNext();) { + ColumnBackgroundInfo b = (ColumnBackgroundInfo) iter.next(); + TraitSetter.addBackground(b.backgroundArea, + b.column.getCommonBorderPaddingBackground(), this, + b.xShift, -b.backgroundArea.getYOffset(), + b.column.getColumnWidth().getValue(this), tableHeight); + } + columnBackgroundAreas.clear(); + } + if (getTable().isSeparateBorderModel()) { TraitSetter.addBorders(curBlockArea, getTable().getCommonBorderPaddingBackground(), @@ -366,6 +422,15 @@ public class TableLayoutManager extends BlockStackingLayoutManager } } + /** + * Adds the given area to this layout manager's area, without updating the used bpd. + * + * @param background an area + */ + void addBackgroundArea(Block background) { + curBlockArea.addChildArea(background); + } + /** {@inheritDoc} */ public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) { // TODO Auto-generated method stub diff --git a/status.xml b/status.xml index e622c589f..594b113e1 100644 --- a/status.xml +++ b/status.xml @@ -94,6 +94,17 @@

+ + Added support for background on fo:table-column and fo:table-header/footer/body elements. + + + Fixed the rendering of the area of a table corresponding to border-separation, which must + be filled with the background of the table, and not the rows. + + + Improved conformance: even if a table-cell spans several rows its background must + correspond to the first row spanned. + Slight improvement for thin lines in Java2D/AWT output. diff --git a/test/layoutengine/standard-testcases/table-cell_conditional-spaces_2.xml b/test/layoutengine/standard-testcases/table-cell_conditional-spaces_2.xml index 1a9940e38..9a0920cf1 100644 --- a/test/layoutengine/standard-testcases/table-cell_conditional-spaces_2.xml +++ b/test/layoutengine/standard-testcases/table-cell_conditional-spaces_2.xml @@ -168,94 +168,108 @@ + - - - + + + - - + + + + - - + + + + - - - + + + - - + + + + - - - + + + + - - + + + - - + + + + - - - + + + - + + - + + - + + diff --git a/test/layoutengine/standard-testcases/table-footer_omit-footer-at-break.xml b/test/layoutengine/standard-testcases/table-footer_omit-footer-at-break.xml index 7b2cc6904..59c60f72e 100644 --- a/test/layoutengine/standard-testcases/table-footer_omit-footer-at-break.xml +++ b/test/layoutengine/standard-testcases/table-footer_omit-footer-at-break.xml @@ -97,35 +97,35 @@ - - - + + + - - - - - - + + + + + + - + - - - + + + - - + + - - - - + + + + diff --git a/test/layoutengine/standard-testcases/table-header_omit-header-at-break.xml b/test/layoutengine/standard-testcases/table-header_omit-header-at-break.xml index 6834deeac..5ec211645 100644 --- a/test/layoutengine/standard-testcases/table-header_omit-header-at-break.xml +++ b/test/layoutengine/standard-testcases/table-header_omit-header-at-break.xml @@ -32,7 +32,7 @@ - + @@ -97,23 +97,23 @@ - - - + + + - - - - - - + + + + + + - - - - + + + + @@ -122,8 +122,8 @@ - - - + + + diff --git a/test/layoutengine/standard-testcases/table-header_table-footer_1.xml b/test/layoutengine/standard-testcases/table-header_table-footer_1.xml index df45e328b..00cbcde54 100644 --- a/test/layoutengine/standard-testcases/table-header_table-footer_1.xml +++ b/test/layoutengine/standard-testcases/table-header_table-footer_1.xml @@ -32,7 +32,7 @@ - + @@ -82,31 +82,41 @@ - + + + + + + - - - + + + - - - - + + + + - - - - - + - + + + + + + + + + + diff --git a/test/layoutengine/standard-testcases/table-header_table-footer_2.xml b/test/layoutengine/standard-testcases/table-header_table-footer_2.xml index 9cf3db4c0..7716dfe58 100644 --- a/test/layoutengine/standard-testcases/table-header_table-footer_2.xml +++ b/test/layoutengine/standard-testcases/table-header_table-footer_2.xml @@ -32,7 +32,7 @@ - + @@ -97,40 +97,40 @@ - - - + + + - - - - - - + + + + + + - - - - + + + + - - - + + + - - + + - - - - + + + + diff --git a/test/layoutengine/standard-testcases/table-row_background-image.xml b/test/layoutengine/standard-testcases/table-row_background-image.xml index fa29f1b8e..f4dc65ae7 100644 --- a/test/layoutengine/standard-testcases/table-row_background-image.xml +++ b/test/layoutengine/standard-testcases/table-row_background-image.xml @@ -34,10 +34,8 @@ - - - - + + @@ -82,8 +80,20 @@ - + + + + + + + + + + + + + diff --git a/test/layoutengine/standard-testcases/table-row_height.xml b/test/layoutengine/standard-testcases/table-row_height.xml index b158113f6..934afa69f 100644 --- a/test/layoutengine/standard-testcases/table-row_height.xml +++ b/test/layoutengine/standard-testcases/table-row_height.xml @@ -81,27 +81,37 @@ - - + + + - + + + + + - - - - + - + + + + + + + + + - - - - + + + + diff --git a/test/layoutengine/standard-testcases/table_backgrounds.xml b/test/layoutengine/standard-testcases/table_backgrounds.xml new file mode 100644 index 000000000..30564ecf7 --- /dev/null +++ b/test/layoutengine/standard-testcases/table_backgrounds.xml @@ -0,0 +1,392 @@ + + + + + +

+ This test checks backgrounds on table elements. +

+
+ + ../../resources/images/fop-logo-gray-8bit.png
+ ../../resources/images/bgimg72dpi.jpg + ../../resources/images/fop-logo-color-24bit.png + ../../../examples/fo/graphics/asf-logo.png + ../../../examples/fo/graphics/asf-logo.png +
../../resources/images/box1.png
+
+ + + + + + + + + + + Before the table. + + + + + + Header 1.1 + Header 1.1 + Header 1.1 + Header 1.1 + Header 1.1 + + + Header 1.2 + Header 1.2 + Header 1.2 + Header 1.2 + Header 1.2 + + + + + Footer 1.1 + Footer 1.1 + + + Footer 1.2 + Footer 1.2 + + + + + + Cell 1.1 + Cell 1.1 + Cell 1.1 + Cell 1.1 + + + Cell 1.2 + Cell 1.2 + + + + + Cell 2.2 + + + + + Cell 3.1 + Cell 3.1 + + + + + Cell 4.1 + Cell 4.1 + + + Cell 4.2 + + + + + + + Cell 5.1 + Cell 5.1 + Cell 5.1 + Cell 5.1 + Cell 5.1 Line 5 + Cell 5.1 + Cell 5.1 + Cell 5.1 + Cell 5.1 + Cell 5.1 Line 10 + Cell 5.1 + + + Cell 5.2 + Cell 5.2 + Cell 5.2 + Cell 5.2 + + + + + Cell 6.2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-- 2.39.5