package org.apache.fop.layoutmgr;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
import org.apache.fop.traits.MinOptMax;
*/
public class BalancingColumnBreakingAlgorithm extends PageBreakingAlgorithm {
- private static final Log LOG = LogFactory.getLog(BalancingColumnBreakingAlgorithm.class);
-
private int columnCount;
- private int fullLen;
- private int idealPartLen;
-
- /**
- * Construct a balancing column breaking algorithm.
- * @param topLevelLM the top level layout manager
- * @param pageProvider the page provider
- * @param layoutListener the layout listener
- * @param alignment alignment of the paragraph/page. One of
- * {@link org.apache.fop.fo.Constants#EN_START},
- * {@link org.apache.fop.fo.Constants#EN_JUSTIFY},
- * {@link org.apache.fop.fo.Constants#EN_CENTER},
- * {@link org.apache.fop.fo.Constants#EN_END}.
- * For pages, {@link org.apache.fop.fo.Constants#EN_BEFORE} and
- * {@link org.apache.fop.fo.Constants#EN_AFTER}
- * are mapped to the corresponding inline properties,
- * {@link org.apache.fop.fo.Constants#EN_START} and
- * {@link org.apache.fop.fo.Constants#EN_END}.
- * @param alignmentLast alignment of the paragraph's last line
- * @param footnoteSeparatorLength length of footnote separator
- * @param partOverflowRecovery {@code true} if too long elements should be moved to
- * the next line/part
- * @param columnCount number of columns
- * @see PageBreakingAlgorithm
- */
- public BalancingColumnBreakingAlgorithm( // CSOK: ParameterNumber
- LayoutManager topLevelLM,
+ private List<Integer> idealBreaks;
+
+ public BalancingColumnBreakingAlgorithm(LayoutManager topLevelLM,
PageProvider pageProvider,
PageBreakingLayoutListener layoutListener,
int alignment, int alignmentLast,
/** {@inheritDoc} */
protected double computeDemerits(KnuthNode activeNode,
KnuthElement element, int fitnessClass, double r) {
- double dem = super.computeDemerits(activeNode, element, fitnessClass, r);
- if (LOG.isTraceEnabled()) {
- LOG.trace("original demerit=" + dem + " " + totalWidth
- + " line=" + activeNode.line + "/" + columnCount
- + " pos=" + activeNode.position + "/" + (par.size() - 1));
+ double demerits = Double.MAX_VALUE;
+ if (idealBreaks == null) {
+ idealBreaks = calculateIdealBreaks(activeNode.position);
}
- int remParts = columnCount - activeNode.line;
- int curPos = par.indexOf(element);
- if (fullLen == 0) {
- fullLen = ElementListUtils.calcContentLength(par, activeNode.position, par.size() - 1);
- this.idealPartLen = (fullLen / columnCount);
+ LinkedList<Integer> curPossibility = getPossibilityTrail(activeNode);
+ boolean notIdeal = false;
+ int idealDemerit = columnCount + 1 - curPossibility.size();
+ if (curPossibility.size() > idealBreaks.size()) {
+ return demerits;
}
- int partLen = ElementListUtils.calcContentLength(par, activeNode.position, curPos - 1);
- int restLen = ElementListUtils.calcContentLength(par, curPos - 1, par.size() - 1);
- int avgRestLen = 0;
- if (remParts > 0) {
- avgRestLen = restLen / remParts;
+ for (int breakPos = 0; breakPos < curPossibility.size(); breakPos++) {
+ if (curPossibility.get(breakPos) != 0 && curPossibility.get(breakPos)
+ != idealBreaks.get(breakPos)) {
+ notIdeal = true;
+ break;
+ }
}
- if (LOG.isTraceEnabled()) {
- LOG.trace("remaining parts: " + remParts + " rest len: " + restLen
- + " avg=" + avgRestLen);
+ if (!notIdeal) {
+ demerits = idealDemerit;
}
- double balance = (idealPartLen - partLen) / 1000f;
- if (LOG.isTraceEnabled()) {
- LOG.trace("balance=" + balance);
+ return demerits;
+ }
+
+ private List<Integer> calculateIdealBreaks(int startPos) {
+ List<ColumnContent> previousPreviousBreaks = null;
+ List<ColumnContent> previousBreaks = null;
+ List<ColumnContent> breaks = new ArrayList<ColumnContent>();
+ breaks.add(new ColumnContent(startPos, par.size() - 1));
+ do {
+ previousPreviousBreaks = previousBreaks;
+ previousBreaks = breaks;
+ breaks = getInitialBreaks(startPos, getAverageColumnLength(breaks));
+ } while (!breaks.equals(previousBreaks) && !breaks.equals(previousPreviousBreaks));
+ breaks = sortElementsForBreaks(breaks);
+ return getElementIdBreaks(breaks, startPos);
+ }
+
+ private static final class ColumnContent {
+
+ public final int startIndex;
+
+ public final int endIndex;
+
+ ColumnContent(int startIndex, int endIndex) {
+ this.startIndex = startIndex;
+ this.endIndex = endIndex;
}
- double absBalance = Math.abs(balance);
- dem = absBalance;
- //Step 1: This does the rough balancing
- if (columnCount > 2) {
- if (balance > 0) {
- //shorter parts are less desired than longer ones
- dem = dem * 1.2f;
+
+ @Override
+ public int hashCode() {
+ return startIndex << 16 | endIndex;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ColumnContent)) {
+ return false;
+ } else {
+ ColumnContent other = (ColumnContent) obj;
+ return other.startIndex == startIndex && other.endIndex == endIndex;
}
- } else {
- if (balance < 0) {
- //shorter parts are less desired than longer ones
- dem = dem * 1.2f;
+ }
+
+ @Override
+ public String toString() {
+ return startIndex + "-" + endIndex;
+ }
+
+ }
+
+ private int getAverageColumnLength(List<ColumnContent> columns) {
+ int totalLength = 0;
+ for (ColumnContent col : columns) {
+ totalLength += calcContentLength(par, col.startIndex, col.endIndex);
+ }
+ return totalLength / columnCount;
+ }
+
+ private List<ColumnContent> getInitialBreaks(int startIndex, int averageColLength) {
+ List<ColumnContent> initialColumns = new ArrayList<ColumnContent>();
+ int colStartIndex = startIndex;
+ int totalLength = 0;
+ int idealBreakLength = averageColLength;
+ int previousBreakLength = 0;
+ int prevBreakIndex = startIndex;
+ boolean prevIsBox = false;
+ int colNumber = 1;
+ for (int i = startIndex; i < par.size(); i++) {
+ KnuthElement element = (KnuthElement) par.get(i);
+ if (isLegalBreak(i, prevIsBox)) {
+ int breakLength = totalLength
+ + (element instanceof KnuthPenalty ? element.getWidth() : 0);
+ if (breakLength > idealBreakLength && colNumber < columnCount) {
+ int breakIndex;
+ if (breakLength - idealBreakLength > idealBreakLength - previousBreakLength) {
+ breakIndex = prevBreakIndex;
+ totalLength = previousBreakLength;
+ } else {
+ breakIndex = element instanceof KnuthPenalty ? i : i - 1;
+ totalLength = breakLength;
+ }
+ initialColumns.add(new ColumnContent(colStartIndex, breakIndex));
+ i = getNextStartIndex(breakIndex);
+ colStartIndex = i--;
+ colNumber++;
+ idealBreakLength += averageColLength;
+ } else {
+ previousBreakLength = breakLength;
+ prevBreakIndex = element instanceof KnuthPenalty ? i : i - 1;
+ prevIsBox = false;
+ }
+ } else {
+ totalLength += element instanceof KnuthPenalty ? 0 : element.getWidth();
+ prevIsBox = element instanceof KnuthBox;
}
}
- //Step 2: This helps keep the trailing parts shorter than the previous ones
- dem += (avgRestLen) / 1000f;
+ assert initialColumns.size() == columnCount - 1;
+ initialColumns.add(new ColumnContent(colStartIndex, par.size() - 1));
+ return initialColumns;
+ }
- if (activeNode.line >= columnCount) {
- //We don't want more columns than available
- dem = Double.MAX_VALUE;
+ private int getNextStartIndex(int breakIndex) {
+ int startIndex = breakIndex;
+ @SuppressWarnings("unchecked")
+ Iterator<KnuthElement> iter = par.listIterator(breakIndex);
+ while (iter.hasNext() && !(iter.next() instanceof KnuthBox)) {
+ startIndex++;
}
- if (LOG.isTraceEnabled()) {
- LOG.trace("effective dem=" + dem + " " + totalWidth);
+ return startIndex;
+ }
+
+ private List<ColumnContent> sortElementsForBreaks(List<ColumnContent> breaks) {
+ boolean changes;
+ /* Relax factor to make balancing more visually appealing as in some cases
+ * strict balancing would lead to ragged column endings. */
+ int fFactor = 4000;
+ do {
+ changes = false;
+ ColumnContent curColumn = breaks.get(breaks.size() - 1);
+ int curColLength = calcContentLength(par, curColumn.startIndex, curColumn.endIndex);
+ for (int colIndex = (breaks.size() - 1); colIndex > 0; colIndex--) {
+ ColumnContent prevColumn = breaks.get(colIndex - 1);
+ int prevColLength = calcContentLength(par, prevColumn.startIndex, prevColumn.endIndex);
+ if (prevColLength < curColLength) {
+ int newBreakIndex = curColumn.startIndex;
+ boolean prevIsBox = true;
+ while (newBreakIndex <= curColumn.endIndex && !(isLegalBreak(newBreakIndex, prevIsBox))) {
+ newBreakIndex++;
+ prevIsBox = par.get(newBreakIndex) instanceof KnuthBox;
+ }
+ if (newBreakIndex < curColumn.endIndex) {
+ if (prevIsBox) {
+ newBreakIndex--;
+ }
+ int newStartIndex = getNextStartIndex(newBreakIndex);
+ int newPrevColLength = calcContentLength(par, prevColumn.startIndex, newBreakIndex);
+ if (newPrevColLength <= fFactor + curColLength) {
+ prevColumn = new ColumnContent(prevColumn.startIndex, newBreakIndex);
+ breaks.set(colIndex - 1, prevColumn);
+ breaks.set(colIndex, new ColumnContent(newStartIndex, curColumn.endIndex));
+ prevColLength = calcContentLength(par, prevColumn.startIndex, newBreakIndex);
+ changes = true;
+ }
+ }
+ }
+ curColLength = prevColLength;
+ curColumn = prevColumn;
+ }
+ } while (changes);
+ return breaks;
+ }
+
+ private boolean isLegalBreak(int index, boolean prevIsBox) {
+ KnuthElement element = (KnuthElement) par.get(index);
+ return element instanceof KnuthPenalty && element.getPenalty() < KnuthPenalty.INFINITE
+ || prevIsBox && element instanceof KnuthGlue;
+ }
+
+ private int calcContentLength(KnuthSequence par, int startIndex, int endIndex) {
+ return ElementListUtils.calcContentLength(par, startIndex, endIndex) + getPenaltyWidth(endIndex);
+ }
+
+ private int getPenaltyWidth(int index) {
+ KnuthElement element = (KnuthElement) par.get(index);
+ return element instanceof KnuthPenalty ? element.getWidth() : 0;
+ }
+
+ private List<Integer> getElementIdBreaks(List<ColumnContent> breaks, int startPos) {
+ List<Integer> elementIdBreaks = new ArrayList<Integer>();
+ elementIdBreaks.add(startPos);
+ for (ColumnContent column : breaks) {
+ if (breaks.get(breaks.size() - 1).equals(column)) {
+ continue;
+ }
+ elementIdBreaks.add(column.endIndex);
}
- return dem;
+ return elementIdBreaks;
+ }
+
+ private LinkedList<Integer> getPossibilityTrail(KnuthNode activeNode) {
+ LinkedList<Integer> trail = new LinkedList<Integer>();
+ KnuthNode previous = activeNode;
+ do {
+ trail.addFirst(previous.position);
+ previous = previous.previous;
+ } while (previous != null);
+ return trail;
}
}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks different row heights when balancing columns
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
+ <fo:layout-master-set>
+ <fo:simple-page-master margin-right="1cm" margin-left="1cm" margin-bottom="0.3cm" margin-top="1cm" page-width="21cm" page-height="29.7cm" master-name="all">
+ <fo:region-body column-count="4" margin-left="0cm" margin-bottom="1cm" />
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="all">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:table table-layout="fixed" width="100%">
+ <fo:table-column column-width="proportional-column-width(1)" />
+ <fo:table-header>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>header</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-header>
+ <fo:table-footer>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>footer</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-footer>
+ <fo:table-body>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>1</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>3</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>5</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block font-size="36">7</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block font-size="18">9</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block font-size="18">11</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>13</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>15</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>17</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ <fo:block span="all">spanning</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="13" xpath="//span[1]/flow[4]//block[1]/block[2]/block[1]/block[1]/lineArea[1]/text[1]/word[1]"/>
+ <eval expected="11" xpath="//span[1]/flow[3]//block[1]/block[3]/block[1]/block[1]/lineArea[1]/text[1]/word[1]"/>
+ <eval expected="5" xpath="//span[1]/flow[1]//block[1]/block[4]/block[1]/block[1]/lineArea[1]/text[1]/word[1]"/>
+ </checks>
+</testcase>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks balanced columns with tables and blocks
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
+ <fo:layout-master-set>
+ <fo:simple-page-master margin-right="1cm" margin-left="1cm" margin-bottom="0.3cm" margin-top="1cm" page-width="21cm" page-height="29.7cm" master-name="all">
+ <fo:region-body column-count="4" margin-left="0cm" margin-bottom="1cm" margin-right="0cm" margin-top="0cm"/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="all">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:table table-layout="fixed" width="100%">
+ <fo:table-column column-width="proportional-column-width(1)" />
+ <fo:table-header>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>header 1</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-header>
+ <fo:table-footer>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>footer 1</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-footer>
+ <fo:table-body>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>1</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>2</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>3</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>4</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>5</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>6</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>7</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>8</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>9</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>10</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>11</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ <fo:block>Some text</fo:block>
+ <fo:table table-layout="fixed" width="100%">
+ <fo:table-column column-width="proportional-column-width(1)" />
+ <fo:table-header>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>header 2</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-header>
+ <fo:table-footer>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>footer 2</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-footer>
+ <fo:table-body>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>1</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>2</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>3</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>4</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>5</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>6</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>7</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>8</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>9</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>10</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>11</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ <fo:block span="all">spanning</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="6" xpath="//span[1]/flow[1]/block[1]/block[7]/block[1]/block[1]/lineArea[1]/text[1]/word[1]"/>
+ <eval expected="Some" xpath="//span[1]/flow[2]/block[2]/lineArea[1]/text[1]/word[1]"/>
+ <eval expected="11" xpath="//span[1]/flow[4]/block[1]/block[6]/block[1]/block[1]/lineArea[1]/text[1]/word[1]"/>
+ </checks>
+</testcase>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks balancing with multiple tables with headers / footers
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" >
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="simple" page-height="5in" page-width="5in">
+ <fo:region-body region-name="PageBody" column-count="3"/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="simple">
+ <fo:flow flow-name="PageBody">
+ <fo:block>
+ <fo:table table-layout="fixed" >
+ <fo:table-header font-weight="bold">
+ <fo:table-cell>
+ <fo:block>T1 H1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell>
+ <fo:block>T1 H2</fo:block>
+ </fo:table-cell>
+ </fo:table-header>
+ <fo:table-body>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>T1 C1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell>
+ <fo:block>T1 C2</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ <fo:table table-layout="fixed" >
+ <fo:table-header font-weight="bold">
+ <fo:table-cell>
+ <fo:block>T2 H1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell>
+ <fo:block>T2 H2</fo:block>
+ </fo:table-cell>
+ </fo:table-header>
+ <fo:table-footer font-weight="bold">
+ <fo:table-cell>
+ <fo:block>T2 F1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell>
+ <fo:block>T2 F2</fo:block>
+ </fo:table-cell>
+ </fo:table-footer>
+ <fo:table-body>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>T2 C1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell>
+ <fo:block>T2 C2</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>T2 C1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell>
+ <fo:block>T2 C2</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>T2 C1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell>
+ <fo:block>T2 C2</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>T2 C1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell>
+ <fo:block>T2 C2</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>T2 C1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell>
+ <fo:block>T2 C2</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>T2 C1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell>
+ <fo:block>T2 C2</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ <fo:table table-layout="fixed" >
+ <fo:table-header font-weight="bold">
+ <fo:table-cell>
+ <fo:block>T3 H1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell>
+ <fo:block>T3 H2</fo:block>
+ </fo:table-cell>
+ </fo:table-header>
+ <fo:table-body>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>T3 C1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell>
+ <fo:block>T3 C2</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>T3 C1</fo:block>
+ </fo:table-cell>
+ <fo:table-cell>
+ <fo:block>T3 C2</fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ </fo:block>
+ <fo:block span="all">SUMMARY</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="86400" xpath="//flow[1]/@bpd"/>
+ <eval expected="86400" xpath="//flow[2]/@bpd"/>
+ <eval expected="43200" xpath="//flow[3]/@bpd"/>
+ </checks>
+</testcase>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks balancing when KnuthGlue elements are introduced
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="page"
+ page-height="320pt" page-width="420pt" margin="10pt">
+ <fo:region-body column-count="2"/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+
+ <fo:page-sequence master-reference="page">
+ <fo:flow flow-name="xsl-region-body" text-align="justify">
+
+ <fo:block border="1pt solid black" border-before-width.conditionality="retain"
+ border-after-width.conditionality="retain">Lorem ipsum dolor sit amet, consectetur
+ adipiscing elit. Vestibulum arcu felis, gravida vitae laoreet in, molestie nec libero.
+ Mauris non enim diam. Pellentesque nisl diam, aliquet nec euismod vitae, convallis nec
+ massa. Mauris gravida arcu ac erat euismod molestie. Maecenas eget neque in sem aliquam
+ viverra. Vivamus dictum lobortis scelerisque. In cursus venenatis arcu, id vulputate nisi
+ interdum non. Nulla venenatis porta ipsum. Aenean mattis placerat nibh, porttitor consequat
+ orci suscipit sed. Sed eget orci nisi, eget commodo arcu. Nulla urna urna, tristique ac
+ sagittis ut, mollis in leo. Praesent et dui nulla. Nullam nec dui quis velit pretium
+ tristique. Nullam et neque eros. Sed non dolor id dolor vulputate faucibus. Suspendisse non
+ lacus eget nibh faucibus scelerisque eget vel nunc. In malesuada ornare eros vitae sagittis.
+ Aliquam erat volutpat. Aenean feugiat dignissim lobortis.</fo:block>
+
+ <fo:block span="all"/>
+
+ </fo:flow>
+ </fo:page-sequence>
+
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="placerat" xpath="//span[1]/flow[1]/block[1]/lineArea[14]/text[1]/word[5]"/>
+ <eval expected="nibh," xpath="//span[1]/flow[2]/block[1]/lineArea[1]/text[1]/word[1]"/>
+ </checks>
+</testcase>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks column balancing with different row heights
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
+ <fo:layout-master-set>
+ <fo:simple-page-master margin-right="1cm" margin-left="1cm" margin-bottom="0.3cm" margin-top="1cm" page-width="21cm" page-height="29.7cm" master-name="all">
+ <fo:region-body column-count="4" margin-left="0cm" margin-bottom="1cm"/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="all">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:table table-layout="fixed" width="100%">
+ <fo:table-column column-width="proportional-column-width(1)" />
+ <fo:table-header>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>header</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-header>
+ <fo:table-footer>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>footer</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-footer>
+ <fo:table-body>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block font-size="36">1</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block font-size="12">2</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block font-size="12">3</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block font-size="12">4</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block font-size="6">5</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block font-size="6">6</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block font-size="12">7</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block font-size="12">8</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block font-size="12">9</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ <fo:block span="all">spanning</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="4" xpath="//span[1]/flow[2]/block[1]/block[4]/block[1]/block[1]/lineArea[1]/text[1]/word[1]"/>
+ <eval expected="7" xpath="//span[1]/flow[3]/block[1]/block[4]/block[1]/block[1]/lineArea[1]/text[1]/word[1]"/>
+ <eval expected="8" xpath="//span[1]/flow[4]/block[1]/block[2]/block[1]/block[1]/lineArea[1]/text[1]/word[1]"/>
+ </checks>
+</testcase>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks column balancing with different row heights.
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
+ <fo:layout-master-set>
+ <fo:simple-page-master margin-right="1cm" margin-left="1cm" margin-bottom="0.3cm" margin-top="1cm" page-width="21cm" page-height="29.7cm" master-name="all">
+ <fo:region-body column-count="4" margin-left="0cm" margin-bottom="1cm"/>
+ </fo:simple-page-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="all">
+ <fo:flow flow-name="xsl-region-body">
+ <fo:table table-layout="fixed" width="100%">
+ <fo:table-column column-width="proportional-column-width(1)" />
+ <fo:table-header>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>header</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-header>
+ <fo:table-footer>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block>footer</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-footer>
+ <fo:table-body>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block font-size="36">1</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block font-size="12">2</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block font-size="12">3</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block font-size="12">4</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block font-size="12">5</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block font-size="12">6</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block font-size="6">7</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block font-size="6">8</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ <fo:table-row>
+ <fo:table-cell>
+ <fo:block>
+ <fo:block font-size="12">9</fo:block>
+ </fo:block>
+ </fo:table-cell>
+ </fo:table-row>
+ </fo:table-body>
+ </fo:table>
+ <fo:block span="all">spanning</fo:block>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="1" xpath="//span[1]/flow[1]/block[1]/block[2]/block[1]/block[1]/lineArea[1]/text[1]/word[1]"/>
+ <eval expected="4" xpath="//span[1]/flow[2]/block[1]/block[4]/block[1]/block[1]/lineArea[1]/text[1]/word[1]"/>
+ <eval expected="6" xpath="//span[1]/flow[3]/block[1]/block[3]/block[1]/block[1]/lineArea[1]/text[1]/word[1]"/>
+ <eval expected="7" xpath="//span[1]/flow[4]/block[1]/block[2]/block[1]/block[1]/lineArea[1]/text[1]/word[1]"/>
+ </checks>
+</testcase>