Просмотр исходного кода

Implemented the resolution of collapsing borders in the FO tree, for every situation (normal, cell at the top of a page, cell broken), taking conditionality, headers and footers into account.


git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@603945 13f79535-47bb-0310-9956-ffa450edef68
tags/fop-0_95beta
Vincent Hennebert 16 лет назад
Родитель
Сommit
709bda3d6c

+ 1
- 1
src/java/org/apache/fop/fo/FOPropertyMapping.java Просмотреть файл

@@ -1141,7 +1141,7 @@ public final class FOPropertyMapping implements Constants {
m.useGeneric(genericSpace);
corr = new SpacePropertyMaker(m);
corr.setCorresponding(PR_MARGIN_TOP, PR_MARGIN_TOP, PR_MARGIN_RIGHT);
corr.setUseParent(true);
corr.setUseParent(false);
corr.setRelative(true);
addPropertyMaker("space-before", m);


+ 23
- 1
src/java/org/apache/fop/fo/flow/table/BorderSpecification.java Просмотреть файл

@@ -20,6 +20,7 @@
package org.apache.fop.fo.flow.table;

import org.apache.fop.fo.Constants;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo;

/**
@@ -28,6 +29,8 @@ import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo;
*/
public/*TODO*/ class BorderSpecification {

private static BorderSpecification defaultBorder;

private BorderInfo borderInfo;

private int holder;
@@ -43,6 +46,14 @@ public/*TODO*/ class BorderSpecification {
this.holder = holder;
}

static synchronized BorderSpecification getDefaultBorder() {
if (defaultBorder == null) {
defaultBorder = new BorderSpecification(CommonBorderPaddingBackground
.getDefaultBorderInfo(), Constants.FO_TABLE_CELL);
}
return defaultBorder;
}

/**
* Returns this border's informations.
*
@@ -66,6 +77,17 @@ public/*TODO*/ class BorderSpecification {

/** {@inheritDoc} */
public String toString() {
return "{" + borderInfo + ", " + holder + "}";
String holderName = "";
switch (holder) {
case Constants.FO_TABLE: holderName = "table"; break;
case Constants.FO_TABLE_COLUMN: holderName = "table-column"; break;
case Constants.FO_TABLE_HEADER: holderName = "table-header"; break;
case Constants.FO_TABLE_FOOTER: holderName = "table-footer"; break;
case Constants.FO_TABLE_BODY: holderName = "table-body"; break;
case Constants.FO_TABLE_ROW: holderName = "table-row"; break;
case Constants.FO_TABLE_CELL: holderName = "table-cell"; break;
default: assert false;
}
return "{" + borderInfo + ", " + holderName + "}";
}
}

+ 321
- 92
src/java/org/apache/fop/fo/flow/table/CollapsingBorderResolver.java Просмотреть файл

@@ -19,6 +19,7 @@

package org.apache.fop.fo.flow.table;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

@@ -31,138 +32,366 @@ 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.
* The previously registered row, either in the header or the body(-ies), but not in
* the footer (handled separately).
*/
private List previousRowSave;

private TableBody currentTablePart;
private List/*<GridUnit>*/ previousRow;

private boolean firstInTable;

private boolean firstInPart;
private List/*<GridUnit>*/ footerFirstRow;

private List footerFirstRow;
/** The last currently registered footer row. */
private List/*<GridUnit>*/ footerLastRow;

private List footerLastRow;
private Resolver delegate;

private boolean inFooter;
private Resolver resolverInFooter;

CollapsingBorderResolver(Table table) {
this.table = table;
firstInTable = true;
}
private List/*<ConditionalBorder>*/ leadingBorders;

/** {@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();
private List/*<ConditionalBorder>*/ trailingBorders;

/**
* Base class for delegate resolvers. Implementation of the State design pattern: the
* treatment differs slightly whether we are in the table's header, footer or body. To
* avoid complicated if statements, specialised delegate resolvers will be used
* instead.
*/
private abstract class Resolver {

protected TableBody tablePart;

protected boolean firstInPart;

/**
* Integrates border-before specified on the table and its column.
*
* @param row the first row of the table (in the header, or in the body if the
* table has no header)
* @param withLeadingTrailing
* @param withNonLeadingTrailing
* @param withRest
*/
void resolveBordersFirstRowInTable(List/*<GridUnit>*/ row, boolean withLeadingTrailing,
boolean withNonLeadingTrailing, boolean withRest) {
assert firstInTable;
for (int i = 0; i < row.size(); i++) {
TableColumn column = table.getColumn(i);
((GridUnit) row.get(i)).integrateBorderSegment(
CommonBorderPaddingBackground.BEFORE, column, withLeadingTrailing,
withNonLeadingTrailing, withRest);
}
firstInTable = false;
}

/**
* Resolves border-after for the first row, border-before for the second one.
*
* @param rowBefore
* @param rowAfter
*/
void resolveBordersBetweenRows(List/*<GridUnit>*/ rowBefore, List/*<GridUnit>*/ rowAfter) {
assert rowBefore != null && rowAfter != null;
for (int i = 0; i < rowAfter.size(); i++) {
GridUnit gu = (GridUnit) rowAfter.get(i);
if (gu.getRowSpanIndex() == 0) {
gu.resolveBorder(CommonBorderPaddingBackground.BEFORE, tableRow);
}
if (gu.isLastGridUnitRowSpan()) {
gu.resolveBorder(CommonBorderPaddingBackground.AFTER, tableRow);
GridUnit beforeGU = (GridUnit) rowBefore.get(i);
gu.resolveBorder(beforeGU, CommonBorderPaddingBackground.BEFORE);
}
}
}
if (inFooter) {
if (footerFirstRow == null) {
footerFirstRow = row;

/** Integrates the border-after of the part. */
void resolveBordersLastRowInPart(List/*<GridUnit>*/ row, boolean withLeadingTrailing,
boolean withNonLeadingTrailing, boolean withRest) {
for (int i = 0; i < row.size(); i++) {
((GridUnit) row.get(i)).integrateBorderSegment(CommonBorderPaddingBackground.AFTER,
tablePart, withLeadingTrailing, withNonLeadingTrailing, withRest);
}
footerLastRow = row;
} else if (firstInTable) {
// Resolve border-before for the first row in the table
}

/**
* Integrates border-after specified on the table and its columns.
*
* @param row the last row of the footer, or of the last body if the table has no
* footer
* @param withLeadingTrailing
* @param withNonLeadingTrailing
* @param withRest
*/
void resolveBordersLastRowInTable(List/*<GridUnit>*/ row, boolean withLeadingTrailing,
boolean withNonLeadingTrailing, boolean withRest) {
for (int i = 0; i < row.size(); i++) {
TableColumn column = table.getColumn(i);
((GridUnit) row.get(i)).resolveBorder(CommonBorderPaddingBackground.BEFORE, column);
((GridUnit) row.get(i)).integrateBorderSegment(CommonBorderPaddingBackground.AFTER,
column, withLeadingTrailing, withNonLeadingTrailing, withRest);
}
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);

/**
* Integrates either border-before specified on the table and its columns if the
* table has no header, or border-after specified on the cells of the header's
* last row. For the case the grid unit are at the top of a page.
*
* @param row
*/
void integrateLeadingBorders(List/*<GridUnit>*/ row) {
for (int i = 0; i < table.getNumberOfColumns(); i++) {
GridUnit gu = (GridUnit) row.get(i);
ConditionalBorder border = (ConditionalBorder) leadingBorders.get(i);
gu.integrateCompetingBorder(CommonBorderPaddingBackground.BEFORE, border,
true, false, true);
}
firstInPart = false;
}
if (previousRow != null) {
// Resolve after/before borders between rows
for (int i = 0; i < row.size(); i++) {

/**
* Integrates either border-after specified on the table and its columns if the
* table has no footer, or border-before specified on the cells of the footer's
* first row. For the case the grid unit are at the bottom of a page.
*
* @param row
*/
void integrateTrailingBorders(List/*<GridUnit>*/ row) {
for (int i = 0; i < table.getNumberOfColumns(); i++) {
GridUnit gu = (GridUnit) row.get(i);
if (gu.getRowSpanIndex() == 0) {
GridUnit beforeGU = (GridUnit) previousRow.get(i);
gu.resolveBorder(beforeGU, CommonBorderPaddingBackground.BEFORE);
}
ConditionalBorder border = (ConditionalBorder) trailingBorders.get(i);
gu.integrateCompetingBorder(CommonBorderPaddingBackground.AFTER, border,
true, false, true);
}
}
// 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);

void startPart(TableBody part) {
tablePart = part;
firstInPart = true;
}

/**
* Resolves the applicable borders for the given row.
* <ul>
* <li>Integrates the border-before/after of the containing table-row if any;</li>
* <li>Integrates the border-before of the containing part, if first row;</li>
* <li>Resolves border-start/end between grid units.</li>
* </ul>
*
* @param row the row being finished
* @param container the containing element
*/
void endRow(List/*<GridUnit>*/ 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.integrateBorderSegment(CommonBorderPaddingBackground.BEFORE, tableRow,
true, true, true);
}
if (gu.isLastGridUnitRowSpan()) {
gu.integrateBorderSegment(CommonBorderPaddingBackground.AFTER, tableRow,
true, true, true);
}
}
}
if (firstInPart) {
// Integrate the border-before of the part
for (int i = 0; i < row.size(); i++) {
((GridUnit) row.get(i)).integrateBorderSegment(
CommonBorderPaddingBackground.BEFORE, tablePart, true, true, true);
}
firstInPart = false;
}
// Resolve start/end borders in the row
Iterator guIter = row.iterator();
GridUnit gu = (GridUnit) guIter.next();
gu.integrateBorderSegment(CommonBorderPaddingBackground.START, container);
while (guIter.hasNext()) {
GridUnit guEnd = (GridUnit) guIter.next();
if (gu.isLastGridUnitColSpan()) {
gu.resolveBorder(guEnd, CommonBorderPaddingBackground.END);
}
gu = guEnd;
}
gu = guEnd;
gu.integrateBorderSegment(CommonBorderPaddingBackground.END, container);
}
gu.resolveBorder(CommonBorderPaddingBackground.END, container);

previousRow = row;
void endPart() {
resolveBordersLastRowInPart(previousRow, true, true, true);
}

abstract void endTable();
}

/** {@inheritDoc} */
public void startPart(TableBody part) {
firstInPart = true;
currentTablePart = part;
if (part.isTableFooter()) {
inFooter = true;
previousRowSave = previousRow;
previousRow = null;
private class ResolverInHeader extends Resolver {

void endRow(List/*<GridUnit>*/ row, TableCellContainer container) {
super.endRow(row, container);
if (previousRow != null) {
resolveBordersBetweenRows(previousRow, row);
} else {
/*
* This is a bit hacky...
* The two only sensible values for border-before on the header's first row are:
* - at the beginning of the table (normal case)
* - if the header is repeated after each page break
* To represent those values we (ab)use the nonLeadingTrailing and the rest
* fields of ConditionalBorder. But strictly speaking this is not their
* purposes.
*/
for (Iterator guIter = row.iterator(); guIter.hasNext();) {
ConditionalBorder borderBefore = ((GridUnit) guIter.next()).borderBefore;
borderBefore.leadingTrailing = null;
borderBefore.rest = borderBefore.nonLeadingTrailing;
}
resolveBordersFirstRowInTable(row, false, true, true);
}
previousRow = row;
}
}

/** {@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);
void endPart() {
super.endPart();
leadingBorders = new ArrayList(table.getNumberOfColumns());
/*
* Another hack...
* The border-after of a header is always the same. Leading and rest don't
* apply to cells in the header since they are never broken. To ease
* resolution we override the (normally unused) leadingTrailing and rest
* fields of ConditionalBorder with the only sensible nonLeadingTrailing
* field. That way grid units from the body will always resolve against the
* same, normal header border.
*/
for (Iterator guIter = previousRow.iterator(); guIter.hasNext();) {
ConditionalBorder borderAfter = ((GridUnit) guIter.next()).borderAfter;
borderAfter.leadingTrailing = borderAfter.nonLeadingTrailing;
borderAfter.rest = borderAfter.nonLeadingTrailing;
leadingBorders.add(borderAfter);
}
}
if (inFooter) {
inFooter = false;
previousRow = previousRowSave;
void endTable() {
throw new IllegalStateException();
}
}

/** {@inheritDoc} */
public void endTable() {
if (footerFirstRow != null) {
private class ResolverInFooter extends Resolver {

void endRow(List/*<GridUnit>*/ row, TableCellContainer container) {
super.endRow(row, container);
if (footerFirstRow == null) {
footerFirstRow = row;
} else {
// There is a previous row
resolveBordersBetweenRows(footerLastRow, row);
}
footerLastRow = row;
}

void endPart() {
resolveBordersLastRowInPart(footerLastRow, true, true, true);
trailingBorders = new ArrayList(table.getNumberOfColumns());
// See same method in ResolverInHeader for an explanation of the hack
for (Iterator guIter = footerFirstRow.iterator(); guIter.hasNext();) {
ConditionalBorder borderBefore = ((GridUnit) guIter.next()).borderBefore;
borderBefore.leadingTrailing = borderBefore.nonLeadingTrailing;
borderBefore.rest = borderBefore.nonLeadingTrailing;
trailingBorders.add(borderBefore);
}
}

void endTable() {
// 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);
resolveBordersBetweenRows(previousRow, footerFirstRow);
// See endRow method in ResolverInHeader for an explanation of the hack
for (Iterator guIter = footerLastRow.iterator(); guIter.hasNext();) {
ConditionalBorder borderAfter = ((GridUnit) guIter.next()).borderAfter;
borderAfter.leadingTrailing = null;
borderAfter.rest = borderAfter.nonLeadingTrailing;
}
resolveBordersLastRowInTable(footerLastRow, false, true, true);
}
List lastRow;
if (footerLastRow != null) {
lastRow = footerLastRow;
} else {
lastRow = previousRow;
}

private class ResolverInBody extends Resolver {

void endRow(List/*<GridUnit>*/ row, TableCellContainer container) {
super.endRow(row, container);
if (firstInTable) {
resolveBordersFirstRowInTable(row, true, true, true);
} else {
// Either there is a header, and then previousRow is set to the header's last row,
// or this is not the first row in the body, and previousRow is not null
resolveBordersBetweenRows(previousRow, row);
integrateLeadingBorders(row);
}
integrateTrailingBorders(row);
previousRow = row;
}
// 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);

void endTable() {
if (resolverInFooter != null) {
resolverInFooter.endTable();
} else {
// Trailing and rest borders already resolved with integrateTrailingBorders
resolveBordersLastRowInTable(previousRow, false, true, false);
}
}
}

CollapsingBorderResolver(Table table) {
this.table = table;
firstInTable = true;
}

/** {@inheritDoc} */
public void endRow(List/*<GridUnit>*/ row, TableCellContainer container) {
delegate.endRow(row, container);
}

/** {@inheritDoc} */
public void startPart(TableBody part) {
if (part.isTableHeader()) {
delegate = new ResolverInHeader();
} else {
if (leadingBorders == null) {
// No header, leading borders determined by the table
leadingBorders = new ArrayList(table.getNumberOfColumns());
for (Iterator colIter = table.getColumns().iterator(); colIter.hasNext();) {
// See endRow method in ResolverInHeader for an explanation of the hack
ConditionalBorder border = ((TableColumn) colIter.next()).borderBefore;
border.leadingTrailing = border.rest;
leadingBorders.add(border);
}
}
if (part.isTableFooter()) {
resolverInFooter = new ResolverInFooter();
delegate = resolverInFooter;
} else {
if (trailingBorders == null) {
// No footer, trailing borders determined by the table
trailingBorders = new ArrayList(table.getNumberOfColumns());
for (Iterator colIter = table.getColumns().iterator(); colIter.hasNext();) {
// See endRow method in ResolverInHeader for an explanation of the hack
ConditionalBorder border = ((TableColumn) colIter.next()).borderAfter;
border.leadingTrailing = border.rest;
trailingBorders.add(border);
}
}
delegate = new ResolverInBody();
}
}
delegate.startPart(part);
}

/** {@inheritDoc} */
public void endPart(TableBody part) {
delegate.endPart();
}

/** {@inheritDoc} */
public void endTable() {
delegate.endTable();
delegate = null;
}
}

+ 204
- 0
src/java/org/apache/fop/fo/flow/table/ConditionalBorder.java Просмотреть файл

@@ -0,0 +1,204 @@
/*
* 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.layoutmgr.table.CollapsingBorderModel;

/**
* A class that holds the three possible values for a border-before/after on a table-cell,
* in the collapsing model. These three values are (for border-before, similar for
* border-after):
* <ul>
* <li>non-leading: common case, when a cell follows the cell before on a same page;</li>
* <li>leading: when the table is broken and the cell appears at the top of a page, in
* which case its border must be resolved with the header (or the top of the table)
* instead of with the previous cell;</li>
* <li>rest: when a cell is broken over several pages; same as leading but with
* conditionality taken into account.</li>
* </ul>
*/
public class ConditionalBorder {

/** Special case: the cell is at the top or the bottom of the page. */
BorderSpecification leadingTrailing;

/** Normal case, no break. */
BorderSpecification nonLeadingTrailing;

/** Special case: break inside the cell. */
BorderSpecification rest;

/** The model used to resolve borders. */
private CollapsingBorderModel collapsingBorderModel;

private ConditionalBorder(BorderSpecification leadingTrailing,
BorderSpecification nonLeadingTrailing, BorderSpecification rest,
CollapsingBorderModel collapsingBorderModel) {
this.leadingTrailing = leadingTrailing;
this.nonLeadingTrailing = nonLeadingTrailing;
this.rest = rest;
this.collapsingBorderModel = collapsingBorderModel;
}

/**
* Creates a new conditional border.
*
* @param borderSpecification the border specification to take as a basis
* @param collapsingBorderModel the model that will be used to resolved borders
*/
ConditionalBorder(BorderSpecification borderSpecification,
CollapsingBorderModel collapsingBorderModel) {
leadingTrailing = borderSpecification;
nonLeadingTrailing = leadingTrailing;
if (borderSpecification.getBorderInfo().getWidth().isDiscard()) {
rest = BorderSpecification.getDefaultBorder();
} else {
rest = leadingTrailing;
}
this.collapsingBorderModel = collapsingBorderModel;
}

/**
* Resolves and updates the relevant parts of this border as well as the given one.
*
* @param competitor
* @param withLeadingTrailing
* @param withNonLeadingTrailing
* @param withRest
*/
void resolve(ConditionalBorder competitor, boolean withLeadingTrailing,
boolean withNonLeadingTrailing, boolean withRest) {
if (withLeadingTrailing) {
BorderSpecification resolvedBorder = collapsingBorderModel.determineWinner(
leadingTrailing, competitor.leadingTrailing);
if (resolvedBorder != null) {
leadingTrailing = resolvedBorder;
competitor.leadingTrailing = resolvedBorder;
}
}
if (withNonLeadingTrailing) {
BorderSpecification resolvedBorder = collapsingBorderModel.determineWinner(
nonLeadingTrailing, competitor.nonLeadingTrailing);
if (resolvedBorder != null) {
nonLeadingTrailing = resolvedBorder;
competitor.nonLeadingTrailing = resolvedBorder;
}
}
if (withRest) {
BorderSpecification resolvedBorder = collapsingBorderModel.determineWinner(rest,
competitor.rest);
if (resolvedBorder != null) {
rest = resolvedBorder;
competitor.rest = resolvedBorder;
}
}
}

/**
* Integrates the given segment in this border. Unlike for
* {@link #integrateSegment(ConditionalBorder, boolean, boolean, boolean)}, this
* method nicely handles the case where the CollapsingBorderModel returns null, by
* keeping the components to their old values.
*
* @param competitor
* @param withLeadingTrailing
* @param withNonLeadingTrailing
* @param withRest
*/
void integrateCompetingSegment(ConditionalBorder competitor, boolean withLeadingTrailing,
boolean withNonLeadingTrailing, boolean withRest) {
if (withLeadingTrailing) {
BorderSpecification resolvedBorder = collapsingBorderModel.determineWinner(
leadingTrailing, competitor.leadingTrailing);
if (resolvedBorder != null) {
leadingTrailing = resolvedBorder;
}
}
if (withNonLeadingTrailing) {
BorderSpecification resolvedBorder = collapsingBorderModel.determineWinner(
nonLeadingTrailing, competitor.nonLeadingTrailing);
if (resolvedBorder != null) {
nonLeadingTrailing = resolvedBorder;
}
}
if (withRest) {
BorderSpecification resolvedBorder = collapsingBorderModel.determineWinner(rest,
competitor.rest);
if (resolvedBorder != null) {
rest = resolvedBorder;
}
}
}

/**
* Updates this border after taking into account the given segment. The
* CollapsingBorderModel is not expected to return null.
*
* @param segment
* @param withLeadingTrailing
* @param withNonLeadingTrailing
* @param withRest
*/
void integrateSegment(ConditionalBorder segment, boolean withLeadingTrailing,
boolean withNonLeadingTrailing, boolean withRest) {
if (withLeadingTrailing) {
leadingTrailing = collapsingBorderModel.determineWinner(leadingTrailing,
segment.leadingTrailing);
assert leadingTrailing != null;
}
if (withNonLeadingTrailing) {
nonLeadingTrailing = collapsingBorderModel.determineWinner(nonLeadingTrailing,
segment.nonLeadingTrailing);
assert nonLeadingTrailing != null;
}
if (withRest) {
rest = collapsingBorderModel.determineWinner(rest, segment.rest);
assert rest != null;
}
}

/**
* Returns a shallow copy of this border.
*
* @return a copy of this border
*/
ConditionalBorder copy() {
return new ConditionalBorder(leadingTrailing, nonLeadingTrailing, rest,
collapsingBorderModel);
}

/** {@inheritDoc} */
public String toString() {
return "{non-leading: " + nonLeadingTrailing + ", leading: " + leadingTrailing + ", rest: "
+ rest + "}";
}

/**
* Returns a default border specification.
*
* @param collapsingBorderModel the model that will be used to resolve borders
* @return a border with style 'none' for all of the three components
*/
static ConditionalBorder getDefaultBorder(CollapsingBorderModel collapsingBorderModel) {
BorderSpecification defaultBorderSpec = BorderSpecification.getDefaultBorder();
return new ConditionalBorder(defaultBorderSpec, defaultBorderSpec, defaultBorderSpec,
collapsingBorderModel);
}
}

+ 5
- 6
src/java/org/apache/fop/fo/flow/table/EmptyGridUnit.java Просмотреть файл

@@ -19,8 +19,6 @@

package org.apache.fop.fo.flow.table;

import org.apache.fop.fo.Constants;
import org.apache.fop.fo.properties.CommonBorderPaddingBackground;

/**
* GridUnit subclass for empty grid units.
@@ -40,10 +38,11 @@ public class EmptyGridUnit extends GridUnit {
}

/** {@inheritDoc} */
protected void setBorder(int side) {
resolvedBorders[side] = new BorderSpecification(
CommonBorderPaddingBackground.getDefaultBorderInfo(),
Constants.FO_TABLE_CELL);
protected void setBordersFromCell() {
borderBefore = ConditionalBorder.getDefaultBorder(collapsingBorderModel);
borderAfter = ConditionalBorder.getDefaultBorder(collapsingBorderModel);
borderStart = BorderSpecification.getDefaultBorder();
borderEnd = BorderSpecification.getDefaultBorder();
}

/** {@inheritDoc} */

+ 128
- 32
src/java/org/apache/fop/fo/flow/table/GridUnit.java Просмотреть файл

@@ -90,9 +90,12 @@ public class GridUnit {
/** flags for the grid unit */
private byte flags = 0;

protected BorderSpecification[] resolvedBorders;
ConditionalBorder borderBefore;
ConditionalBorder borderAfter;
BorderSpecification borderStart;
BorderSpecification borderEnd;

private CollapsingBorderModel collapsingBorderModel;
protected CollapsingBorderModel collapsingBorderModel;

/**
* Creates a new grid unit.
@@ -156,26 +159,27 @@ public class GridUnit {
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);
}
setBordersFromCell();
}
}

protected void setBorder(int side) {
resolvedBorders[side] = cell.resolvedBorders[side];
protected void setBordersFromCell() {
borderBefore = cell.borderBefore.copy();
if (rowSpanIndex > 0) {
borderBefore.nonLeadingTrailing = null;
}
borderAfter = cell.borderAfter.copy();
if (!isLastGridUnitRowSpan()) {
borderAfter.nonLeadingTrailing = null;
}
if (colSpanIndex == 0) {
borderStart = cell.borderStart;
}
if (isLastGridUnitColSpan()) {
borderEnd = cell.borderEnd;
}
}

public TableCell getCell() {
@@ -301,8 +305,30 @@ public class GridUnit {
}

private void setBorderInfo(int side) {
if (resolvedBorders[side] != null) {
effectiveBorders.setBorderInfo(resolvedBorders[side].getBorderInfo(), side);
switch (side) {
case CommonBorderPaddingBackground.BEFORE:
if (borderBefore.nonLeadingTrailing/*TODO*/ != null) {
effectiveBorders.setBorderInfo(borderBefore.nonLeadingTrailing.getBorderInfo(),
side);
}
break;
case CommonBorderPaddingBackground.AFTER:
if (borderAfter.nonLeadingTrailing/*TODO*/ != null) {
effectiveBorders.setBorderInfo(borderAfter.nonLeadingTrailing.getBorderInfo(),
side);
}
break;
case CommonBorderPaddingBackground.START:
if (borderStart != null) {
effectiveBorders.setBorderInfo(borderStart.getBorderInfo(), side);
}
break;
case CommonBorderPaddingBackground.END:
if (borderEnd != null) {
effectiveBorders.setBorderInfo(borderEnd.getBorderInfo(), side);
}
break;
default: assert false;
}
}

@@ -332,26 +358,96 @@ public class GridUnit {
* CommonBorderPaddingBackground.BEFORE|AFTER|START|END)
*/
void resolveBorder(GridUnit other, int side) {
BorderSpecification resolvedBorder = collapsingBorderModel.determineWinner(
resolvedBorders[side], other.resolvedBorders[CollapsingBorderModel
.getOtherSide(side)]);
if (resolvedBorder != null) {
this.resolvedBorders[side] = resolvedBorder;
other.resolvedBorders[CollapsingBorderModel.getOtherSide(side)] = resolvedBorder;
switch (side) {
case CommonBorderPaddingBackground.BEFORE:
borderBefore.resolve(other.borderAfter, false, true, false);
break;
case CommonBorderPaddingBackground.AFTER:
borderAfter.resolve(other.borderBefore, false, true, false);
break;
case CommonBorderPaddingBackground.START:
BorderSpecification resolvedBorder = collapsingBorderModel.determineWinner(
borderStart, other.borderEnd);
if (resolvedBorder != null) {
this.borderStart = resolvedBorder;
other.borderEnd = resolvedBorder;
}
break;
case CommonBorderPaddingBackground.END:
resolvedBorder = collapsingBorderModel.determineWinner(
borderEnd, other.borderStart);
if (resolvedBorder != null) {
this.borderEnd = resolvedBorder;
other.borderStart = resolvedBorder;
}
break;
default: assert false;
}
}

/**
* Resolves the border on the given side of this grid unit, comparing it against the
* same border of the given parent element.
* For the given side, integrates in the conflict resolution the border segment of the
* given parent element.
*
* @param side the side to resolve (one of
* @param side the side to consider (either CommonBorderPaddingBackground.BEFORE or
* AFTER)
* @param parent a table element whose corresponding border coincides on the given
* side
*/
void integrateBorderSegment(int side, TableFObj parent, boolean withLeadingTrailing,
boolean withNonLeadingTrailing, boolean withRest) {
switch (side) {
case CommonBorderPaddingBackground.BEFORE:
borderBefore.integrateSegment(parent.borderBefore, withLeadingTrailing,
withNonLeadingTrailing, withRest);
break;
case CommonBorderPaddingBackground.AFTER:
borderAfter.integrateSegment(parent.borderAfter, withLeadingTrailing,
withNonLeadingTrailing, withRest);
break;
default: assert false;
}
}

/**
* For the given side, integrates in the conflict resolution the border segment of the
* given parent element.
*
* @param side the side to consider (one of
* CommonBorderPaddingBackground.BEFORE|AFTER|START|END)
* @param parent the parent element holding a competing border
* @param parent a table element whose corresponding border coincides on the given side
*/
void resolveBorder(int side, TableFObj parent) {
resolvedBorders[side] = collapsingBorderModel.determineWinner(resolvedBorders[side],
parent.resolvedBorders[side]);
void integrateBorderSegment(int side, TableFObj parent) {
switch (side) {
case CommonBorderPaddingBackground.BEFORE:
case CommonBorderPaddingBackground.AFTER:
integrateBorderSegment(side, parent, true, true, true);
break;
case CommonBorderPaddingBackground.START:
borderStart = collapsingBorderModel.determineWinner(borderStart,
parent.borderStart);
break;
case CommonBorderPaddingBackground.END:
borderEnd = collapsingBorderModel.determineWinner(borderEnd,
parent.borderEnd);
break;
default: assert false;
}
}

void integrateCompetingBorder(int side, ConditionalBorder competitor,
boolean withLeadingTrailing, boolean withNonLeadingTrailing, boolean withRest) {
switch (side) {
case CommonBorderPaddingBackground.BEFORE:
borderBefore.integrateCompetingSegment(competitor, withLeadingTrailing,
withNonLeadingTrailing, withRest);
break;
case CommonBorderPaddingBackground.AFTER:
borderAfter.integrateCompetingSegment(competitor, withLeadingTrailing,
withNonLeadingTrailing, withRest);
break;
default: assert false;
}
}

/**

+ 4
- 0
src/java/org/apache/fop/fo/flow/table/TableBody.java Просмотреть файл

@@ -261,6 +261,10 @@ public class TableBody extends TableCellContainer {
return FO_TABLE_BODY;
}

protected boolean isTableHeader() {
return false;
}

protected boolean isTableFooter() {
return false;
}

+ 38
- 6
src/java/org/apache/fop/fo/flow/table/TableFObj.java Просмотреть файл

@@ -42,7 +42,10 @@ public abstract class TableFObj extends FObj {
private Numeric borderEndPrecedence;
private Numeric borderStartPrecedence;

BorderSpecification[] resolvedBorders = new BorderSpecification[4]; // TODO
ConditionalBorder borderBefore;
ConditionalBorder borderAfter;
BorderSpecification borderStart;
BorderSpecification borderEnd;

CollapsingBorderModel collapsingBorderModel;
@@ -200,7 +203,6 @@ public abstract class TableFObj extends FObj {
if (!inMarker() && !table.isSeparateBorderModel()) {
collapsingBorderModel = CollapsingBorderModel.getBorderModelFor(table
.getBorderCollapse());
resolvedBorders = new BorderSpecification[4];
setCollapsedBorders();
}
}
@@ -226,8 +228,23 @@ public abstract class TableFObj extends FObj {
* @param side one of CommonBorderPaddingBackground.BEFORE|AFTER|START|END
*/
protected void createBorder(int side) {
resolvedBorders[side] = new BorderSpecification(getCommonBorderPaddingBackground()
.getBorderInfo(side), getNameId());
BorderSpecification borderSpec = new BorderSpecification(
getCommonBorderPaddingBackground().getBorderInfo(side), getNameId());
switch (side) {
case CommonBorderPaddingBackground.BEFORE:
borderBefore = new ConditionalBorder(borderSpec, collapsingBorderModel);
break;
case CommonBorderPaddingBackground.AFTER:
borderAfter = new ConditionalBorder(borderSpec, collapsingBorderModel);
break;
case CommonBorderPaddingBackground.START:
borderStart = borderSpec;
break;
case CommonBorderPaddingBackground.END:
borderEnd = borderSpec;
break;
default: assert false;
}
}

/**
@@ -240,7 +257,22 @@ public abstract class TableFObj extends FObj {
*/
protected void createBorder(int side, TableFObj competitor) {
createBorder(side);
resolvedBorders[side] = collapsingBorderModel.determineWinner(resolvedBorders[side],
competitor.resolvedBorders[side]);
switch (side) {
case CommonBorderPaddingBackground.BEFORE:
borderBefore.integrateSegment(competitor.borderBefore, true, true, true);
break;
case CommonBorderPaddingBackground.AFTER:
borderAfter.integrateSegment(competitor.borderAfter, true, true, true);
break;
case CommonBorderPaddingBackground.START:
borderStart = collapsingBorderModel.determineWinner(borderStart,
competitor.borderStart);
break;
case CommonBorderPaddingBackground.END:
borderEnd = collapsingBorderModel.determineWinner(borderEnd,
competitor.borderEnd);
break;
default: assert false;
}
}
}

+ 5
- 0
src/java/org/apache/fop/fo/flow/table/TableHeader.java Просмотреть файл

@@ -66,4 +66,9 @@ public class TableHeader extends TableBody {
public int getNameId() {
return FO_TABLE_HEADER;
}

/** {@inheritDoc} */
protected boolean isTableHeader() {
return true;
}
}

+ 56
- 2
src/java/org/apache/fop/fo/properties/CommonBorderPaddingBackground.java Просмотреть файл

@@ -130,6 +130,59 @@ public class CommonBorderPaddingBackground {
*/
private static BorderInfo defaultBorderInfo;

/**
* A conditional length of value 0. Returned by the
* {@link CommonBorderPaddingBackground#getBorderInfo(int)} method when the
* corresponding border isn't specified, to avoid to callers painful checks for null.
*/
private static class ConditionalNullLength extends CondLengthProperty {

/** {@inheritDoc} */
public Property getComponent(int cmpId) {
throw new UnsupportedOperationException();
}

/** {@inheritDoc} */
public Property getConditionality() {
throw new UnsupportedOperationException();
}

/** {@inheritDoc} */
public Length getLength() {
throw new UnsupportedOperationException();
}

/** {@inheritDoc} */
public Property getLengthComponent() {
throw new UnsupportedOperationException();
}

/** {@inheritDoc} */
public int getLengthValue() {
return 0;
}

/** {@inheritDoc} */
public int getLengthValue(PercentBaseContext context) {
return 0;
}

/** {@inheritDoc} */
public boolean isDiscard() {
return true;
}

/** {@inheritDoc} */
public void setComponent(int cmpId, Property cmpnValue, boolean isDefault) {
throw new UnsupportedOperationException();
}

/** {@inheritDoc} */
public String toString() {
return "CondLength[0mpt, discard]";
}
}

/**
* Returns a default BorderInfo of style none.
*
@@ -137,8 +190,9 @@ public class CommonBorderPaddingBackground {
*/
public static synchronized BorderInfo getDefaultBorderInfo() {
if (defaultBorderInfo == null) {
/* It is enough to set color and width to null, as they should never be consulted */
defaultBorderInfo = new BorderInfo(Constants.EN_NONE, null, null);
/* It is enough to set color to null, as it should never be consulted */
defaultBorderInfo = new BorderInfo(Constants.EN_NONE,
new ConditionalNullLength(), null);
}
return defaultBorderInfo;
}

+ 15
- 0
src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModel.java Просмотреть файл

@@ -181,8 +181,23 @@ public abstract class CollapsingBorderModel {
*
* @param border1 a border specification
* @param border2 another border specification
* @param discard true if the .conditionality component of the border width must be
* taken into account
* @return the winning border, null if the two borders are equivalent
*/
public abstract BorderSpecification determineWinner(BorderSpecification border1,
BorderSpecification border2, boolean discard);

/**
* Returns the border which wins the border conflict resolution. Same as
* {@link #determineWinner(BorderSpecification, BorderSpecification, boolean)
* determineWinner(border1, border2, false)}.
*
* @param border1 a border specification
* @param border2 another border specification
* @return the winning border, null if the two borders are equivalent
* @see determineWinner
*/
public abstract BorderSpecification determineWinner(BorderSpecification border1,
BorderSpecification border2);


+ 22
- 0
src/java/org/apache/fop/layoutmgr/table/CollapsingBorderModelEyeCatching.java Просмотреть файл

@@ -21,6 +21,7 @@ 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;

/**
@@ -30,6 +31,27 @@ import org.apache.fop.fo.properties.CommonBorderPaddingBackground.BorderInfo;
*/
public class CollapsingBorderModelEyeCatching extends CollapsingBorderModel {

/** {@inheritDoc} */
public BorderSpecification determineWinner(BorderSpecification border1,
BorderSpecification border2, boolean discard) {
BorderInfo bi1 = border1.getBorderInfo();
BorderInfo bi2 = border2.getBorderInfo();
if (discard) {
if (bi1.getWidth().isDiscard()) {
if (bi2.getWidth().isDiscard()) {
return new BorderSpecification(
CommonBorderPaddingBackground.getDefaultBorderInfo(), 0/*TODO*/);
} else {
return border2;
}
} else if (bi2.getWidth().isDiscard()) {
return border1;
}
}
// Otherwise, fall back to the default resolution algorithm
return determineWinner(border1, border2);
}

/** {@inheritDoc} */
public BorderSpecification determineWinner(BorderSpecification border1,
BorderSpecification border2) {

Загрузка…
Отмена
Сохранить