diff options
Diffstat (limited to 'src/java/org')
26 files changed, 1153 insertions, 712 deletions
diff --git a/src/java/org/apache/fop/fo/flow/RetrieveMarker.java b/src/java/org/apache/fop/fo/flow/RetrieveMarker.java index f2679d04b..7356bc72e 100644 --- a/src/java/org/apache/fop/fo/flow/RetrieveMarker.java +++ b/src/java/org/apache/fop/fo/flow/RetrieveMarker.java @@ -23,12 +23,13 @@ import java.util.Iterator; import org.apache.fop.apps.FOPException; import org.apache.fop.fo.FONode; +import org.apache.fop.fo.FOText; import org.apache.fop.fo.FObj; import org.apache.fop.fo.FObjMixed; -import org.apache.fop.fo.FOText; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; import org.apache.fop.fo.flow.table.Table; +import org.apache.fop.fo.flow.table.TableFObj; import org.xml.sax.Locator; /** @@ -126,6 +127,12 @@ public class RetrieveMarker extends FObjMixed { getLocator(), pList, newPropertyList); + if (newChild instanceof TableFObj) { + // TODO calling startOfNode (and endOfNode, below) on other fobjs may + // have undesirable side-effects. This is really ugly and will need to + // be addressed sooner or later + ((TableFObj) newChild).startOfNode(); + } addChildTo(newChild, (FObj) newParent); if (newChild.getNameId() == FO_TABLE) { Table t = (Table) child; @@ -138,6 +145,10 @@ public class RetrieveMarker extends FObjMixed { } cloneSubtree(child.getChildNodes(), newChild, marker, newPropertyList); + if (newChild instanceof TableFObj) { + // TODO this is ugly + ((TableFObj) newChild).endOfNode(); + } } else if (child instanceof FOText) { FOText ft = (FOText) newChild; ft.bind(parentPropertyList); diff --git a/src/java/org/apache/fop/fo/flow/table/BorderResolver.java b/src/java/org/apache/fop/fo/flow/table/BorderResolver.java new file mode 100644 index 000000000..0a8f3d39e --- /dev/null +++ b/src/java/org/apache/fop/fo/flow/table/BorderResolver.java @@ -0,0 +1,56 @@ +/* + * 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.fo.flow.table; + +import java.util.List; + +/** + * A class dedicated to the resolution of borders in tables. It receives a series of + * events as the table is parsed and performs border resolution accordingly. + */ +interface BorderResolver { + + /** + * Receives notification of the end of a row. + * + * @param row the row that has just been finished + * @param container the FO element holding the given row + */ + void endRow(List/*<GridUnit>*/ row, TableCellContainer container); + + /** + * Receives notification of the start of a table-header/footer/body. + * + * @param part the part that has started + */ + void startPart(TableBody part); + + /** + * Receives notification of the end of a table-header/footer/body. + * + * @param part the part that has ended + */ + void endPart(TableBody part); + + /** + * Receives notification of the end of the table. + */ + void endTable(); +} diff --git a/src/java/org/apache/fop/fo/flow/table/BorderSpecification.java b/src/java/org/apache/fop/fo/flow/table/BorderSpecification.java new file mode 100644 index 000000000..214f9be07 --- /dev/null +++ b/src/java/org/apache/fop/fo/flow/table/BorderSpecification.java @@ -0,0 +1,71 @@ +/* + * 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.fo.flow.table; + +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; + +/** + * A border's informations, along with the FO element which declared it. Used for border + * resolution in the collapsing-border model. + */ +public/*TODO*/ class BorderSpecification { + + private BorderInfo borderInfo; + + private int holder; + + /** + * Creates a new border specification. + * + * @param borderInfo the border's informations + * @param holder the FO element declaring this border + */ + public/*TODO*/ BorderSpecification(BorderInfo borderInfo, int holder) { + this.borderInfo = borderInfo; + this.holder = holder; + } + + /** + * Returns this border's informations. + * + * @return this border's informations + */ + public/*TODO*/ BorderInfo getBorderInfo() { + return borderInfo; + } + + /** + * Returns the FO element declaring this border. + * + * @return one of {@link Constants#FO_TABLE}, {@link Constants#FO_TABLE_COLUMN}, + * {@link Constants#FO_TABLE_HEADER}, {@link Constants#FO_TABLE_FOOTER}, + * {@link Constants#FO_TABLE_BODY}, {@link Constants#FO_TABLE_ROW}, + * {@link Constants#FO_TABLE_CELL} + */ + public/*TODO*/ int getHolder() { + return holder; + } + + /** {@inheritDoc} */ + public String toString() { + return "{" + borderInfo + ", " + holder + "}"; + } +} diff --git a/src/java/org/apache/fop/fo/flow/table/CollapsingBorderResolver.java b/src/java/org/apache/fop/fo/flow/table/CollapsingBorderResolver.java new file mode 100644 index 000000000..732acb6be --- /dev/null +++ b/src/java/org/apache/fop/fo/flow/table/CollapsingBorderResolver.java @@ -0,0 +1,169 @@ +/* + * 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.fo.flow.table; + +import java.util.Iterator; +import java.util.List; + +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; +import org.apache.fop.layoutmgr.table.GridUnit; + +/** + * A class that implements the border-collapsing model. + */ +class CollapsingBorderResolver implements BorderResolver { + + private Table table; + + private List previousRow; + + /** + * The flow of rows is interrupted by the table-footer. Save the header's last row (if + * any) for resolution between it and the body's first row. + */ + private List previousRowSave; + + private TableBody currentTablePart; + + private boolean firstInTable; + + private boolean firstInPart; + + private List footerFirstRow; + + private List footerLastRow; + + private boolean inFooter; + + CollapsingBorderResolver(Table table) { + this.table = table; + firstInTable = true; + } + + /** {@inheritDoc} */ + public void endRow(List row, TableCellContainer container) { + // Resolve before- and after-borders for the table-row + if (container instanceof TableRow) { + TableRow tableRow = (TableRow) container; + for (Iterator iter = row.iterator(); iter.hasNext();) { + GridUnit gu = (GridUnit) iter.next(); + if (gu.getRowSpanIndex() == 0) { + gu.resolveBorder(CommonBorderPaddingBackground.BEFORE, tableRow); + } + if (gu.isLastGridUnitRowSpan()) { + gu.resolveBorder(CommonBorderPaddingBackground.AFTER, tableRow); + } + } + } + if (inFooter) { + if (footerFirstRow == null) { + footerFirstRow = row; + } + footerLastRow = row; + } else if (firstInTable) { + // Resolve border-before for the first row in the table + for (int i = 0; i < row.size(); i++) { + TableColumn column = table.getColumn(i); + ((GridUnit) row.get(i)).resolveBorder(CommonBorderPaddingBackground.BEFORE, column); + } + firstInTable = false; + } + if (firstInPart) { + // Resolve border-before for the first row in the part + for (int i = 0; i < row.size(); i++) { + ((GridUnit) row.get(i)).resolveBorder(CommonBorderPaddingBackground.BEFORE, + currentTablePart); + } + firstInPart = false; + } + if (previousRow != null) { + // Resolve after/before borders between rows + for (int i = 0; i < row.size(); i++) { + GridUnit gu = (GridUnit) row.get(i); + if (gu.getRowSpanIndex() == 0) { + GridUnit beforeGU = (GridUnit) previousRow.get(i); + gu.resolveBorder(beforeGU, CommonBorderPaddingBackground.BEFORE); + } + } + } + // Resolve start/end borders in the row + Iterator guIter = row.iterator(); + GridUnit gu = (GridUnit) guIter.next(); + gu.resolveBorder(CommonBorderPaddingBackground.START, container); + while (guIter.hasNext()) { + GridUnit guEnd = (GridUnit) guIter.next(); + if (gu.isLastGridUnitColSpan()) { + gu.resolveBorder(guEnd, CommonBorderPaddingBackground.END); + } + gu = guEnd; + } + gu.resolveBorder(CommonBorderPaddingBackground.END, container); + + previousRow = row; + } + + /** {@inheritDoc} */ + public void startPart(TableBody part) { + firstInPart = true; + currentTablePart = part; + if (part.isTableFooter()) { + inFooter = true; + previousRowSave = previousRow; + previousRow = null; + } + } + + /** {@inheritDoc} */ + public void endPart(TableBody part) { + // Resolve border-after for the last row in the part + for (int i = 0; i < previousRow.size(); i++) { + ((GridUnit) previousRow.get(i)) + .resolveBorder(CommonBorderPaddingBackground.AFTER, part); + } + if (inFooter) { + inFooter = false; + previousRow = previousRowSave; + } + } + + /** {@inheritDoc} */ + public void endTable() { + if (footerFirstRow != null) { + // Resolve after/before border between the last row of table-body and the + // first row of table-footer + for (int i = 0; i < footerFirstRow.size(); i++) { + GridUnit gu = (GridUnit) footerFirstRow.get(i); + GridUnit beforeGU = (GridUnit) previousRow.get(i); + gu.resolveBorder(beforeGU, CommonBorderPaddingBackground.BEFORE); + } + } + List lastRow; + if (footerLastRow != null) { + lastRow = footerLastRow; + } else { + lastRow = previousRow; + } + // Resolve border-after for the last row of the table + for (int i = 0; i < lastRow.size(); i++) { + TableColumn column = table.getColumn(i); + ((GridUnit) lastRow.get(i)).resolveBorder(CommonBorderPaddingBackground.AFTER, column); + } + } +} diff --git a/src/java/org/apache/fop/fo/flow/table/FixedColRowGroupBuilder.java b/src/java/org/apache/fop/fo/flow/table/FixedColRowGroupBuilder.java index 22e5c02b8..602ae84a6 100644 --- a/src/java/org/apache/fop/fo/flow/table/FixedColRowGroupBuilder.java +++ b/src/java/org/apache/fop/fo/flow/table/FixedColRowGroupBuilder.java @@ -19,6 +19,15 @@ package org.apache.fop.fo.flow.table; +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; + +import org.apache.fop.fo.ValidationException; +import org.apache.fop.layoutmgr.table.EmptyGridUnit; +import org.apache.fop.layoutmgr.table.GridUnit; +import org.apache.fop.layoutmgr.table.PrimaryGridUnit; + /** * A row group builder optimised for a fixed number of columns, known before the parsing @@ -26,10 +35,149 @@ package org.apache.fop.fo.flow.table; */ class FixedColRowGroupBuilder extends RowGroupBuilder { + /** Number of columns in the corresponding table. */ + private int numberOfColumns; + + /** 0-based, index in the row group. */ + private int currentRowIndex; + + /** The rows belonging to this row group. List of List of {@link GridUnit}s. */ + private List/*<List<GridUnit>>*/ rows; + + private boolean firstInTable = true; + + private boolean firstInPart = true; + + /** The last encountered row. This is the last row of the table if it has no footer. */ + private List lastRow; + + private BorderResolver borderResolver; + + private boolean inFooter; + + private List lastFooterRow; FixedColRowGroupBuilder(Table t) { super(t); numberOfColumns = t.getNumberOfColumns(); + if (t.isSeparateBorderModel()) { + borderResolver = new SeparateBorderResolver(); + } else { + borderResolver = new CollapsingBorderResolver(t); + } + initialize(); + } + + /** + * Prepares this builder for creating a new row group. + */ + private void initialize() { + rows = new ArrayList(); + currentRowIndex = 0; } + /** {@inheritDoc} */ + void addTableCell(TableCell cell) { + for (int i = rows.size(); i < currentRowIndex + cell.getNumberRowsSpanned(); i++) { + List effRow = new ArrayList(numberOfColumns); + for (int j = 0; j < numberOfColumns; j++) { + effRow.add(null); + } + rows.add(effRow); + } + int columnIndex = cell.getColumnNumber() - 1; + PrimaryGridUnit pgu = new PrimaryGridUnit(cell, table.getColumn(columnIndex), columnIndex, + currentRowIndex); + List row = (List) rows.get(currentRowIndex); + row.set(columnIndex, pgu); + // TODO + GridUnit[] cellRow = new GridUnit[cell.getNumberColumnsSpanned()]; + cellRow[0] = pgu; + for (int j = 1; j < cell.getNumberColumnsSpanned(); j++) { + GridUnit gu = new GridUnit(pgu, table.getColumn(columnIndex + j), + columnIndex + j, j, 0); + row.set(columnIndex + j, gu); + cellRow[j] = gu; + } + pgu.addRow(cellRow); + for (int i = 1; i < cell.getNumberRowsSpanned(); i++) { + row = (List) rows.get(currentRowIndex + i); + cellRow = new GridUnit[cell.getNumberColumnsSpanned()]; + for (int j = 0; j < cell.getNumberColumnsSpanned(); j++) { + GridUnit gu = new GridUnit(pgu, table.getColumn(columnIndex + j), + columnIndex + j, j, i); + row.set(columnIndex + j, gu); + cellRow[j] = gu; + } + pgu.addRow(cellRow); + } + } + + private static void setFlagForCols(int flag, List row) { + for (ListIterator iter = row.listIterator(); iter.hasNext();) { + ((GridUnit) iter.next()).setFlag(flag); + } + } + + /** {@inheritDoc} */ + void endRow(TableCellContainer container) { + List currentRow = (List) rows.get(currentRowIndex); + lastRow = currentRow; + // Fill gaps with empty grid units + for (int i = 0; i < numberOfColumns; i++) { + if (currentRow.get(i) == null) { + currentRow.set(i, new EmptyGridUnit(table, currentRowIndex, i)); + } + } + borderResolver.endRow(currentRow, container); + ((GridUnit) currentRow.get(0)).setFlag(GridUnit.IN_FIRST_COLUMN); + ((GridUnit) currentRow.get(numberOfColumns - 1)).setFlag(GridUnit.IN_LAST_COLUMN); + if (inFooter) { + lastFooterRow = currentRow; + } else if (firstInTable) { + setFlagForCols(GridUnit.FIRST_IN_TABLE, currentRow); + firstInTable = false; + } + if (firstInPart) { + setFlagForCols(GridUnit.FIRST_IN_PART, currentRow); + firstInPart = false; + } + if (currentRowIndex == rows.size() - 1) { + // Means that the current row has no cell spanning over following rows + container.getTablePart().addRowGroup(rows); + initialize(); + } else { + currentRowIndex++; + } + } + + /** {@inheritDoc} */ + void startTablePart(TableBody part) { + firstInPart = true; + inFooter = part.isTableFooter(); + borderResolver.startPart(part); + } + + /** {@inheritDoc} */ + void endTablePart(TableBody tableBody) throws ValidationException { + if (rows.size() > 0) { + throw new ValidationException( + "A table-cell is spanning more rows than available in its parent element."); + } + setFlagForCols(GridUnit.LAST_IN_PART, lastRow); + borderResolver.endPart(tableBody); + inFooter = false; + } + + /** {@inheritDoc} */ + void endTable(TableBody lastTablePart) { + List lastTableRow; + if (lastFooterRow != null) { + lastTableRow = lastFooterRow; + } else { + lastTableRow = lastRow; + } + setFlagForCols(GridUnit.LAST_IN_TABLE, lastTableRow); + borderResolver.endTable(); + } } diff --git a/src/java/org/apache/fop/fo/flow/table/RowGroupBuilder.java b/src/java/org/apache/fop/fo/flow/table/RowGroupBuilder.java index 2f9a009c2..7f09f7d46 100644 --- a/src/java/org/apache/fop/fo/flow/table/RowGroupBuilder.java +++ b/src/java/org/apache/fop/fo/flow/table/RowGroupBuilder.java @@ -19,12 +19,8 @@ package org.apache.fop.fo.flow.table; -import java.util.ArrayList; -import java.util.List; - import org.apache.fop.fo.ValidationException; import org.apache.fop.layoutmgr.table.GridUnit; -import org.apache.fop.layoutmgr.table.PrimaryGridUnit; /** * A class that creates groups of rows belonging to a same set of spans. The first row of @@ -34,16 +30,7 @@ import org.apache.fop.layoutmgr.table.PrimaryGridUnit; */ abstract class RowGroupBuilder { - /** Number of columns in the corresponding table. */ - protected int numberOfColumns; - - /** 0-based, index in the row group. */ - private int currentRowIndex; - - private Table table; - - /** The rows belonging to this row group. List of List of {@link GridUnit}s. */ - protected List rows; + protected Table table; /** * Creates and initialises a new builder for the given table. @@ -52,81 +39,48 @@ abstract class RowGroupBuilder { */ protected RowGroupBuilder(Table t) { table = t; - initialize(); } + /** - * Prepares this builder for creating a new row group. + * Adds a table-cell to the current row-group, creating {@link GridUnit}s accordingly. + * + * @param cell the cell to add */ - private void initialize() { - rows = new ArrayList(); - currentRowIndex = 0; - } + abstract void addTableCell(TableCell cell); /** - * Adds a table-cell to the row-group, creating {@link GridUnit}s accordingly. + * Receives notification of the end of the current row. If the current row finishes + * the row group, the {@link TableBody#addRowGroup(List)} method of the parent table + * part (i.e., the given container itself or its parent if this is a table-row) will + * be called * - * @param cell + * @param container the parent element of the current row */ - void addTableCell(TableCell cell) { - for (int i = rows.size(); i < currentRowIndex + cell.getNumberRowsSpanned(); i++) { - List effRow = new ArrayList(numberOfColumns); - for (int j = 0; j < numberOfColumns; j++) { - effRow.add(null); - } - rows.add(effRow); - } - int columnIndex = cell.getColumnNumber() - 1; - PrimaryGridUnit pgu = new PrimaryGridUnit(cell, table.getColumn(columnIndex), columnIndex, - currentRowIndex); - List row = (List) rows.get(currentRowIndex); - row.set(columnIndex, pgu); - for (int j = 1; j < cell.getNumberColumnsSpanned(); j++) { - row.set(j + columnIndex, - new GridUnit(pgu, table.getColumn(columnIndex + j), columnIndex + j, j)); - } - for (int i = 1; i < cell.getNumberRowsSpanned(); i++) { - row = (List) rows.get(currentRowIndex + i); - for (int j = 0; j < cell.getNumberColumnsSpanned(); j++) { - row.set(j + columnIndex, - new GridUnit(pgu, table.getColumn(columnIndex + j), columnIndex + j, j)); - } - } - - } + abstract void endRow(TableCellContainer container); /** - * Signals that a table row has just ended, potentially finishing the current row - * group. + * Receives notification of the start of a table-header/footer/body. * - * @param body the table-body containing the row. Its - * {@link TableBody#addRowGroup(List)} method will be called if the current row group - * is finished. + * @param part the part being started */ - void signalRowEnd(TableBody body) { - if (currentRowIndex == rows.size() - 1) { - // Means that the current row has no cell spanning over following rows - body.addRowGroup(rows); - initialize(); - } else { - currentRowIndex++; - } - } + abstract void startTablePart(TableBody part); /** - * Signals that the end of a table-header/footer/body has been reached. The current + * Receives notification of the end of a table-header/footer/body. The current * row-group is checked for emptiness. This row group builder is reset for handling * further possible table parts. * - * @param tableBody the table part being finished - * @throws ValidationException if a cell is spanning further than the given table part + * @param tableBody the table part being ended + * @throws ValidationException if a row-spanning cell overflows the given table part */ - void signalEndOfPart(TableBody tableBody) throws ValidationException { - if (rows.size() > 0) { - throw new ValidationException( - "A table-cell is spanning more rows than available in its parent element."); - } - initialize(); - } + abstract void endTablePart(TableBody tableBody) throws ValidationException; + /** + * Receives notification of the end of the table. + * + * @param lastTablePart the last part of the table + * @throws ValidationException if a row-spanning cell overflows one of the table's parts + */ + abstract void endTable(TableBody lastTablePart) throws ValidationException; } diff --git a/src/java/org/apache/fop/fo/flow/table/SeparateBorderResolver.java b/src/java/org/apache/fop/fo/flow/table/SeparateBorderResolver.java new file mode 100644 index 000000000..37e3cb6e8 --- /dev/null +++ b/src/java/org/apache/fop/fo/flow/table/SeparateBorderResolver.java @@ -0,0 +1,44 @@ +/* + * 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.fo.flow.table; + +import java.util.List; + +/** + * A resolver for the separate-border model. Basically this class does nothing. + */ +class SeparateBorderResolver implements BorderResolver { + + /** {@inheritDoc} */ + public void endRow(List row, TableCellContainer container) { + } + + /** {@inheritDoc} */ + public void startPart(TableBody part) { + } + + /** {@inheritDoc} */ + public void endPart(TableBody part) { + } + + /** {@inheritDoc} */ + public void endTable() { + } +} diff --git a/src/java/org/apache/fop/fo/flow/table/Table.java b/src/java/org/apache/fop/fo/flow/table/Table.java index 07fc95f7e..b2f240c72 100644 --- a/src/java/org/apache/fop/fo/flow/table/Table.java +++ b/src/java/org/apache/fop/fo/flow/table/Table.java @@ -20,7 +20,6 @@ package org.apache.fop.fo.flow.table; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import org.apache.fop.apps.FOPException; @@ -154,7 +153,7 @@ public class Table extends TableFObj implements ColumnNumberManagerHolder { /** * {@inheritDoc} */ - protected void startOfNode() throws FOPException { + public void startOfNode() throws FOPException { super.startOfNode(); getFOEventHandler().startTable(this); } @@ -211,7 +210,7 @@ public class Table extends TableFObj implements ColumnNumberManagerHolder { /** * {@inheritDoc} */ - protected void endOfNode() throws FOPException { + public void endOfNode() throws FOPException { if (!tableBodyFound) { missingChildElementError( @@ -219,6 +218,11 @@ public class Table extends TableFObj implements ColumnNumberManagerHolder { + ",table-body+)"); } if (!inMarker()) { + if (tableFooter != null) { + rowGroupBuilder.endTable(tableFooter); + } else { + rowGroupBuilder.endTable((TableBody) getChildNodes().lastNode()); + } /* clean up */ for (int i = columns.size(); --i >= 0;) { TableColumn col = (TableColumn) columns.get(i); @@ -278,6 +282,13 @@ public class Table extends TableFObj implements ColumnNumberManagerHolder { } } + protected void setCollapsedBorders() { + createBorder(CommonBorderPaddingBackground.START); + createBorder(CommonBorderPaddingBackground.END); + createBorder(CommonBorderPaddingBackground.BEFORE); + createBorder(CommonBorderPaddingBackground.AFTER); + } + private void finalizeColumns() throws FOPException { for (int i = 0; i < columns.size(); i++) { if (columns.get(i) == null) { @@ -305,32 +316,6 @@ public class Table extends TableFObj implements ColumnNumberManagerHolder { for (int i = columns.size() + 1; i <= columnNumber; i++) { columns.add(createImplicitColumn(i)); } - ((VariableColRowGroupBuilder) rowGroupBuilder).ensureNumberOfColumns(columnNumber); - if (tableHeader != null) { - for (Iterator iter = tableHeader.getRowGroups().iterator(); iter.hasNext();) { - VariableColRowGroupBuilder.fillWithEmptyGridUnits((List) iter.next(), - columnNumber); - } - } - if (tableFooter != null) { - for (Iterator iter = tableFooter.getRowGroups().iterator(); iter.hasNext();) { - VariableColRowGroupBuilder.fillWithEmptyGridUnits((List) iter.next(), - columnNumber); - } - } - FONodeIterator bodyIter = getChildNodes(); - if (bodyIter != null) { - while (bodyIter.hasNext()) { - FONode node = bodyIter.nextNode(); - if (node instanceof TableBody) { // AFAIK, may be a marker - for (Iterator iter = ((TableBody) node).getRowGroups().iterator(); - iter.hasNext();) { - VariableColRowGroupBuilder.fillWithEmptyGridUnits((List) iter.next(), - columnNumber); - } - } - } - } } private TableColumn createImplicitColumn(int colNumber) @@ -342,6 +327,9 @@ public class Table extends TableFObj implements ColumnNumberManagerHolder { implicitColumn.bind(pList); implicitColumn.setColumnWidth(new TableColLength(1.0, implicitColumn)); implicitColumn.setColumnNumber(colNumber); + if (!isSeparateBorderModel()) { + implicitColumn.setCollapsedBorders(collapsingBorderModel); // TODO + } return implicitColumn; } @@ -399,7 +387,7 @@ public class Table extends TableFObj implements ColumnNumberManagerHolder { * @param index index of the column to be retrieved, 0-based * @return the corresponding column (may be an implicitly created column) */ - TableColumn getColumn(int index) { + public/*TODO*/ TableColumn getColumn(int index) { return (TableColumn) columns.get(index); } diff --git a/src/java/org/apache/fop/fo/flow/table/TableBody.java b/src/java/org/apache/fop/fo/flow/table/TableBody.java index 7e4ee2fb0..ece1f0049 100644 --- a/src/java/org/apache/fop/fo/flow/table/TableBody.java +++ b/src/java/org/apache/fop/fo/flow/table/TableBody.java @@ -50,15 +50,15 @@ public class TableBody extends TableCellContainer { protected boolean tableRowsFound = false; protected boolean tableCellsFound = false; - /** - * used for initial values of column-number property - */ private boolean firstRow = true; private boolean rowsStarted = false; private boolean lastCellEndsRow = true; + /** The last encountered table-row. */ + private TableRow lastRow; + private List rowGroups = new LinkedList(); /** @@ -101,14 +101,15 @@ public class TableBody extends TableCellContainer { /** * {@inheritDoc} */ - protected void startOfNode() throws FOPException { + public void startOfNode() throws FOPException { + super.startOfNode(); getFOEventHandler().startBody(this); } /** * {@inheritDoc} */ - protected void endOfNode() throws FOPException { + public void endOfNode() throws FOPException { if (!inMarker()) { pendingSpans = null; @@ -130,16 +131,25 @@ public class TableBody extends TableCellContainer { } } + /** {@inheritDoc} */ + TableBody getTablePart() { + return this; + } + protected void finishLastRowGroup() throws ValidationException { - RowGroupBuilder rowGroupBuilder = getTable().getRowGroupBuilder(); - if (tableRowsFound || !lastCellEndsRow) { - rowGroupBuilder.signalRowEnd(this); - } - try { - rowGroupBuilder.signalEndOfPart(this); - } catch (ValidationException e) { - e.setLocator(locator); - throw e; + if (!inMarker()) { + RowGroupBuilder rowGroupBuilder = getTable().getRowGroupBuilder(); + if (tableRowsFound) { + rowGroupBuilder.endRow(lastRow); + } else if (!lastCellEndsRow) { + rowGroupBuilder.endRow(this); + } + try { + rowGroupBuilder.endTablePart(this); + } catch (ValidationException e) { + e.setLocator(locator); + throw e; + } } } @@ -184,13 +194,19 @@ public class TableBody extends TableCellContainer { if (!inMarker()) { switch (child.getNameId()) { case FO_TABLE_ROW: - if (rowsStarted) { + if (!rowsStarted) { + getTable().getRowGroupBuilder().startTablePart(this); + } else { columnNumberManager.prepareForNextRow(pendingSpans); - getTable().getRowGroupBuilder().signalRowEnd(this); + getTable().getRowGroupBuilder().endRow(lastRow); } rowsStarted = true; + lastRow = (TableRow) child; break; case FO_TABLE_CELL: + if (!rowsStarted) { + getTable().getRowGroupBuilder().startTablePart(this); + } rowsStarted = true; TableCell cell = (TableCell) child; addTableCellChild(cell, firstRow); @@ -198,7 +214,7 @@ public class TableBody extends TableCellContainer { if (lastCellEndsRow) { firstRow = false; columnNumberManager.prepareForNextRow(pendingSpans); - getTable().getRowGroupBuilder().signalRowEnd(this); + getTable().getRowGroupBuilder().endRow(this); } break; default: @@ -208,11 +224,20 @@ public class TableBody extends TableCellContainer { super.addChildNode(child); } + /** {inheritDoc} */ + protected void setCollapsedBorders() { + Table table = (Table) parent; + createBorder(CommonBorderPaddingBackground.START, table); + createBorder(CommonBorderPaddingBackground.END, table); + createBorder(CommonBorderPaddingBackground.BEFORE); + createBorder(CommonBorderPaddingBackground.AFTER); + } + void addRowGroup(List rowGroup) { rowGroups.add(rowGroup); } - List getRowGroups() { + public List getRowGroups() { return rowGroups; } @@ -235,6 +260,10 @@ public class TableBody extends TableCellContainer { return FO_TABLE_BODY; } + protected boolean isTableFooter() { + return false; + } + /** * @param obj table row in question * @return true if the given table row is the first row of this body. @@ -249,7 +278,7 @@ public class TableBody extends TableCellContainer { firstRow = false; if (!lastCellEndsRow) { columnNumberManager.prepareForNextRow(pendingSpans); - getTable().getRowGroupBuilder().signalRowEnd(this); + getTable().getRowGroupBuilder().endRow(this); } } rowsStarted = true; diff --git a/src/java/org/apache/fop/fo/flow/table/TableCell.java b/src/java/org/apache/fop/fo/flow/table/TableCell.java index 34c6266a7..f85d0e97e 100644 --- a/src/java/org/apache/fop/fo/flow/table/TableCell.java +++ b/src/java/org/apache/fop/fo/flow/table/TableCell.java @@ -90,7 +90,7 @@ public class TableCell extends TableFObj { /** * {@inheritDoc} */ - protected void startOfNode() throws FOPException { + public void startOfNode() throws FOPException { super.startOfNode(); getFOEventHandler().startCell(this); } @@ -100,7 +100,7 @@ public class TableCell extends TableFObj { * FOEventHandler that we are at the end of the flow. * {@inheritDoc} */ - protected void endOfNode() throws FOPException { + public void endOfNode() throws FOPException { if (!blockItemFound) { if (getUserAgent().validateStrictly()) { missingChildElementError("marker* (%block;)+"); @@ -135,6 +135,21 @@ public class TableCell extends TableFObj { } /** {@inheritDoc} */ + protected void setCollapsedBorders() { + createBorder(CommonBorderPaddingBackground.BEFORE); + createBorder(CommonBorderPaddingBackground.AFTER); + Table table = getTable(); + if (table.hasExplicitColumns()) { + TableColumn col = table.getColumn(getColumnNumber() - 1); + createBorder(CommonBorderPaddingBackground.START, col); + createBorder(CommonBorderPaddingBackground.END, col); + } else { + createBorder(CommonBorderPaddingBackground.START); + createBorder(CommonBorderPaddingBackground.END); + } + } + + /** {@inheritDoc} */ public boolean generatesReferenceAreas() { return true; } diff --git a/src/java/org/apache/fop/fo/flow/table/TableCellContainer.java b/src/java/org/apache/fop/fo/flow/table/TableCellContainer.java index ebb066c5c..7c91be351 100644 --- a/src/java/org/apache/fop/fo/flow/table/TableCellContainer.java +++ b/src/java/org/apache/fop/fo/flow/table/TableCellContainer.java @@ -93,6 +93,13 @@ public abstract class TableCellContainer extends TableFObj implements ColumnNumb } } + /** + * Returns the enclosing table-header/footer/body of this container. + * + * @return <code>this</code> for TableBody, or the parent element for TableRow + */ + abstract TableBody getTablePart(); + /** {@inheritDoc} */ public ColumnNumberManager getColumnNumberManager() { return columnNumberManager; diff --git a/src/java/org/apache/fop/fo/flow/table/TableColumn.java b/src/java/org/apache/fop/fo/flow/table/TableColumn.java index aa2dfcb0a..aeb401893 100644 --- a/src/java/org/apache/fop/fo/flow/table/TableColumn.java +++ b/src/java/org/apache/fop/fo/flow/table/TableColumn.java @@ -31,6 +31,7 @@ import org.apache.fop.fo.expr.PropertyException; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.Property; import org.apache.fop.fo.properties.TableColLength; +import org.apache.fop.layoutmgr.table.CollapsingBorderModel; /** * Class modelling the fo:table-column object. @@ -114,14 +115,27 @@ public class TableColumn extends TableFObj { /** * {@inheritDoc} */ - protected void startOfNode() throws FOPException { + public void startOfNode() throws FOPException { + super.startOfNode(); getFOEventHandler().startColumn(this); } - /** - * {@inheritDoc} - */ - protected void endOfNode() throws FOPException { + void setCollapsedBorders(CollapsingBorderModel collapsingBorderModel) { + this.collapsingBorderModel = collapsingBorderModel; + setCollapsedBorders(); + } + + /** {@inheritDoc} */ + protected void setCollapsedBorders() { + Table table = (Table) parent; + createBorder(CommonBorderPaddingBackground.BEFORE, table); + createBorder(CommonBorderPaddingBackground.AFTER, table); + createBorder(CommonBorderPaddingBackground.START); + createBorder(CommonBorderPaddingBackground.END); + } + + /** {@inheritDoc} */ + public void endOfNode() throws FOPException { getFOEventHandler().endColumn(this); } diff --git a/src/java/org/apache/fop/fo/flow/table/TableFObj.java b/src/java/org/apache/fop/fo/flow/table/TableFObj.java index 53995464b..984f242f2 100644 --- a/src/java/org/apache/fop/fo/flow/table/TableFObj.java +++ b/src/java/org/apache/fop/fo/flow/table/TableFObj.java @@ -30,6 +30,7 @@ import org.apache.fop.fo.expr.PropertyException; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.NumberProperty; import org.apache.fop.fo.properties.Property; +import org.apache.fop.layoutmgr.table.CollapsingBorderModel; /** * Common base class for table-related FOs @@ -41,6 +42,10 @@ public abstract class TableFObj extends FObj { private Numeric borderEndPrecedence; private Numeric borderStartPrecedence; + public/*TODO*/ BorderSpecification[] resolvedBorders = new BorderSpecification[4]; // TODO + + CollapsingBorderModel collapsingBorderModel; + /** * Main constructor * @@ -122,9 +127,7 @@ public abstract class TableFObj extends FObj { super(propId); } - /** - * {@inheritDoc} - */ + /** {@inheritDoc} */ public Property make(PropertyList propertyList) throws PropertyException { FObj fo = propertyList.getFObj(); @@ -189,4 +192,55 @@ public abstract class TableFObj extends FObj { return p; } } + + /** {@inheritDoc} */ + public void startOfNode() throws FOPException { + super.startOfNode(); + Table table = getTable(); + if (!inMarker() && !table.isSeparateBorderModel()) { + collapsingBorderModel = CollapsingBorderModel.getBorderModelFor(table + .getBorderCollapse()); + resolvedBorders = new BorderSpecification[4]; + setCollapsedBorders(); + } + } + + /* + * TODO made public so that RetrieveMarker can access it. + */ + /** {@inheritDoc} */ + public void endOfNode() throws FOPException { + super.endOfNode(); + } + + /** + * Prepares the borders of this element if the collapsing-border model is in use. + * Conflict resolution with parent elements is done where applicable. + */ + protected abstract void setCollapsedBorders(); + + /** + * Creates a BorderSpecification from the border set on the given side. If no border + * is set, a BorderSpecification with border-style none is created. + * + * @param side one of CommonBorderPaddingBackground.BEFORE|AFTER|START|END + */ + protected void createBorder(int side) { + resolvedBorders[side] = new BorderSpecification(getCommonBorderPaddingBackground() + .getBorderInfo(side), getNameId()); + } + + /** + * Creates a BorderSpecification from the border set on the given side, performing + * conflict resolution with the same border on the given object. + * + * @param side one of CommonBorderPaddingBackground.BEFORE|AFTER|START|END + * @param competitor a parent table element whose side coincides with the given side + * on this element + */ + protected void createBorder(int side, TableFObj competitor) { + createBorder(side); + resolvedBorders[side] = collapsingBorderModel.determineWinner(resolvedBorders[side], + competitor.resolvedBorders[side]); + } } diff --git a/src/java/org/apache/fop/fo/flow/table/TableFooter.java b/src/java/org/apache/fop/fo/flow/table/TableFooter.java index a7ee21406..e886d2ee1 100644 --- a/src/java/org/apache/fop/fo/flow/table/TableFooter.java +++ b/src/java/org/apache/fop/fo/flow/table/TableFooter.java @@ -39,14 +39,15 @@ public class TableFooter extends TableBody { /** * {@inheritDoc} */ - protected void startOfNode() throws FOPException { + public void startOfNode() throws FOPException { + super.startOfNode(); //getFOEventHandler().startBody(this); } /** * {@inheritDoc} */ - protected void endOfNode() throws FOPException { + public void endOfNode() throws FOPException { // getFOEventHandler().endFooter(this); if (!(tableRowsFound || tableCellsFound)) { missingChildElementError("marker* (table-row+|table-cell+)"); @@ -65,4 +66,9 @@ public class TableFooter extends TableBody { public int getNameId() { return FO_TABLE_FOOTER; } + + /** {@inheritDoc} */ + protected boolean isTableFooter() { + return true; + } } diff --git a/src/java/org/apache/fop/fo/flow/table/TableHeader.java b/src/java/org/apache/fop/fo/flow/table/TableHeader.java index bc9d88952..01ada2aa7 100644 --- a/src/java/org/apache/fop/fo/flow/table/TableHeader.java +++ b/src/java/org/apache/fop/fo/flow/table/TableHeader.java @@ -39,14 +39,15 @@ public class TableHeader extends TableBody { /** * {@inheritDoc} */ - protected void startOfNode() throws FOPException { + public void startOfNode() throws FOPException { + super.startOfNode(); //getFOEventHandler().startHeader(this); } /** * {@inheritDoc} */ - protected void endOfNode() throws FOPException { + public void endOfNode() throws FOPException { // getFOEventHandler().endHeader(this); if (!(tableRowsFound || tableCellsFound)) { missingChildElementError("marker* (table-row+|table-cell+)"); diff --git a/src/java/org/apache/fop/fo/flow/table/TableRow.java b/src/java/org/apache/fop/fo/flow/table/TableRow.java index edc8d13df..a025f92fd 100644 --- a/src/java/org/apache/fop/fo/flow/table/TableRow.java +++ b/src/java/org/apache/fop/fo/flow/table/TableRow.java @@ -97,7 +97,7 @@ public class TableRow extends TableCellContainer { /** * {@inheritDoc} */ - protected void startOfNode() throws FOPException { + public void startOfNode() throws FOPException { super.startOfNode(); getFOEventHandler().startRow(this); } @@ -105,7 +105,7 @@ public class TableRow extends TableCellContainer { /** * {@inheritDoc} */ - protected void endOfNode() throws FOPException { + public void endOfNode() throws FOPException { if (firstChild == null) { missingChildElementError("(table-cell+)"); } @@ -128,6 +128,25 @@ public class TableRow extends TableCellContainer { } } + /** {@inheritDoc} */ + TableBody getTablePart() { + return (TableBody) parent; + } + + /** {@inheritDoc} */ + boolean isTableRow() { + return true; + } + + /** {inheritDoc} */ + protected void setCollapsedBorders() { + TableBody body = (TableBody) parent; + createBorder(CommonBorderPaddingBackground.START, body); + createBorder(CommonBorderPaddingBackground.END, body); + createBorder(CommonBorderPaddingBackground.BEFORE); + createBorder(CommonBorderPaddingBackground.AFTER); + } + /** @return the "break-after" property. */ public int getBreakAfter() { return breakAfter; diff --git a/src/java/org/apache/fop/fo/flow/table/VariableColRowGroupBuilder.java b/src/java/org/apache/fop/fo/flow/table/VariableColRowGroupBuilder.java index 53b7bfca1..e35ad5c27 100644 --- a/src/java/org/apache/fop/fo/flow/table/VariableColRowGroupBuilder.java +++ b/src/java/org/apache/fop/fo/flow/table/VariableColRowGroupBuilder.java @@ -19,9 +19,12 @@ package org.apache.fop.fo.flow.table; +import java.util.Iterator; +import java.util.LinkedList; import java.util.List; -import org.apache.fop.layoutmgr.table.EmptyGridUnit; +import org.apache.fop.fo.ValidationException; + /** * A row group builder accommodating a variable number of columns. More flexible, but less @@ -31,34 +34,69 @@ class VariableColRowGroupBuilder extends RowGroupBuilder { VariableColRowGroupBuilder(Table t) { super(t); - numberOfColumns = 1; } /** - * Fills the given row group with empty grid units if necessary, so that it matches - * the given number of columns. - * - * @param rowGroup a List of List of GridUnit - * @param numberOfColumns the number of columns that the row group must have + * Each event is recorded and will be played once the table is finished, and the final + * number of columns known. */ - static void fillWithEmptyGridUnits(List rowGroup, int numberOfColumns) { - for (int i = 0; i < rowGroup.size(); i++) { - List effRow = (List) rowGroup.get(i); - for (int j = effRow.size(); j < numberOfColumns; j++) { - effRow.add(new EmptyGridUnit(null, null, null, j)); + private static interface Event { + /** + * Plays this event + * + * @param rowGroupBuilder the delegate builder which will actually create the row + * groups + * @throws ValidationException if a row-spanning cell overflows its parent body + */ + void play(RowGroupBuilder rowGroupBuilder) throws ValidationException; + } + + /** The queue of events sent to this builder. */ + private List events = new LinkedList(); + + /** {@inheritDoc} */ + void addTableCell(final TableCell cell) { + events.add(new Event() { + public void play(RowGroupBuilder rowGroupBuilder) { + rowGroupBuilder.addTableCell(cell); } - } + }); } - /** - * Updates the current row group to match the given number of columns, by adding empty - * grid units if necessary. - * - * @param numberOfColumns new number of columns - */ - void ensureNumberOfColumns(int numberOfColumns) { - this.numberOfColumns = numberOfColumns; - fillWithEmptyGridUnits(rows, numberOfColumns); + /** {@inheritDoc} */ + void endRow(final TableCellContainer container) { + events.add(new Event() { + public void play(RowGroupBuilder rowGroupBuilder) { + rowGroupBuilder.endRow(container); + } + }); } + /** {@inheritDoc} */ + void startTablePart(final TableBody part) { + events.add(new Event() { + public void play(RowGroupBuilder rowGroupBuilder) { + rowGroupBuilder.startTablePart(part); + } + }); + } + + /** {@inheritDoc} */ + void endTablePart(final TableBody tableBody) throws ValidationException { + // TODO catch the ValidationException sooner? + events.add(new Event() { + public void play(RowGroupBuilder rowGroupBuilder) throws ValidationException { + rowGroupBuilder.endTablePart(tableBody); + } + }); + } + + /** {@inheritDoc} */ + void endTable(final TableBody lastTablePart) throws ValidationException { + RowGroupBuilder delegate = new FixedColRowGroupBuilder(table); + for (Iterator eventIter = events.iterator(); eventIter.hasNext();) { + ((Event) eventIter.next()).play(delegate); + } + delegate.endTable(lastTablePart); + } } diff --git a/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java b/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java index d021ec922..114dadb94 100755 --- a/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java +++ b/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java @@ -83,7 +83,7 @@ public class CommonBorderPaddingBackground { private Color mColor; // Border color private CondLengthProperty mWidth; - BorderInfo(int style, CondLengthProperty width, Color color) { + public BorderInfo(int style, CondLengthProperty width, Color color) { mStyle = style; mWidth = width; mColor = color; @@ -227,7 +227,11 @@ public class CommonBorderPaddingBackground { * @return the border info for a side */ public BorderInfo getBorderInfo(int side) { - return this.borderInfo[side]; + if (this.borderInfo[side] == null) { + return new BorderInfo(Constants.EN_NONE, null, null); // TODO + } else { + return this.borderInfo[side]; + } } /** diff --git a/src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModel.java b/src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModel.java index 5c60ee6fd..5cfe5e382 100644 --- a/src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModel.java +++ b/src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModel.java @@ -20,8 +20,8 @@ package org.apache.fop.layoutmgr.table; import org.apache.fop.fo.Constants; +import org.apache.fop.fo.flow.table.BorderSpecification; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; -import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; /** * This class is a superclass for the two collapsing border models defined @@ -37,10 +37,7 @@ public abstract class CollapsingBorderModel { protected static final int START = CommonBorderPaddingBackground.START; /** end side */ protected static final int END = CommonBorderPaddingBackground.END; - - /** Flag: current grid unit is either start or end of the table. */ - public static final int VERTICAL_START_END_OF_TABLE = 1; - + /** Indicates that the cell is/starts in the first row being painted on a particular page */ //public static final int FIRST_ROW_IN_TABLE_PART = 1; /** Indicates that the cell is/ends in the last row being painted on a particular page */ @@ -53,7 +50,7 @@ public abstract class CollapsingBorderModel { //These statics are used singleton-style. No MT issues here. private static CollapsingBorderModel collapse = null; private static CollapsingBorderModel collapseWithPrecedence = null; - + /** * @param borderCollapse border collapse control * @return the border model for the cell @@ -74,12 +71,12 @@ public abstract class CollapsingBorderModel { throw new IllegalArgumentException("Illegal border-collapse mode."); } } - + /** * @param side the side on the current cell * @return the adjacent side on the neighbouring cell */ - public static int getOtherSide(int side) { + static int getOtherSide(int side) { switch (side) { case CommonBorderPaddingBackground.BEFORE: return CommonBorderPaddingBackground.AFTER; @@ -93,23 +90,32 @@ public abstract class CollapsingBorderModel { throw new IllegalArgumentException("Illegal parameter: side"); } } - + /** * @param side the side to investigate * @return true if the adjacent cell is before or after */ protected boolean isVerticalRelation(int side) { - return (side == CommonBorderPaddingBackground.BEFORE + return (side == CommonBorderPaddingBackground.BEFORE || side == CommonBorderPaddingBackground.AFTER); } - + private static int compareInt(int value1, int value2) { + if (value1 < value2) { + return -1; + } else if (value1 == value2) { + return 0; + } else { + return 1; + } + } + /** * See rule 4 in 6.7.10 for the collapsing border model. * @param style the border style to get the preference value for * @return the preference value of the style */ - public int getPreferenceValue(int style) { + private static int getStylePreferenceValue(int style) { switch (style) { case Constants.EN_DOUBLE: return 0; case Constants.EN_SOLID: return -1; @@ -122,14 +128,62 @@ public abstract class CollapsingBorderModel { default: throw new IllegalStateException("Illegal border style: " + style); } } - + + /** + * Compares the two given styles (see {@link Constants}). + * + * @param style1 a style constant + * @param style2 another style constant + * @return a value < 0 if style1 has less priority than style2, 0 if both are + * equal, a value > 0 if style1 has more priority than style2 + */ + static int compareStyles(int style1, int style2) { + int value1 = getStylePreferenceValue(style1); + int value2 = getStylePreferenceValue(style2); + return compareInt(value1, value2); + } + + private static int getHolderPreferenceValue(int id) { + switch (id) { + case Constants.FO_TABLE_CELL: return 0; + case Constants.FO_TABLE_ROW: return -1; + case Constants.FO_TABLE_HEADER: + case Constants.FO_TABLE_FOOTER: + case Constants.FO_TABLE_BODY: + return -2; + case Constants.FO_TABLE_COLUMN: return -3; + // TODO colgroup + case Constants.FO_TABLE: return -4; + default: throw new IllegalStateException(); + } + } + + /** + * Compares the two given FO ids ({@link Constants}.FO*) in terms of border + * declaration. + * + * @param id1 a FO id ({@link Constants#FO_TABLE}, {@link Constants#FO_TABLE_BODY}, + * etc.) + * @param id2 another FO id + * @return a value < 0 if id1 has less priority than id2, 0 if both are equal, a + * value > 0 if id1 has more priority than id2 + */ + static int compareFOs(int id1, int id2) { + int p1 = getHolderPreferenceValue(id1); + int p2 = getHolderPreferenceValue(id2); + return compareInt(p1, p2); + } + /** - * Determines the winning BorderInfo. - * @param current grid unit of the current element - * @param neighbour grid unit of the neighbouring element - * @return the winning BorderInfo + * Returns the border which wins the border conflict resolution. In case the two + * borders are equivalent (identical, or only the color is different), null is + * returned. + * + * @param border1 a border specification + * @param border2 another border specification + * @return the winning border, null if the two borders are equivalent */ - public abstract BorderInfo determineWinner( - GridUnit current, GridUnit neighbour, int side, int flags); - + public abstract BorderSpecification determineWinner(BorderSpecification border1, + BorderSpecification border2); + } diff --git a/src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModelEyeCatching.java b/src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModelEyeCatching.java index ddbb20c47..5f979c986 100644 --- a/src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModelEyeCatching.java +++ b/src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModelEyeCatching.java @@ -20,11 +20,7 @@ package org.apache.fop.layoutmgr.table; import org.apache.fop.fo.Constants; -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.flow.table.BorderSpecification; import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; /** @@ -34,268 +30,45 @@ import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; */ public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel { - public BorderInfo determineWinner(GridUnit currentGridUnit, - GridUnit otherGridUnit, int side, int flags) { - final boolean vertical = isVerticalRelation(side); - final int otherSide = getOtherSide(side); - - //Get cells - TableCell currentCell = currentGridUnit.getCell(); - TableCell otherCell = null; - if (otherGridUnit != null) { - otherCell = otherGridUnit.getCell(); - } - - //Get rows - TableRow currentRow = currentGridUnit.getRow(); - TableRow otherRow = null; - if (vertical && otherCell != null) { - otherRow = otherGridUnit.getRow(); - } - - //get bodies - TableBody currentBody = currentGridUnit.getBody(); - TableBody otherBody = null; - if (otherRow != null) { - otherBody = otherGridUnit.getBody(); - } - - //get columns - TableColumn currentColumn = currentGridUnit.getColumn(); - TableColumn otherColumn = null; - if (otherGridUnit != null) { - otherColumn = otherGridUnit.getColumn(); - } - - //TODO get column groups - - //Get table - Table table = currentGridUnit.getTable(); - - //---------------------------------------------------------------------- - //We're creating two arrays containing the applicable BorderInfos for - //each cell in question. - //0 = cell, 1 = row, 2 = row group (body), 3 = column, - //4 = col group (spanned column, see 6.7.3), 5 = table - - BorderInfo[] current = new BorderInfo[6]; - BorderInfo[] other = new BorderInfo[6]; - //cell - current[0] = currentGridUnit.getOriginalBorderInfoForCell(side); - if (otherGridUnit != null) { - other[0] = otherGridUnit.getOriginalBorderInfoForCell(otherSide); - } - if ((currentRow != null) - && (side == BEFORE - || side == AFTER - || (currentGridUnit.getFlag(GridUnit.IN_FIRST_COLUMN) && side == START) - || (currentGridUnit.getFlag(GridUnit.IN_LAST_COLUMN) && side == END))) { - //row - current[1] = currentRow.getCommonBorderPaddingBackground().getBorderInfo(side); - } - if (otherRow != null) { - //row - other[1] = otherRow.getCommonBorderPaddingBackground().getBorderInfo(otherSide); - } - if (currentBody != null - && ((side == BEFORE && currentGridUnit.getFlag(GridUnit.FIRST_IN_PART)) - || (side == AFTER && currentGridUnit.getFlag(GridUnit.LAST_IN_PART)) - || (currentGridUnit.getFlag(GridUnit.IN_FIRST_COLUMN) && side == START) - || (currentGridUnit.getFlag(GridUnit.IN_LAST_COLUMN) && side == END))) { - //row group (=body, table-header or table-footer) - current[2] = currentBody.getCommonBorderPaddingBackground().getBorderInfo(side); - } - if (otherGridUnit != null - && otherBody != null - && ((otherSide == BEFORE && otherGridUnit.getFlag(GridUnit.FIRST_IN_PART)) - || (otherSide == AFTER && otherGridUnit.getFlag(GridUnit.LAST_IN_PART)))) { - //row group (=body, table-header or table-footer) - other[2] = otherBody.getCommonBorderPaddingBackground().getBorderInfo(otherSide); - } - if ((side == BEFORE && otherGridUnit == null) - || (side == AFTER && otherGridUnit == null) - || (side == START) - || (side == END)) { - //column - current[3] = currentColumn.getCommonBorderPaddingBackground().getBorderInfo(side); - } - if (otherColumn != null) { - //column - other[3] = otherColumn.getCommonBorderPaddingBackground().getBorderInfo(otherSide); - } - //TODO current[4] and other[4] for column groups - if (otherGridUnit == null - && ((side == BEFORE && (flags & VERTICAL_START_END_OF_TABLE) > 0) - || (side == AFTER && (flags & VERTICAL_START_END_OF_TABLE) > 0) - || (side == START) - || (side == END))) { - //table - current[5] = table.getCommonBorderPaddingBackground().getBorderInfo(side); - } - //other[6] is always null, since it's always the same table - - BorderInfo resolved = null; - - // *** Rule 1 *** - resolved = doRule1(current, other); - if (resolved != null) { - return resolved; - } - - // *** Rule 2 *** - if (!doRule2(current, other)) { - } - - // *** Rule 3 *** - resolved = doRule3(current, other); - if (resolved != null) { - return resolved; - } - - // *** Rule 4 *** - resolved = doRule4(current, other); - if (resolved != null) { - return resolved; - } - - // *** Rule 5 *** - resolved = doRule5(current, other); - if (resolved != null) { - return resolved; - } - - return null; //no winner, no border - } - - private BorderInfo doRule1(BorderInfo[] current, BorderInfo[] other) { - for (int i = 0; i < current.length; i++) { - if ((current[i] != null) && (current[i].getStyle() == Constants.EN_HIDDEN)) { - return current[i]; - } - if ((other[i] != null) && (other[i].getStyle() == Constants.EN_HIDDEN)) { - return other[i]; - } - } - return null; - } - - private boolean doRule2(BorderInfo[] current, BorderInfo[] other) { - boolean found = false; - for (int i = 0; i < current.length; i++) { - if ((current[i] != null) && (current[i].getStyle() != Constants.EN_NONE)) { - found = true; - break; + /** {@inheritDoc} */ + public BorderSpecification determineWinner(BorderSpecification border1, + BorderSpecification border2) { + BorderInfo bi1 = border1.getBorderInfo(); + BorderInfo bi2 = border2.getBorderInfo(); + // Rule 1 + if (bi1.getStyle() == Constants.EN_HIDDEN) { + return border1; + } else if (bi2.getStyle() == Constants.EN_HIDDEN) { + return border2; + } + // Rule 2 + if (bi2.getStyle() == Constants.EN_NONE) { + return border1; + } else if (bi1.getStyle() == Constants.EN_NONE) { + return border2; + } + // Rule 3 + int width1 = bi1.getRetainedWidth(); + int width2 = bi2.getRetainedWidth(); + if (width1 > width2) { + return border1; + } else if (width1 == width2) { + int cmp = compareStyles(bi1.getStyle(), bi2.getStyle()); + if (cmp > 0) { + return border1; + } else if (cmp < 0) { + return border2; } - if ((other[i] != null) && (other[i].getStyle() != Constants.EN_NONE)) { - found = true; - break; - } - } - return found; - } - - private BorderInfo doRule3(BorderInfo[] current, BorderInfo[] other) { - int width = 0; - //Find max border width - for (int i = 0; i < current.length; i++) { - if ((current[i] != null) && (current[i].getRetainedWidth() > width)) { - width = current[i].getRetainedWidth(); - } - if ((other[i] != null) && (other[i].getRetainedWidth() > width)) { - width = other[i].getRetainedWidth(); - } - } - BorderInfo widest = null; - int count = 0; - //See if there's only one with the widest border - for (int i = 0; i < current.length; i++) { - if ((current[i] != null) && (current[i].getRetainedWidth() == width)) { - count++; - if (widest == null) { - widest = current[i]; - } - } else { - current[i] = null; //Discard the narrower ones - } - if ((other[i] != null) && (other[i].getRetainedWidth() == width)) { - count++; - if (widest == null) { - widest = other[i]; - } - } else { - other[i] = null; //Discard the narrower ones - } - } - if (count == 1) { - return widest; } else { - return null; + return border2; } - } - - private BorderInfo doRule4(BorderInfo[] current, BorderInfo[] other) { - int pref = getPreferenceValue(Constants.EN_INSET); //Lowest preference - //Find highest preference value - for (int i = 0; i < current.length; i++) { - if (current[i] != null) { - int currPref = getPreferenceValue(current[i].getStyle()); - if (currPref > pref) { - pref = currPref; - } - } - if (other[i] != null) { - int currPref = getPreferenceValue(other[i].getStyle()); - if (currPref > pref) { - pref = currPref; - } - } - } - BorderInfo preferred = null; - int count = 0; - //See if there's only one with the preferred border style - for (int i = 0; i < current.length; i++) { - if (current[i] != null) { - int currPref = getPreferenceValue(current[i].getStyle()); - if (currPref == pref) { - count++; - if (preferred == null) { - preferred = current[i]; - } - break; - } - } else { - current[i] = null; //Discard the ones that are not preferred - } - if (other[i] != null) { - int currPref = getPreferenceValue(other[i].getStyle()); - if (currPref == pref) { - count++; - if (preferred == null) { - preferred = other[i]; - } - break; - } - } else { - other[i] = null; //Discard the ones that are not preferred - } - } - if (count == 1) { - return preferred; - } else { - return null; - } - } - - private BorderInfo doRule5(BorderInfo[] current, BorderInfo[] other) { - for (int i = 0; i < current.length; i++) { - if (current[i] != null) { - return current[i]; - } - if (other[i] != null) { - return other[i]; - } + // Rule 4 + int cmp = compareFOs(border1.getHolder(), border2.getHolder()); + if (cmp > 0) { + return border1; + } else if (cmp < 0) { + return border2; } return null; } - } diff --git a/src/java/org/apache/fop/layoutmgr/table/EffRow.java b/src/java/org/apache/fop/layoutmgr/table/EffRow.java index 1e4924a5a..fe57809e9 100644 --- a/src/java/org/apache/fop/layoutmgr/table/EffRow.java +++ b/src/java/org/apache/fop/layoutmgr/table/EffRow.java @@ -47,12 +47,14 @@ class EffRow { * Creates a new effective row instance. * @param index index of the row * @param bodyType type of body (one of HEADER, FOOTER, BODY as found on TableRowIterator) + * @param gridUnits the grid units this row is made of */ - public EffRow(int index, int bodyType) { + public EffRow(int index, int bodyType, List gridUnits) { this.index = index; this.bodyType = bodyType; + this.gridUnits = gridUnits; } - + /** @return the index of the EffRow in the sequence of rows */ public int getIndex() { return this.index; diff --git a/src/java/org/apache/fop/layoutmgr/table/EmptyGridUnit.java b/src/java/org/apache/fop/layoutmgr/table/EmptyGridUnit.java index 5c208b84b..50500c498 100644 --- a/src/java/org/apache/fop/layoutmgr/table/EmptyGridUnit.java +++ b/src/java/org/apache/fop/layoutmgr/table/EmptyGridUnit.java @@ -19,9 +19,12 @@ package org.apache.fop.layoutmgr.table; +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.flow.table.BorderSpecification; +import org.apache.fop.fo.flow.table.Table; import org.apache.fop.fo.flow.table.TableBody; -import org.apache.fop.fo.flow.table.TableColumn; import org.apache.fop.fo.flow.table.TableRow; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; /** * GridUnit subclass for empty grid units. @@ -30,25 +33,34 @@ public class EmptyGridUnit extends GridUnit { private TableRow row; private TableBody body; - + /** - * @param row Optional table-row instance - * @param column table-column instance - * @param body table-body the grid unit belongs to - * @param startCol column index + * @param table the containing table + * @param startRow index of the row this grid unit belongs to, 0-based + * @param startCol column index, 0-based */ - public EmptyGridUnit(TableRow row, TableColumn column, TableBody body, - int startCol) { - super(null, null, column, startCol, 0); - this.row = row; - this.body = body; + public EmptyGridUnit(Table table, int startRow, int startCol) { + super(table, table.getColumn(startCol), startCol, 0, 0); } - + + /** {@inheritDoc} */ + protected void setBorder(int side) { + resolvedBorders[side] = new BorderSpecification( + new CommonBorderPaddingBackground.BorderInfo(Constants.EN_NONE, null, null), + Constants.FO_TABLE_CELL); + } + + /** {@inheritDoc} */ + public PrimaryGridUnit getPrimary() { + throw new UnsupportedOperationException(); +// return this; TODO + } + /** {@inheritDoc} */ public boolean isPrimary() { return true; } - + /** {@inheritDoc} */ public TableBody getBody() { return this.body; @@ -58,4 +70,14 @@ public class EmptyGridUnit extends GridUnit { public TableRow getRow() { return this.row; } + + /** {@inheritDoc} */ + public boolean isLastGridUnitColSpan() { + return true; + } + + /** {@inheritDoc} */ + public boolean isLastGridUnitRowSpan() { + return true; + } } diff --git a/src/java/org/apache/fop/layoutmgr/table/GridUnit.java b/src/java/org/apache/fop/layoutmgr/table/GridUnit.java index 79b2cca9e..7f713351f 100644 --- a/src/java/org/apache/fop/layoutmgr/table/GridUnit.java +++ b/src/java/org/apache/fop/layoutmgr/table/GridUnit.java @@ -22,10 +22,12 @@ package org.apache.fop.layoutmgr.table; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.fop.fo.FONode; +import org.apache.fop.fo.flow.table.BorderSpecification; 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.TableFObj; import org.apache.fop.fo.flow.table.TableRow; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; @@ -39,81 +41,142 @@ public class GridUnit { /** Indicates that the grid unit is in the first column. */ public static final int IN_FIRST_COLUMN = 0; + /** Indicates that the grid unit is in the last column. */ public static final int IN_LAST_COLUMN = 1; + /** Indicates that the grid unit is in the first row of the table. */ public static final int FIRST_IN_TABLE = 2; - /** Indicates that the grid unit is in the first row of the table part (header, footer, body). */ + + /** + * Indicates that the grid unit is in the first row of the table part (header, footer, + * body). + */ public static final int FIRST_IN_PART = 3; - /** Indicates that the grid unit is in the last row of the table part (header, footer, body). */ + + /** + * Indicates that the grid unit is in the last row of the table part (header, footer, + * body). + */ public static final int LAST_IN_PART = 4; + /** Indicates that the grid unit is in the last row of the table. */ public static final int LAST_IN_TABLE = 5; + /** Indicates that the primary grid unit has a pending keep-with-next. */ public static final int KEEP_WITH_NEXT_PENDING = 6; + /** Indicates that the primary grid unit has a pending keep-with-previous. */ public static final int KEEP_WITH_PREVIOUS_PENDING = 7; /** Primary grid unit */ private PrimaryGridUnit primary; + /** Table cell which occupies this grid unit */ - private TableCell cell; + protected TableCell cell; + /** Table row which occupies this grid unit (may be null) */ private TableRow row; + /** Table column that this grid unit belongs to */ private TableColumn column; /** start index of grid unit within row in column direction */ private int startCol; + /** index of grid unit within cell in column direction */ private int colSpanIndex; + /** index of grid unit within cell in row direction */ private int rowSpanIndex; + /** effective borders for a cell slot */ private CommonBorderPaddingBackground effectiveBorders; + /** flags for the grid unit */ private byte flags = 0; + protected BorderSpecification[] resolvedBorders; + + private CollapsingBorderModel collapsingBorderModel; /** * Creates a new grid unit. - * - * @param cell table cell which occupies this grid unit + * + * @param table the containing table * @param column table column this grid unit belongs to * @param startCol index of the column this grid unit belongs to * @param colSpanIndex index of this grid unit in the span, in column direction + * @param rowSpanIndex index of this grid unit in the span, in row direction */ - public GridUnit(TableCell cell, TableColumn column, int startCol, int colSpanIndex) { - this(null, cell, column, startCol, colSpanIndex); + protected GridUnit(Table table, TableColumn column, int startCol, int colSpanIndex, + int rowSpanIndex) { + this(column, startCol, colSpanIndex, rowSpanIndex); + setBorders(table); } /** * Creates a new grid unit. - * - * @param primary the before-start grid unit of the cell containing this grid unit + * + * @param cell table cell which occupies this grid unit * @param column table column this grid unit belongs to * @param startCol index of the column this grid unit belongs to * @param colSpanIndex index of this grid unit in the span, in column direction + * @param rowSpanIndex index of this grid unit in the span, in row direction */ - public GridUnit(PrimaryGridUnit primary, TableColumn column, int startCol, int colSpanIndex) { - this(primary, primary.getCell(), column, startCol, colSpanIndex); + protected GridUnit(TableCell cell, TableColumn column, int startCol, int colSpanIndex, + int rowSpanIndex) { + this(column, startCol, colSpanIndex, rowSpanIndex); + this.cell = cell; + setBorders(cell.getTable()); } /** * Creates a new grid unit. - * + * * @param primary the before-start grid unit of the cell containing this grid unit - * @param cell table cell which occupies this grid unit * @param column table column this grid unit belongs to * @param startCol index of the column this grid unit belongs to * @param colSpanIndex index of this grid unit in the span, in column direction + * @param rowSpanIndex index of this grid unit in the span, in row direction */ - protected GridUnit(PrimaryGridUnit primary, TableCell cell, TableColumn column, int startCol, int colSpanIndex) { + public GridUnit(PrimaryGridUnit primary, TableColumn column, int startCol, int colSpanIndex, + int rowSpanIndex) { + this(primary.getCell(), column, startCol, colSpanIndex, rowSpanIndex); this.primary = primary; - this.cell = cell; + } + + private GridUnit(TableColumn column, int startCol, int colSpanIndex, int rowSpanIndex) { this.column = column; this.startCol = startCol; this.colSpanIndex = colSpanIndex; + this.rowSpanIndex = rowSpanIndex; + } + + private void setBorders(Table table/*TODO*/) { + if (table.isSeparateBorderModel()) { + assignBorderForSeparateBorderModel(); + } else { + resolvedBorders = new BorderSpecification[4]; + collapsingBorderModel = CollapsingBorderModel.getBorderModelFor(table + .getBorderCollapse()); + if (rowSpanIndex == 0) { + setBorder(CommonBorderPaddingBackground.BEFORE); + } + if (isLastGridUnitRowSpan()) { + setBorder(CommonBorderPaddingBackground.AFTER); + } + if (colSpanIndex == 0) { + setBorder(CommonBorderPaddingBackground.START); + } + if (isLastGridUnitColSpan()) { + setBorder(CommonBorderPaddingBackground.END); + } + } + } + + protected void setBorder(int side) { + resolvedBorders[side] = cell.resolvedBorders[side]; } public TableCell getCell() { @@ -128,7 +191,7 @@ public class GridUnit { if (row != null) { return row; } else if (getCell().getParent() instanceof TableRow) { - return (TableRow)getCell().getParent(); + return (TableRow) getCell().getParent(); } else { return null; } @@ -136,6 +199,7 @@ public class GridUnit { /** * Sets the table-row FO, if applicable. + * * @param row the table-row FO */ public void setRow(TableRow row) { @@ -147,18 +211,7 @@ public class GridUnit { while (node != null && !(node instanceof TableBody)) { node = node.getParent(); } - return (TableBody)node; - } - - public Table getTable() { - FONode node = getBody(); - while (node != null && !(node instanceof Table)) { - node = node.getParent(); - } - if (node == null && getColumn() != null) { - node = getColumn().getParent(); - } - return (Table)node; + return (TableBody) node; } /** @@ -167,7 +220,7 @@ public class GridUnit { * @return the before-start grid unit of the cell containing this grid unit. */ public PrimaryGridUnit getPrimary() { - return (isPrimary() ? (PrimaryGridUnit)this : primary); + return primary; } /** @@ -194,20 +247,12 @@ public class GridUnit { /** @return true if the grid unit is the last in column spanning direction */ public boolean isLastGridUnitColSpan() { - if (cell != null) { - return (colSpanIndex == cell.getNumberColumnsSpanned() - 1); - } else { - return true; - } + return (colSpanIndex == cell.getNumberColumnsSpanned() - 1); } /** @return true if the grid unit is the last in row spanning direction */ public boolean isLastGridUnitRowSpan() { - if (cell != null) { - return (rowSpanIndex == cell.getNumberRowsSpanned() - 1); - } else { - return true; - } + return (rowSpanIndex == cell.getNumberRowsSpanned() - 1); } /** @@ -225,9 +270,10 @@ public class GridUnit { } /** - * Returns a BorderInfo instance for a side of the currently applicable cell before border - * resolution (i.e. the value from the FO). A return value of null indicates an empty cell. - * See CollapsingBorderModel(EyeCatching) where this method is used. + * Returns a BorderInfo instance for a side of the currently applicable cell before + * border resolution (i.e. the value from the FO). A return value of null indicates an + * empty cell. See CollapsingBorderModel(EyeCatching) where this method is used. + * * @param side for which side to return the BorderInfo * @return the requested BorderInfo instance or null if the grid unit is an empty cell */ @@ -243,9 +289,33 @@ public class GridUnit { * @return the resolved normal borders for this grid unit */ public CommonBorderPaddingBackground getBorders() { + // TODO + if (effectiveBorders == null) { + effectiveBorders = new CommonBorderPaddingBackground(); + setBorderInfo(CommonBorderPaddingBackground.BEFORE); + setBorderInfo(CommonBorderPaddingBackground.AFTER); + setBorderInfo(CommonBorderPaddingBackground.START); + setBorderInfo(CommonBorderPaddingBackground.END); + if (cell != null) { + effectiveBorders.setPadding(cell.getCommonBorderPaddingBackground()); + } + if (log.isDebugEnabled()) { + log.debug(this + " resolved borders: " + "before=" + + effectiveBorders.getBorderBeforeWidth(false) + ", " + "after=" + + effectiveBorders.getBorderAfterWidth(false) + ", " + "start=" + + effectiveBorders.getBorderStartWidth(false) + ", " + "end=" + + effectiveBorders.getBorderEndWidth(false)); + } + } return effectiveBorders; } + private void setBorderInfo(int side) { + if (resolvedBorders[side] != null) { + effectiveBorders.setBorderInfo(resolvedBorders[side].getBorderInfo(), side); + } + } + /** * @return true if the grid unit has any borders. */ @@ -254,8 +324,8 @@ public class GridUnit { } /** - * Assigns the borders from the given cell to this cell info. Used in - * case of separate border model. + * Assigns the borders from the given cell to this cell info. Used in case of separate + * border model. */ public void assignBorderForSeparateBorderModel() { if (cell != null) { @@ -264,42 +334,39 @@ public class GridUnit { } /** - * Resolve collapsing borders for the given cell. Used in case of the collapsing border model. - * @param other neighbouring grid unit if any - * @param side the side to resolve (one of CommonBorderPaddingBackground.BEFORE|AFTER|START|END) + * Resolve collapsing borders for the given cell. Used in case of the collapsing + * border model. + * + * @param other neighbouring grid unit + * @param side the side to resolve (one of + * CommonBorderPaddingBackground.BEFORE|AFTER|START|END) */ public void resolveBorder(GridUnit other, int side) { - resolveBorder(other, side, 0); + BorderSpecification resolvedBorder = collapsingBorderModel.determineWinner( + resolvedBorders[side], other.resolvedBorders[CollapsingBorderModel + .getOtherSide(side)]); + if (resolvedBorder != null) { + this.resolvedBorders[side] = resolvedBorder; + other.resolvedBorders[CollapsingBorderModel.getOtherSide(side)] = resolvedBorder; + } } /** - * Resolve collapsing borders for the given cell. Used in case of the collapsing border model. - * @param other neighbouring grid unit if any - * @param side the side to resolve (one of CommonBorderPaddingBackground.BEFORE|AFTER|START|END) - * @param resFlags flags for the border resolution + * Resolves the border on the given side of this grid unit, comparing it against the + * same border of the given parent element. + * + * @param side the side to resolve (one of + * CommonBorderPaddingBackground.BEFORE|AFTER|START|END) + * @param parent the parent element holding a competing border */ - public void resolveBorder(GridUnit other, int side, int resFlags) { - CollapsingBorderModel borderModel = CollapsingBorderModel.getBorderModelFor( - getTable().getBorderCollapse()); - if (effectiveBorders == null) { - effectiveBorders = new CommonBorderPaddingBackground(); - } - effectiveBorders.setBorderInfo(borderModel.determineWinner(this, other, - side, resFlags), side); - if (cell != null) { - effectiveBorders.setPadding(cell.getCommonBorderPaddingBackground()); - } - if (log.isDebugEnabled()) { - log.debug(this + " resolved borders: " - + "before=" + effectiveBorders.getBorderBeforeWidth(false) + ", " - + "after=" + effectiveBorders.getBorderAfterWidth(false) + ", " - + "start=" + effectiveBorders.getBorderStartWidth(false) + ", " - + "end=" + effectiveBorders.getBorderEndWidth(false)); - } + public void resolveBorder(int side, TableFObj parent) { + resolvedBorders[side] = collapsingBorderModel.determineWinner(resolvedBorders[side], + parent.resolvedBorders[side]); } /** * Returns a flag for this GridUnit. + * * @param which the requested flag * @return the value of the flag */ @@ -309,29 +376,25 @@ public class GridUnit { /** * Sets a flag on a GridUnit. + * * @param which the flag to set * @param value the new value for the flag */ public void setFlag(int which, boolean value) { if (value) { - flags |= (1 << which); //set flag + flags |= (1 << which); // set flag } else { - flags &= ~(1 << which); //clear flag + flags &= ~(1 << which); // clear flag } } /** - * @return the grid unit just below this grid unit if the cell is spanning. + * Sets the given flag on this grid unit. + * + * @param which the flag to set */ - public GridUnit createNextRowSpanningGridUnit() { - if (isLastGridUnitRowSpan()) { - return null; - } else { - //cloning the current GridUnit with adjustments - GridUnit gu = new GridUnit(getPrimary(), getColumn(), startCol, colSpanIndex); - gu.rowSpanIndex = rowSpanIndex + 1; - return gu; - } + public void setFlag(int which) { + setFlag(which, true); } /** {@inheritDoc} */ diff --git a/src/java/org/apache/fop/layoutmgr/table/PrimaryGridUnit.java b/src/java/org/apache/fop/layoutmgr/table/PrimaryGridUnit.java index ece022556..34daf7d96 100644 --- a/src/java/org/apache/fop/layoutmgr/table/PrimaryGridUnit.java +++ b/src/java/org/apache/fop/layoutmgr/table/PrimaryGridUnit.java @@ -55,18 +55,22 @@ public class PrimaryGridUnit extends GridUnit { * @param startRow index of the row this grid unit belongs to, zero-based */ public PrimaryGridUnit(TableCell cell, TableColumn column, int startCol, int startRow) { - super(cell, column, startCol, 0); + super(cell, column, startCol, 0, 0); this.startRow = startRow; log.trace("PrimaryGridUnit created, row " + startRow + " col " + startCol); - if (cell != null) { - cellLM = new TableCellLayoutManager(cell, this); - } } public TableCellLayoutManager getCellLM() { + assert cellLM != null; return cellLM; } + /** {@inheritDoc} */ + public PrimaryGridUnit getPrimary() { + return this; + } + + /** {@inheritDoc} */ public boolean isPrimary() { return true; } @@ -225,4 +229,12 @@ public class PrimaryGridUnit extends GridUnit { || (getCell().getNumberRowsSpanned() > 1); } + /** + * Creates a cellLM for the corresponding table-cell. A new one must be created + * for each new static-content (TODO). + */ + public void createCellLM() { + cellLM = new TableCellLayoutManager(cell, this); + } + } diff --git a/src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java index 4dd42110f..c7776ff7b 100644 --- a/src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/RowGroupLayoutManager.java @@ -97,10 +97,6 @@ class RowGroupLayoutManager { public LinkedList getNextKnuthElements(LayoutContext context, int alignment, int bodyType) { LinkedList returnList = new LinkedList(); - //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. @@ -134,107 +130,6 @@ class RowGroupLayoutManager { } /** - * 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 @@ -271,6 +166,7 @@ class RowGroupLayoutManager { PrimaryGridUnit primary = gu.getPrimary(); if (gu.isPrimary()) { + primary.createCellLM(); // TODO a new LM must be created for every new static-content primary.getCellLM().setParent(tableLM); //Determine the table-row if any diff --git a/src/java/org/apache/fop/layoutmgr/table/TableRowIterator.java b/src/java/org/apache/fop/layoutmgr/table/TableRowIterator.java index dfac329a8..3acde70d3 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableRowIterator.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableRowIterator.java @@ -20,11 +20,14 @@ package org.apache.fop.layoutmgr.table; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.fop.fo.FONode; +import org.apache.fop.fo.FONode.FONodeIterator; import org.apache.fop.fo.flow.Marker; import org.apache.fop.fo.flow.table.Table; import org.apache.fop.fo.flow.table.TableBody; @@ -98,6 +101,8 @@ public class TableRowIterator { /** Iterator over a part's child elements (either table-rows or table-cells). */ private ListIterator tablePartChildIterator = null; + private Iterator rowGroupsIter; + /** * Creates a new TableRowIterator. * @param table the table to iterate over @@ -110,19 +115,23 @@ public class TableRowIterator { this.tablePart = tablePart; switch(tablePart) { case HEADER: { - List bodyList = new java.util.ArrayList(); - bodyList.add(table.getTableHeader()); - this.tablePartIterator = bodyList.listIterator(); + rowGroupsIter = table.getTableHeader().getRowGroups().iterator(); break; } case FOOTER: { - List bodyList = new java.util.ArrayList(); - bodyList.add(table.getTableFooter()); - this.tablePartIterator = bodyList.listIterator(); + rowGroupsIter = table.getTableFooter().getRowGroups().iterator(); break; } default: { - this.tablePartIterator = table.getChildNodes(); + List rowGroupsList = new LinkedList(); + // TODO this is ugly + for (FONodeIterator iter = table.getChildNodes(); iter.hasNext();) { + FONode node = iter.nextNode(); + if (node instanceof TableBody) { + rowGroupsList.addAll(((TableBody) node).getRowGroups()); + } + } + rowGroupsIter = rowGroupsList.iterator(); } } } @@ -132,38 +141,19 @@ public class TableRowIterator { * consecutive rows which contains all spanned grid units of its cells. * @return the next row group, or null */ - public EffRow[] getNextRowGroup() { - EffRow firstRowInGroup = getNextRow(); - if (firstRowInGroup == null) { + EffRow[] getNextRowGroup() { + if (!rowGroupsIter.hasNext()) { return null; } - EffRow lastRowInGroup = firstRowInGroup; - int lastIndex = lastRowInGroup.getIndex(); - boolean allFinished; - do { - allFinished = true; - Iterator iter = lastRowInGroup.getGridUnits().iterator(); - while (iter.hasNext()) { - GridUnit gu = (GridUnit)iter.next(); - if (!gu.isLastGridUnitRowSpan()) { - allFinished = false; - break; - } - } - lastIndex = lastRowInGroup.getIndex(); - if (!allFinished) { - lastRowInGroup = getNextRow(); - if (lastRowInGroup == null) { - allFinished = true; - } - } - } while (!allFinished); - int rowCount = lastIndex - firstRowInGroup.getIndex() + 1; - EffRow[] rowGroup = new EffRow[rowCount]; - for (int i = 0; i < rowCount; i++) { - rowGroup[i] = getCachedRow(i + firstRowInGroup.getIndex()); + List rowGroup = (List) rowGroupsIter.next(); + EffRow[] effRowGroup = new EffRow[rowGroup.size()]; + int i = 0; + for (Iterator rowIter = rowGroup.iterator(); rowIter.hasNext();) { + List gridUnits = (List) rowIter.next(); + effRowGroup[i] = new EffRow(i, tablePart, gridUnits); + i++; } - return rowGroup; + return effRowGroup; } /** @@ -197,7 +187,7 @@ public class TableRowIterator { * @return the preceding row, or null if there is no such row (the given row is the * first one in the table part) */ - public EffRow getPrecedingRow(EffRow row) { + EffRow getPrecedingRow(EffRow row) { return getRow(row.getIndex() - 1); } @@ -207,7 +197,7 @@ public class TableRowIterator { * @param row a row in the iterated table part * @return the following row, or null if there is no more row */ - public EffRow getFollowingRow(EffRow row) { + EffRow getFollowingRow(EffRow row) { return getRow(row.getIndex() + 1); } @@ -215,7 +205,7 @@ public class TableRowIterator { * Returns the first effective row. * @return the requested effective row. */ - public EffRow getFirstRow() { + EffRow getFirstRow() { if (fetchedRows.size() == 0) { prefetchNext(); } @@ -228,7 +218,7 @@ public class TableRowIterator { * if preloaded.</p> * @return the requested effective row. */ - public EffRow getLastRow() { + EffRow getLastRow() { while (prefetchNext()) { //nop } @@ -377,7 +367,7 @@ public class TableRowIterator { * @return the list of grid units */ private EffRow buildGridRow(List cells, TableRow rowFO) { - EffRow row = new EffRow(this.fetchIndex, tablePart); + EffRow row = new EffRow(this.fetchIndex, tablePart, null); List gridUnits = row.getGridUnits(); TableBody bodyFO = null; @@ -393,7 +383,8 @@ public class TableRowIterator { if (gu.getColSpanIndex() == 0) { horzSpan = new GridUnit[gu.getCell().getNumberColumnsSpanned()]; } - GridUnit newGU = gu.createNextRowSpanningGridUnit(); +// GridUnit newGU = gu.createNextRowSpanningGridUnit(); + GridUnit newGU = null; newGU.setRow(rowFO); safelySetListItem(gridUnits, colnum - 1, newGU); horzSpan[newGU.getColSpanIndex()] = newGU; @@ -455,7 +446,7 @@ public class TableRowIterator { horzSpan[0] = gu; for (int j = 1; j < cell.getNumberColumnsSpanned(); j++) { colnum++; - GridUnit guSpan = new GridUnit(gu, columns.getColumn(colnum), colnum - 1, j); + GridUnit guSpan = new GridUnit(gu, columns.getColumn(colnum), colnum - 1, j, 0); //TODO: remove the check below??? other = (GridUnit)safelyGetListItem(gridUnits, colnum - 1); if (other != null) { @@ -486,29 +477,29 @@ public class TableRowIterator { } //Post-processing the list (looking for gaps and resolve start and end borders) - fillEmptyGridUnits(gridUnits, rowFO, bodyFO); +// fillEmptyGridUnits(gridUnits, rowFO, bodyFO); resolveStartEndBorders(gridUnits); return row; } - private void fillEmptyGridUnits(List gridUnits, TableRow row, TableBody body) { - for (int pos = 1; pos <= gridUnits.size(); pos++) { - GridUnit gu = (GridUnit)gridUnits.get(pos - 1); - - //Empty grid units - if (gu == null) { - //Add grid unit - gu = new EmptyGridUnit(row, columns.getColumn(pos), body, - pos - 1); - gridUnits.set(pos - 1, gu); - } - - //Set flags - gu.setFlag(GridUnit.IN_FIRST_COLUMN, (pos == 1)); - gu.setFlag(GridUnit.IN_LAST_COLUMN, (pos == gridUnits.size())); - } - } +// private void fillEmptyGridUnits(List gridUnits, TableRow row, TableBody body) { +// for (int pos = 1; pos <= gridUnits.size(); pos++) { +// GridUnit gu = (GridUnit)gridUnits.get(pos - 1); +// +// //Empty grid units +// if (gu == null) { +// //Add grid unit +// gu = new EmptyGridUnit(row, columns.getColumn(pos), body, +// pos - 1); +// gridUnits.set(pos - 1, gu); +// } +// +// //Set flags +// gu.setFlag(GridUnit.IN_FIRST_COLUMN, (pos == 1)); +// gu.setFlag(GridUnit.IN_LAST_COLUMN, (pos == gridUnits.size())); +// } +// } private void resolveStartEndBorders(List gridUnits) { for (int pos = 1; pos <= gridUnits.size(); pos++) { |