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-ffa450edef68tags/Root_Temp_KnuthStylePageBreaking
@@ -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); | |||
} | |||
} | |||
@@ -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. |
@@ -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); | |||
} |
@@ -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. | |||
* |
@@ -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(); | |||
} | |||
} | |||
} | |||
@@ -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); | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
/** |
@@ -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); | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
} | |||
@@ -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); | |||
} | |||
} | |||
} | |||