From: Jeremias Maerki Date: Mon, 21 Feb 2005 21:52:14 +0000 (+0000) Subject: First real step towards collapsing table borders. X-Git-Tag: Root_Temp_KnuthStylePageBreaking~57 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=1cf6596be02b197c0d2073bf504f62fb99585799;p=xmlgraphics-fop.git First real step towards collapsing table borders. border-collapse="collapse" rules implemented but not fully tested, yet. Collapsing of borders works only for start and end borders, yet, and there only for non-spanned cells. CellLM is now prepared to get the full list of grid unit it occupies (necessary for collapsed border painting). Row.CellInfo extracted and renamed to GridUnit. WIP... git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@198450 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/fop/fo/flow/TableBody.java b/src/java/org/apache/fop/fo/flow/TableBody.java index 5f30c618a..28c182424 100644 --- a/src/java/org/apache/fop/fo/flow/TableBody.java +++ b/src/java/org/apache/fop/fo/flow/TableBody.java @@ -152,5 +152,23 @@ public class TableBody extends FObj { public int getNameId() { return FO_TABLE_BODY; } + + /** + * @param obj table row in question + * @return true if the given table row is the first row of this body. + */ + public boolean isFirst(TableRow obj) { + return (childNodes.size() > 0) + && (childNodes.get(0) == obj); + } + + /** + * @param obj table row in question + * @return true if the given table row is the first row of this body. + */ + public boolean isLast(TableRow obj) { + return (childNodes.size() > 0) + && (childNodes.get(childNodes.size() - 1) == obj); + } } diff --git a/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java b/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java index b9848ab0a..fcaa0e217 100755 --- a/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java +++ b/src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java @@ -75,7 +75,7 @@ public class CommonBorderPaddingBackground implements Cloneable { /** the "end" edge */ public static final int END = 3; - private static class BorderInfo implements Cloneable { + public static class BorderInfo implements Cloneable { private int mStyle; // Enum for border style private ColorType mColor; // Border color private CondLengthProperty mWidth; @@ -85,11 +85,52 @@ public class CommonBorderPaddingBackground implements Cloneable { mWidth = width; mColor = color; } + + public int getStyle() { + return this.mStyle; + } + + public ColorType getColor() { + return this.mColor; + } + + public CondLengthProperty getWidth() { + return this.mWidth; + } + + public int getRetainedWidth() { + if ((mStyle == Constants.EN_NONE) + || (mStyle == Constants.EN_HIDDEN)) { + return 0; + } else { + return mWidth.getLengthValue(); + } + } + + /** @see java.lang.Object#toString() */ + public String toString() { + StringBuffer sb = new StringBuffer("BorderInfo"); + sb.append(" {"); + sb.append(mStyle); + sb.append(", "); + sb.append(mColor); + sb.append(", "); + sb.append(mWidth); + sb.append("}"); + return sb.toString(); + } } private BorderInfo[] borderInfo = new BorderInfo[4]; private CondLengthProperty[] padding = new CondLengthProperty[4]; + /** + * Construct a CommonBorderPaddingBackground object. + */ + public CommonBorderPaddingBackground() { + + } + /** * Construct a CommonBorderPaddingBackground object. * @param pList The PropertyList to get properties from. @@ -159,12 +200,29 @@ public class CommonBorderPaddingBackground implements Cloneable { // If style = none, force width to 0, don't get Color (spec 7.7.20) int style = pList.get(styleProp).getEnum(); if (style != Constants.EN_NONE) { - borderInfo[side] = new BorderInfo(style, + setBorderInfo(new BorderInfo(style, pList.get(widthProp).getCondLength(), - pList.get(colorProp).getColorType()); + pList.get(colorProp).getColorType()), side); } } + /** + * Sets a border. + * @param info the border information + * @param side the side to apply the info to + */ + public void setBorderInfo(BorderInfo info, int side) { + this.borderInfo[side] = info; + } + + /** + * @param side the side to retrieve + * @return the border info for a side + */ + public BorderInfo getBorderInfo(int side) { + return this.borderInfo[side]; + } + /** * @return the background image as a preloaded FopImage, null if there is * no background image. diff --git a/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java b/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java index 534af3e9d..85bc59c96 100644 --- a/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java +++ b/src/java/org/apache/fop/layoutmgr/LayoutManagerMapping.java @@ -313,7 +313,7 @@ public class LayoutManagerMapping implements LayoutManagerMaker { public static class TableLayoutManagerMaker extends Maker { - private List getColumnLayoutManagerList(Table table) { + private List getColumnLayoutManagerList(Table table, TableLayoutManager tlm) { List columnLMs = null; List columns = table.getColumns(); if (columns != null) { @@ -329,7 +329,9 @@ public class LayoutManagerMapping implements LayoutManagerMaker { while (colnum > columnLMs.size()) { columnLMs.add(null); } - columnLMs.set(colnum - 1, new Column(col)); + Column colLM = new Column(col); + colLM.setParent(tlm); + columnLMs.set(colnum - 1, colLM); colnum++; } } @@ -350,7 +352,7 @@ public class LayoutManagerMapping implements LayoutManagerMaker { public void make(FONode node, List lms) { Table table = (Table) node; TableLayoutManager tlm = new TableLayoutManager(table); - List columnLMs = getColumnLayoutManagerList(table); + List columnLMs = getColumnLayoutManagerList(table, tlm); if (columnLMs != null) { tlm.setColumns(columnLMs); } diff --git a/src/java/org/apache/fop/layoutmgr/table/Body.java b/src/java/org/apache/fop/layoutmgr/table/Body.java index 9daef193d..65ba5d966 100644 --- a/src/java/org/apache/fop/layoutmgr/table/Body.java +++ b/src/java/org/apache/fop/layoutmgr/table/Body.java @@ -61,6 +61,11 @@ public class Body extends BlockStackingLayoutManager { fobj = node; } + /** @return the table-body|header|footer FO */ + public TableBody getFObj() { + return this.fobj; + } + /** * Set the columns from the table. * diff --git a/src/java/org/apache/fop/layoutmgr/table/Cell.java b/src/java/org/apache/fop/layoutmgr/table/Cell.java index 648528bf7..f664f8b60 100644 --- a/src/java/org/apache/fop/layoutmgr/table/Cell.java +++ b/src/java/org/apache/fop/layoutmgr/table/Cell.java @@ -21,6 +21,7 @@ package org.apache.fop.layoutmgr.table; import org.apache.fop.fo.FONode; import org.apache.fop.fo.flow.Table; import org.apache.fop.fo.flow.TableCell; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.LengthRangeProperty; import org.apache.fop.layoutmgr.BlockStackingLayoutManager; import org.apache.fop.layoutmgr.LayoutManager; @@ -59,9 +60,14 @@ public class Cell extends BlockStackingLayoutManager { private int cellIPD; private int rowHeight; private int usedBPD; + private int startBorderWidth; + private int endBorderWidth; private int borderAndPaddingBPD; private boolean emptyCell = true; + /** List of Lists containing GridUnit instances, one List per row. */ + private List rows = new java.util.ArrayList(); + /** * Create a new Cell layout manager. * @node table-cell FO for which to create the LM @@ -102,10 +108,34 @@ public class Cell extends BlockStackingLayoutManager { return (Table)node; } + + /** + * Called by Row LM to register the grid units occupied by this cell for a row. + * @param spannedGridUnits a List of GridUnits + */ + public void addGridUnitsFromRow(List spannedGridUnits) { + log.debug("Getting another row, " + spannedGridUnits.size() + " grid units"); + this.rows.add(spannedGridUnits); + + } + private int getIPIndents() { int iIndents = 0; - iIndents += fobj.getCommonBorderPaddingBackground().getBorderStartWidth(false); - iIndents += fobj.getCommonBorderPaddingBackground().getBorderEndWidth(false); + startBorderWidth = 0; + endBorderWidth = 0; + for (int i = 0; i < rows.size(); i++) { + List gridUnits = (List)rows.get(i); + startBorderWidth = Math.max(startBorderWidth, + ((GridUnit)gridUnits.get(0)). + effBorders.getBorderStartWidth(false)); + endBorderWidth = Math.max(endBorderWidth, + ((GridUnit)gridUnits.get(gridUnits.size() - 1)). + effBorders.getBorderEndWidth(false)); + } + //iIndents += fobj.getCommonBorderPaddingBackground().getBorderStartWidth(false); + iIndents += startBorderWidth; + //iIndents += fobj.getCommonBorderPaddingBackground().getBorderEndWidth(false); + iIndents += endBorderWidth; if (!fobj.isSeparateBorderModel()) { iIndents /= 2; } @@ -277,8 +307,15 @@ public class Cell extends BlockStackingLayoutManager { TraitSetter.addBackground(curBlockArea, fobj.getCommonBorderPaddingBackground()); //TODO Set these booleans right boolean[] outer = new boolean[] {false, false, false, false}; - TraitSetter.addCollapsingBorders(curBlockArea, - fobj.getCommonBorderPaddingBackground(), outer); + if (rows.size() == 1 && ((List)rows.get(0)).size() == 1) { + //Can set the borders directly if there's no span + CommonBorderPaddingBackground effBorders = + ((GridUnit)((List)rows.get(0)).get(0)).effBorders; + TraitSetter.addCollapsingBorders(curBlockArea, + effBorders, outer); + } else { + log.warn("TODO Add collapsed border painting for spanned cells"); + } } //Handle display-align @@ -339,7 +376,7 @@ public class Cell extends BlockStackingLayoutManager { curBlockArea.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE); curBlockArea.setPositioning(Block.ABSOLUTE); int indent = 0; - indent += fobj.getCommonBorderPaddingBackground().getBorderStartWidth(false); + indent += startBorderWidth; if (!fobj.isSeparateBorderModel()) { indent /= 2; } @@ -390,5 +427,6 @@ public class Cell extends BlockStackingLayoutManager { childBreaks.clear(); } } + } diff --git a/src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModel.java b/src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModel.java new file mode 100644 index 000000000..18999f055 --- /dev/null +++ b/src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModel.java @@ -0,0 +1,121 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.layoutmgr.table; + +import org.apache.fop.fo.Constants; +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 + * in the XSL 1.0 specification. + */ +public abstract class CollapsingBorderModel { + + /** 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 */ + public static final int LAST_ROW_IN_TABLE_PART = 2; + /** Indicates that the cell is/starts in the first row of a body/table-header/table-footer */ + public static final int FIRST_ROW_IN_GROUP = 4; + /** Indicates that the cell is/end in the last row of a body/table-header/table-footer */ + public static final int LAST_ROW_IN_GROUP = 8; + + private static CollapsingBorderModel collapse = null; + private static CollapsingBorderModel collapseWithPrecedence = null; + + /** + * @param cellLM the cell + * @return the border model for the cell + */ + public static CollapsingBorderModel getBorderModelFor(int borderCollapse) { + switch (borderCollapse) { + case Constants.EN_COLLAPSE: + if (collapse == null) { + collapse = new CollapsingBorderModelEyeCatching(); + } + return collapse; + case Constants.EN_COLLAPSE_WITH_PRECEDENCE: + if (collapseWithPrecedence == null) { + //collapseWithPrecedence = new CollapsingBorderModelWithPrecedence(); + } + return collapseWithPrecedence; + default: + 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) { + switch (side) { + case CommonBorderPaddingBackground.BEFORE: + return CommonBorderPaddingBackground.AFTER; + case CommonBorderPaddingBackground.AFTER: + return CommonBorderPaddingBackground.BEFORE; + case CommonBorderPaddingBackground.START: + return CommonBorderPaddingBackground.END; + case CommonBorderPaddingBackground.END: + return CommonBorderPaddingBackground.START; + default: + 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 + || side == CommonBorderPaddingBackground.AFTER); + } + + + /** + * 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) { + switch (style) { + case Constants.EN_DOUBLE: return 0; + case Constants.EN_SOLID: return -1; + case Constants.EN_DASHED: return -2; + case Constants.EN_DOTTED: return -3; + case Constants.EN_RIDGE: return -4; + case Constants.EN_OUTSET: return -5; + case Constants.EN_GROOVE: return -6; + case Constants.EN_INSET: return -6; + default: throw new IllegalStateException("Illegal border style: " + style); + } + } + + /** + * Determines the winning BorderInfo. + * @param current cell info of the current element + * @param neighbour cell info of the neighbouring element + * @return the winning BorderInfo + */ + public abstract BorderInfo determineWinner( + GridUnit current, GridUnit neighbour, int side, int flags); + +} diff --git a/src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModelEyeCatching.java b/src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModelEyeCatching.java new file mode 100644 index 000000000..e3e61033a --- /dev/null +++ b/src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModelEyeCatching.java @@ -0,0 +1,298 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.layoutmgr.table; + +import org.apache.fop.fo.Constants; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; + +/** + * Implements the normal "collapse" border model defined in 6.7.10 in XSL 1.0. + * + * TODO Column groups are not yet checked in this algorithm! + */ +public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel { + + private static final int BEFORE = CommonBorderPaddingBackground.BEFORE; + private static final int AFTER = CommonBorderPaddingBackground.AFTER; + private static final int START = CommonBorderPaddingBackground.START; + private static final int END = CommonBorderPaddingBackground.END; + + public BorderInfo determineWinner(GridUnit currentGridUnit, + GridUnit otherGridUnit, int side, int flags) { + final boolean vertical = isVerticalRelation(side); + final int otherSide = getOtherSide(side); + + //Get cells + Cell currentCell = currentGridUnit.layoutManager; + Cell otherCell = null; + if (otherGridUnit != null) { + otherCell = otherGridUnit.layoutManager; + } + + //Get rows + Row currentRow = currentGridUnit.row; + Row otherRow = null; + if (vertical && otherCell != null) { + otherRow = otherGridUnit.row; + } + + //get bodies + Body currentBody = (Body)currentRow.getParent(); + Body otherBody = null; + if (otherRow != null) { + otherBody = (Body)otherRow.getParent(); + } + + //get columns + Column currentColumn = (Column)currentGridUnit.column; + Column otherColumn = null; + if (otherGridUnit != null) { + otherColumn = (Column)otherGridUnit.column; + } + + //TODO get column groups + + //Get table + TableLayoutManager table = (TableLayoutManager)currentBody.getParent(); + + //---------------------------------------------------------------------- + //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 (side == BEFORE + || side == AFTER + || (currentColumn.isFirst() && side == START) + || (currentColumn.isLast() && side == END)) { + //row + current[1] = currentRow.getFObj().getCommonBorderPaddingBackground().getBorderInfo(side); + } + if (otherRow != null) { + //row + other[1] = otherRow.getFObj().getCommonBorderPaddingBackground().getBorderInfo(otherSide); + } + if ((side == BEFORE && currentRow.isFirstInBody()) + || (side == AFTER && currentRow.isLastInBody()) + || (currentColumn.isFirst() && side == START) + || (currentColumn.isLast() && side == END)) { + //row group (=body, table-header or table-footer) + current[2] = currentBody.getFObj().getCommonBorderPaddingBackground().getBorderInfo(side); + } + if ((otherSide == BEFORE && otherRow.isFirstInBody()) + || (otherSide == AFTER && otherRow.isLastInBody())) { + //row group (=body, table-header or table-footer) + other[2] = otherBody.getFObj().getCommonBorderPaddingBackground().getBorderInfo(otherSide); + } + if ((side == BEFORE && otherGridUnit == null) + || (side == AFTER && otherGridUnit == null) + || (side == START) + || (side == END)) { + //column + current[3] = currentColumn.getFObj().getCommonBorderPaddingBackground().getBorderInfo(side); + } + if (otherColumn != null) { + //column + other[3] = otherColumn.getFObj().getCommonBorderPaddingBackground().getBorderInfo(otherSide); + } + //TODO current[4] and other[4] for column groups + if (otherGridUnit == null) { + //table + current[5] = table.getTable().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)) { + return null; //paint no border + } + + // *** 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 - 1; 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 - 1; i++) { + if ((current[i] != null) && (current[i].getStyle() != Constants.EN_NONE)) { + found = true; + break; + } + 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 - 1; 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 - 1; i++) { + if ((current[i] != null) && (current[i].getRetainedWidth() == width)) { + count++; + if (widest == null) { + widest = current[i]; + } + break; + } else { + current[i] = null; //Discard the narrower ones + } + if ((other[i] != null) && (other[i].getRetainedWidth() == width)) { + count++; + if (widest == null) { + widest = other[i]; + } + break; + } else { + other[i] = null; //Discard the narrower ones + } + } + if (count == 1) { + return widest; + } else { + return null; + } + } + + 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 - 1; i++) { + if (current[i] != null) { + int currPref = getPreferenceValue(current[i].getStyle()); + if (currPref > pref) { + pref = currPref; + break; + } + } + if (other[i] != null) { + int currPref = getPreferenceValue(other[i].getStyle()); + if (currPref > pref) { + pref = currPref; + break; + } + } + } + BorderInfo preferred = null; + int count = 0; + //See if there's only one with the preferred border style + for (int i = 0; i < current.length - 1; 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 - 1; i++) { + if (current[i] != null) { + return current[i]; + } + if (other[i] != null) { + return other[i]; + } + } + return null; + } + +} diff --git a/src/java/org/apache/fop/layoutmgr/table/Column.java b/src/java/org/apache/fop/layoutmgr/table/Column.java index be8bafa23..a09d6f788 100644 --- a/src/java/org/apache/fop/layoutmgr/table/Column.java +++ b/src/java/org/apache/fop/layoutmgr/table/Column.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2004 The Apache Software Foundation. + * Copyright 1999-2005 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import org.apache.fop.layoutmgr.BreakPoss; import org.apache.fop.layoutmgr.LayoutContext; import org.apache.fop.layoutmgr.PositionIterator; import org.apache.fop.layoutmgr.TraitSetter; +import org.apache.fop.fo.flow.TableCell; import org.apache.fop.fo.flow.TableColumn; import org.apache.fop.area.Area; import org.apache.fop.area.Block; @@ -35,17 +36,36 @@ import org.apache.fop.area.Block; * column properties. */ public class Column extends AbstractLayoutManager { + private TableColumn fobj; + private Length columnWidth; /** * Create a new column layout manager. + * @param node the table-column FO */ public Column(TableColumn node) { super(node); fobj = node; + columnWidth = fobj.getColumnWidth(); } + /** @return the table-column FO */ + public TableColumn getFObj() { + return this.fobj; + } + + /** @return true if the column is the first column */ + public boolean isFirst() { + return ((TableLayoutManager)getParent()).isFirst(this); + } + + /** @return true if the column is the last column */ + public boolean isLast() { + return ((TableLayoutManager)getParent()).isLast(this); + } + /** * Get the next break possibility. * Columns do not create or return any areas. @@ -80,13 +100,21 @@ public class Column extends AbstractLayoutManager { return null; } + /** + * Overrides the default column-with coming from the FO. + * @param width the new width to use + */ + public void setWidth(Length width) { + this.columnWidth = width; + } + /** * Get the width of this column. * * @return the width of the column */ public Length getWidth() { - return fobj.getColumnWidth(); + return columnWidth; } /** diff --git a/src/java/org/apache/fop/layoutmgr/table/GridUnit.java b/src/java/org/apache/fop/layoutmgr/table/GridUnit.java new file mode 100644 index 000000000..e89f9d8ec --- /dev/null +++ b/src/java/org/apache/fop/layoutmgr/table/GridUnit.java @@ -0,0 +1,131 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* $Id$ */ + +package org.apache.fop.layoutmgr.table; + +import org.apache.fop.fo.flow.Table; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo; + + +public class GridUnit { + + /** layout manager for the cell occupying this grid unit, may be null */ + public Cell layoutManager; + /** layout manager for the column that this grid unit belongs to */ + public Column column; + /** layout manager for the row that this grid unit belongs to */ + public Row row; + /** index of grid unit within cell in column direction */ + public int colSpanIndex; + /** index of grid unit within cell in row direction */ + public int rowSpanIndex; + /** effective borders for a cell slot (used for collapsing border model) */ + public CommonBorderPaddingBackground effBorders; + + public GridUnit(Cell layoutManager, int colSpanIndex) { + this.layoutManager = layoutManager; + this.colSpanIndex = colSpanIndex; + this.rowSpanIndex = 0; + } + + public GridUnit(Cell layoutManager) { + this(layoutManager, 0); + } + + /** @return true if the grid unit is the primary of a cell */ + public boolean isPrimaryGridUnit() { + return (colSpanIndex == 0) && (rowSpanIndex == 0); + } + + /** @return true if the grid unit is the last in column spanning direction */ + public boolean isLastGridUnitColSpan() { + if (layoutManager != null) { + return (colSpanIndex == layoutManager.getFObj().getNumberColumnsSpanned() - 1); + } else { + return true; + } + } + + /** @return true if the grid unit is the last in column spanning direction */ + public boolean isLastGridUnitRowSpan() { + if (layoutManager != null) { + return (rowSpanIndex == layoutManager.getFObj().getNumberRowsSpanned() - 1); + } else { + return true; + } + } + + /** @return true if the cell is part of a span in column direction */ + public boolean isColSpan() { + return (colSpanIndex > 0); + } + + public BorderInfo getOriginalBorderInfoForCell(int side) { + if (layoutManager != null) { + return layoutManager.getFObj().getCommonBorderPaddingBackground().getBorderInfo(side); + } else { + return null; + } + } + + /** + * Assign the borders from the given cell to this cell info. Used in + * case of separate border model. + * @param current cell to take the borders from + */ + public void assignBorder(Cell current) { + if (current != null) { + this.effBorders = current.getFObj().getCommonBorderPaddingBackground(); + } + } + + /** + * Assign the borders directly. + * @param borders the borders to use + */ + public void assignBorder(CommonBorderPaddingBackground borders) { + if (borders != null) { + this.effBorders = borders; + } + } + + /** + * Resolve collapsing borders for the given cell and store the resulting + * borders in this cell info. Use in case of the collapsing border model. + * @param current cell to resolve borders for + * @param before cell before the current cell, if any + * @param after cell after the current cell, if any + * @param start cell preceeding the current cell, if any + * @param end cell succeeding of the current cell, if any + */ + public static void resolveBorder(Table table, + CommonBorderPaddingBackground target, + GridUnit current, GridUnit other, int side) { + if (current == null) { + return; + } + + CollapsingBorderModel borderModel = CollapsingBorderModel.getBorderModelFor( + table.getBorderCollapse()); + target.setBorderInfo( + borderModel.determineWinner(current, other, + side, 0), side); + } + +} \ No newline at end of file diff --git a/src/java/org/apache/fop/layoutmgr/table/Row.java b/src/java/org/apache/fop/layoutmgr/table/Row.java index f29402c12..87ac870a2 100644 --- a/src/java/org/apache/fop/layoutmgr/table/Row.java +++ b/src/java/org/apache/fop/layoutmgr/table/Row.java @@ -20,8 +20,10 @@ package org.apache.fop.layoutmgr.table; import org.apache.fop.fo.FONode; import org.apache.fop.fo.flow.Table; +import org.apache.fop.fo.flow.TableBody; import org.apache.fop.fo.flow.TableCell; import org.apache.fop.fo.flow.TableRow; +import org.apache.fop.fo.properties.CommonBorderPaddingBackground; import org.apache.fop.fo.properties.LengthRangeProperty; import org.apache.fop.layoutmgr.BlockStackingLayoutManager; import org.apache.fop.layoutmgr.LayoutManager; @@ -52,16 +54,9 @@ import java.util.ListIterator; */ public class Row extends BlockStackingLayoutManager { - /** Used by CellInfo: Indicates start of cell. */ - public static final int CI_START_OF_CELL = 0; - /** Used by CellInfo: Indicates part of a spanned cell in column direction. */ - public static final int CI_COL_SPAN = 1; - /** Used by CellInfo: Indicates part of a spanned cell in row direction. */ - public static final int CI_ROW_SPAN = 2; - private TableRow fobj; - private List cellList = null; + private List gridUnits = null; private List columns = null; private int referenceIPD; private int rowHeight; @@ -85,6 +80,11 @@ public class Row extends BlockStackingLayoutManager { fobj = node; } + /** @return the table-row FO */ + public TableRow getFObj() { + return this.fobj; + } + /** * @return the table owning this row */ @@ -105,8 +105,32 @@ public class Row extends BlockStackingLayoutManager { columns = cols; } - private void setupCells() { - cellList = new java.util.ArrayList(); + /** @return true if this is the layout manager for the first row in a body. */ + public boolean isFirstInBody() { + return ((TableBody)getFObj().getParent()).isFirst(getFObj()); + } + + /** @return true if this is the layout manager for the last row in a body. */ + public boolean isLastInBody() { + return ((TableBody)getFObj().getParent()).isLast(getFObj()); + } + + /** + * Gets the Column at a given index. + * @param index index of the column (index must be >= 1) + * @return the requested Column + */ + private Column getColumn(int index) { + int size = columns.size(); + if (index > size - 1) { + return (Column)columns.get(size - 1); + } else { + return (Column)columns.get(index - 1); + } + } + + private void prepareGridUnits() { + gridUnits = new java.util.ArrayList(); List availableCells = new java.util.ArrayList(); // add cells to list while (childLMiter.hasNext()) { @@ -125,55 +149,108 @@ public class Row extends BlockStackingLayoutManager { if (cell.hasColumnNumber()) { colnum = cell.getColumnNumber(); } - while (colnum > cellList.size()) { - cellList.add(null); + while (colnum > gridUnits.size()) { + gridUnits.add(null); } - if (cellList.get(colnum - 1) != null) { + if (gridUnits.get(colnum - 1) != null) { log.error("Overlapping cell at position " + colnum); } //Add cell info for primary slot - cellList.set(colnum - 1, new CellInfo(cellLM)); + GridUnit info = new GridUnit(cellLM); + info.row = this; + gridUnits.set(colnum - 1, info); + info.column = getColumn(colnum); //Add cell infos on spanned slots if any for (int j = 1; j < cell.getNumberColumnsSpanned(); j++) { colnum++; - if (colnum > cellList.size()) { - cellList.add(new CellInfo(CI_COL_SPAN)); + GridUnit infoSpan = new GridUnit(cellLM, j); + infoSpan.row = this; + infoSpan.column = getColumn(colnum); + if (colnum > gridUnits.size()) { + gridUnits.add(infoSpan); } else { - if (cellList.get(colnum - 1) != null) { + if (gridUnits.get(colnum - 1) != null) { log.error("Overlapping cell at position " + colnum); + //TODO throw layout exception } - cellList.set(colnum - 1, new CellInfo(CI_COL_SPAN)); + gridUnits.set(colnum - 1, infoSpan); } } colnum++; } - //Post-processing the list (looking for gaps) - int pos = 1; - ListIterator ppIter = cellList.listIterator(); - while (ppIter.hasNext()) { - CellInfo cellInfo = (CellInfo)ppIter.next(); - if (cellInfo == null) { - //Add cell info on empty cell - ppIter.set(new CellInfo(CI_START_OF_CELL)); + //Post-processing the list (looking for gaps and resolve start and end borders) + postProcessGridUnits(); + } + + private void postProcessGridUnits() { + 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 GridUnit(null); + gu.row = this; + gu.column = getColumn(pos); + gridUnits.set(pos - 1, gu); + } + } + + //Border resolution now that the empty grid units are filled + for (int pos = 1; pos <= gridUnits.size(); pos++) { + GridUnit gu = (GridUnit)gridUnits.get(pos - 1); + + //Border resolution + if (getTable().isSeparateBorderModel()) { + gu.assignBorder(gu.layoutManager); + } else { + GridUnit start = null; + int find = pos - 1; + while (find >= 1) { + GridUnit candidate = (GridUnit)gridUnits.get(find - 1); + if (candidate.isLastGridUnitColSpan()) { + start = candidate; + break; + } + find--; + } + GridUnit end = null; + find = pos + 1; + while (find <= gridUnits.size()) { + GridUnit candidate = (GridUnit)gridUnits.get(find - 1); + if (candidate.isPrimaryGridUnit()) { + end = candidate; + break; + } + } + CommonBorderPaddingBackground borders = new CommonBorderPaddingBackground(); + GridUnit.resolveBorder(getTable(), borders, gu, + (start != null ? start : null), + CommonBorderPaddingBackground.START); + GridUnit.resolveBorder(getTable(), borders, gu, + (end != null ? end : null), + CommonBorderPaddingBackground.END); + gu.effBorders = borders; + //Only start and end borders here, before and after during layout + //TODO resolve before and after borders during layout } - pos++; } } - + /** * Get the cell info for a cell. * * @param pos the position of the cell (must be >= 1) * @return the cell info object */ - protected CellInfo getCellInfo(int pos) { - if (cellList == null) { - setupCells(); + protected GridUnit getCellInfo(int pos) { + if (gridUnits == null) { + prepareGridUnits(); } - if (pos <= cellList.size()) { - return (CellInfo)cellList.get(pos - 1); + if (pos <= gridUnits.size()) { + return (GridUnit)gridUnits.get(pos - 1); } else { return null; } @@ -189,11 +266,10 @@ public class Row extends BlockStackingLayoutManager { */ public BreakPoss getNextBreakPoss(LayoutContext context) { //LayoutManager curLM; // currently active LM - CellInfo curCellInfo; //currently active cell info + GridUnit curGridUnit; //currently active grid unit BreakPoss lastPos = null; List breakList = new java.util.ArrayList(); - List spannedColumns = new java.util.ArrayList(); int min = 0; int opt = 0; @@ -205,9 +281,9 @@ public class Row extends BlockStackingLayoutManager { int startColumn = 1; boolean over = false; - while ((curCellInfo = getCellInfo(startColumn)) != null) { - Cell cellLM = curCellInfo.layoutManager; - if (curCellInfo.isColSpan()) { + while ((curGridUnit = getCellInfo(startColumn)) != null) { + Cell cellLM = curGridUnit.layoutManager; + if (curGridUnit.isColSpan()) { //skip spanned slots startColumn++; continue; @@ -227,15 +303,17 @@ public class Row extends BlockStackingLayoutManager { stackSize)); //Determine which columns this cell will occupy - getColumnsForCell(cellLM, startColumn, spannedColumns); + List spannedGridUnits = new java.util.ArrayList(); + getGridUnitsForCell(cellLM, startColumn, spannedGridUnits); int childRefIPD = 0; - for (int i = 0; i < spannedColumns.size(); i++) { - Column col = (Column)spannedColumns.get(i); + for (int i = 0; i < spannedGridUnits.size(); i++) { + Column col = ((GridUnit)spannedGridUnits.get(i)).column; childRefIPD += col.getWidth().getValue(); } childLC.setRefIPD(childRefIPD); if (cellLM != null) { + cellLM.addGridUnitsFromRow(spannedGridUnits); cellLM.setInRowIPDOffset(ipdOffset); while (!cellLM.isFinished()) { if ((bp = cellLM.getNextBreakPoss(childLC)) != null) { @@ -309,8 +387,8 @@ public class Row extends BlockStackingLayoutManager { boolean fin = true; startColumn = 1; //Check if any of the cell LMs haven't finished, yet - while ((curCellInfo = getCellInfo(startColumn)) != null) { - Cell cellLM = curCellInfo.layoutManager; + while ((curGridUnit = getCellInfo(startColumn)) != null) { + Cell cellLM = curGridUnit.layoutManager; if (cellLM == null) { //skip empty cell startColumn++; @@ -333,27 +411,13 @@ public class Row extends BlockStackingLayoutManager { return breakPoss; } - /** - * Gets the Column at a given index. - * @param index index of the column (index must be >= 1) - * @return the requested Column - */ - private Column getColumn(int index) { - int size = columns.size(); - if (index > size - 1) { - return (Column)columns.get(size - 1); - } else { - return (Column)columns.get(index - 1); - } - } - /** * Determines the columns that are spanned by the given cell. * @param cellLM table-cell LM * @param startCell starting cell index (must be >= 1) * @param spannedColumns List to receive the applicable columns */ - private void getColumnsForCell(Cell cellLM, int startCell, List spannedColumns) { + private void getGridUnitsForCell(Cell cellLM, int startCell, List spannedColumns) { int count; if (cellLM != null) { count = cellLM.getFObj().getNumberColumnsSpanned(); @@ -362,7 +426,7 @@ public class Row extends BlockStackingLayoutManager { } spannedColumns.clear(); for (int i = 0; i < count; i++) { - spannedColumns.add(getColumn(startCell + i)); + spannedColumns.add(this.gridUnits.get(startCell + i - 1)); } } @@ -376,13 +440,13 @@ public class Row extends BlockStackingLayoutManager { */ protected void reset(Position pos) { //LayoutManager curLM; // currently active LM - CellInfo curCellInfo; + GridUnit curGridUnit; int cellIndex = 1; if (pos == null) { - while ((curCellInfo = getCellInfo(cellIndex)) != null) { - if (curCellInfo.layoutManager != null) { - curCellInfo.layoutManager.resetPosition(null); + while ((curGridUnit = getCellInfo(cellIndex)) != null) { + if (curGridUnit.layoutManager != null) { + curGridUnit.layoutManager.resetPosition(null); } cellIndex++; } @@ -390,10 +454,10 @@ public class Row extends BlockStackingLayoutManager { RowPosition rpos = (RowPosition)pos; List breaks = rpos.cellBreaks; - while ((curCellInfo = getCellInfo(cellIndex)) != null) { - if (curCellInfo.layoutManager != null) { + while ((curGridUnit = getCellInfo(cellIndex)) != null) { + if (curGridUnit.layoutManager != null) { List childbreaks = (List)breaks.get(cellIndex); - curCellInfo.layoutManager.resetPosition( + curGridUnit.layoutManager.resetPosition( (Position)childbreaks.get(childbreaks.size() - 1)); } cellIndex++; @@ -540,27 +604,5 @@ public class Row extends BlockStackingLayoutManager { } } - private class CellInfo { - - /** layout manager for this cell, may be null */ - public Cell layoutManager; - /** flags for this cell, on of Row.CI_* */ - public int flags = CI_START_OF_CELL; - - public CellInfo(Cell layoutManager) { - this.layoutManager = layoutManager; - } - - public CellInfo(int flags) { - this.flags = flags; - } - - /** @return true if the cell is part of a span in column direction */ - public boolean isColSpan() { - return (flags & CI_COL_SPAN) != 0; - } - - } - } diff --git a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java index 5a1ee47d2..37fe8cd78 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableLayoutManager.java @@ -280,10 +280,28 @@ public class TableLayoutManager extends BlockStackingLayoutManager { //new property evaluation context so proportional-column-width() works //correctly. if (columns.size() == 0) { - this.columns.add(new Column(getTable().getDefaultColumn())); + Column col = new Column(getTable().getDefaultColumn()); + col.setParent(this); + this.columns.add(col); } } + /** + * @param column the column to check + * @return true if the column is the first column + */ + public boolean isFirst(Column column) { + return (this.columns.size() == 0 || this.columns.get(0) == column); + } + + /** + * @param column the column to check + * @return true if the column is the last column + */ + public boolean isLast(Column column) { + return (this.columns.size() == 0 || this.columns.get(columns.size() - 1) == column); + } + /** * Get the break possibility and height of the table header or footer. * @@ -455,6 +473,6 @@ public class TableLayoutManager extends BlockStackingLayoutManager { reset(null); } } - + }