+ This test checks multi-column documents. This test: footnotes in multi-column documents (no spans).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ line 1
+ line 2
+ line 3
+ [*]
+
+
+
+
+
+
+ [*]
+
+
+
+ footnote footnote footnote footnote footnote footnote footnote footnote
+
+
+
+
+
+
+
+ line 4
+ line 5
+ line 6
+ line 7
+ line 8
+ line 9
+ [*]
+
+
+
+
+
+
+ [*]
+
+
+
+ footnote footnote footnote footnote footnote footnote footnote footnote
+
+
+
+
+
+
+
+ line 10
+ line 11
+ line 12
+ line 13
+ line 14
+ line 15
+ line 16
+ line 17
+ [*]
+
+
+
+
+
+
+ [*]
+
+
+
+ footnote footnote footnote footnote footnote footnote footnote footnote
+
+
+
+
+
+
+
+ line 18
+ line 19
+ line 20
+ line 21
+ line 22
+ line 23
+ line 24
+ line 25
+ line 26
+ line 27
+ line 28
+ line 29
+ line 30
+ line 31
+ line 32
+ line 33
+ line 34
+ line 35
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Index: src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
===================================================================
--- src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java (revision 1067762)
+++ src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java (revision )
@@ -20,7 +20,6 @@
package org.apache.fop.layoutmgr;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
@@ -45,40 +44,346 @@
/** List of PageBreakPosition elements. */
private LinkedList pageBreaks = null;
+ private final class Footnotes {
+
+ // demerits for a page break that splits a footnote
+ static final int SPLIT_FOOTNOTE_DEMERITS = 5000;
+ // demerits for a page break that defers a whole footnote to the following page
+ static final int DEFERRED_FOOTNOTE_DEMERITS = 10000;
+
+ // the length of the footnote separator
+ private MinOptMax separatorLength = null;
+
- /** Footnotes which are cited between the currently considered active node (previous
- * break) and the current considered break. Its type is
- * List<List<KnuthElement>>, it contains the sequences of KnuthElement
- * representing the footnotes bodies.
- */
- private List> footnotesList = null;
- /** Cumulated bpd of unhandled footnotes. */
- private List lengthList = null;
- /** Length of all the footnotes which will be put on the current page. */
+ /** Footnotes which are cited between the currently considered active node (previous
+ * break) and the current considered break. Its type is
+ * List<List<KnuthElement>>, it contains the sequences of KnuthElement
+ * representing the footnotes bodies.
+ */
+ private List> footnotesList = null;
+ /** Cumulated bpd of unhandled footnotes. */
+ private List lengthList = null;
+ /** Length of all the footnotes which will be put on the current page. */
- private int totalFootnotesLength = 0;
+ private int totalLength = 0;
+
- /**
- * Length of all the footnotes which have already been inserted, up to the currently
- * considered element. That is, footnotes from the currently considered page plus
- * footnotes from its preceding pages.
- */
+ /**
+ * Length of all the footnotes which have already been inserted, up to the currently
+ * considered element. That is, footnotes from the currently considered page plus
+ * footnotes from its preceding pages.
+ */
- private int insertedFootnotesLength = 0;
+ private int insertedFootnotesLength;
- /** True if footnote citations have been met since the beginning of the page sequence. */
- private boolean footnotesPending = false;
- /** True if the elements met after the previous break point contain footnote citations. */
- private boolean newFootnotes = false;
- /** Index of the first footnote met after the previous break point. */
- private int firstNewFootnoteIndex = 0;
- /** Index of the last footnote inserted on the current page. */
+ /** True if footnote citations have been met since the beginning of the page sequence. */
+ private boolean footnotesPending = false;
+ /** True if the elements met after the previous break point contain footnote citations. */
+ private boolean newFootnotes = false;
+ /** Index of the first footnote met after the previous break point. */
+ private int firstNewFootnoteIndex = 0;
+ /** Index of the last footnote inserted on the current page. */
- private int footnoteListIndex = 0;
+ private int listIndex;
- /** Index of the last element of the last footnote inserted on the current page. */
+ /** Index of the last element of the last footnote inserted on the current page. */
- private int footnoteElementIndex = -1;
+ private int elementIndex;
- // demerits for a page break that splits a footnote
- private int splitFootnoteDemerits = 5000;
- // demerits for a page break that defers a whole footnote to the following page
- private int deferredFootnoteDemerits = 10000;
- private MinOptMax footnoteSeparatorLength = null;
+ private Footnotes() {
+ }
+ void initialize() {
+ footnotes.insertedFootnotesLength = 0;
+ footnotes.listIndex = 0;
+ footnotes.elementIndex = -1;
+ }
+
+ /**
+ * Handles the footnotes cited inside a block-level box. Updates footnotesList and the
+ * value of totalLength with the lengths of the given footnotes.
+ * @param elementLists list of KnuthElement sequences corresponding to the footnotes
+ * bodies
+ */
+ void handleFootnotes(List> elementLists) {
+ // initialization
+ if (!footnotesPending) {
+ footnotesPending = true;
+ footnotesList = new ArrayList>();
+ lengthList = new ArrayList();
+ totalLength = 0;
+ }
+ if (!newFootnotes) {
+ newFootnotes = true;
+ firstNewFootnoteIndex = footnotesList.size();
+ }
+
+ // compute the total length of the footnotes
+ for (List noteList : elementLists) {
+
+ //Space resolution (Note: this does not respect possible stacking constraints
+ //between footnotes!)
+ SpaceResolver.resolveElementList(noteList);
+
+ footnotesList.add(noteList);
+ int noteLength = ElementListUtils.calcContentLength(noteList);
+ int prevLength = (lengthList == null || lengthList.isEmpty())
+ ? 0
+ : ListUtil.getLast(lengthList);
+ if (lengthList != null) {
+ lengthList.add(prevLength + noteLength);
+ }
+ totalLength += noteLength;
+ }
+ }
+
+ void resetFootnotes(int fromIndex, int toIndex) {
+ newFootnotes = false;
+ if (footnotesPending) {
+ // remove from footnotesList the note lists that will be met
+ // after the restarting point
+ KnuthElement resetElement;
+ int start = footnotesList.size();
+ int end = start;
+ for (int j = toIndex; j >= fromIndex; j--) {
+ resetElement = getElement(j);
+ if (resetElement instanceof KnuthBlockBox
+ && ((KnuthBlockBox) resetElement).hasAnchors()) {
+ start -= ((KnuthBlockBox) resetElement).getElementLists().size();
+ }
+ }
+ footnotesList.subList(start, end).clear();
+ lengthList.subList(start, end).clear();
+ // update totalLength
+ totalLength = (lengthList.isEmpty() ? 0 : ListUtil.getLast(lengthList));
+ // update footnotesPending;
+ footnotesPending = !footnotesList.isEmpty();
+ listIndex = footnotesList.size() - 1;
+ elementIndex = (listIndex < 0
+ ? -1 : getList(listIndex).size() - 1);
+ }
+ }
+
+ int getDifference(KnuthPageNode pageNode, int elementIndex) {
+
+ int contentWidth = totalWidth - pageNode.totalWidth;
+
+ int totalIntrusions = 0;
+
+ // compute the total length of the footnotes not yet inserted
+ int allFootnotes = totalLength;
+ KnuthPageNode lastPageNode = pageNode;
+ while (lastPageNode != null
+ && !pageProvider.endPage(lastPageNode.line - 1)) {
+ lastPageNode = (KnuthPageNode) lastPageNode.previous;
+ }
+ allFootnotes -= (lastPageNode == null ? 0 : lastPageNode.totalFootnotes);
+
+ if (allFootnotes > 0) {
+ // this page/column contains some footnote citations
+ // add the footnote separator width
+ totalIntrusions += separatorLength.getOpt();
+ totalIntrusions += allFootnotes;
+ insertedFootnotesLength = totalLength;
+ listIndex = footnotesList.size() - 1;
+ this.elementIndex = getList(listIndex).size() - 1;
+
+ int availableWidth = getLineWidth(pageNode.line);
+ if ((contentWidth + totalIntrusions) > availableWidth) {
+ // see if part of the footnotes can be deferred
+ boolean canDeferOldFN = canDeferOldFootnotes(pageNode, elementIndex);
+ if (canDeferOldFN || newFootnotes) {
+ availableWidth -= (contentWidth + separatorLength.getOpt());
+ int footnoteSplit = (availableWidth <= 0 ? 0
+ : getFootnoteSplit(pageNode, availableWidth, canDeferOldFN));
+ if (footnoteSplit > 0) {
+ // the total of content + separator + all footnotes does not fit, but
+ // it is allowed to break or even defer footnotes if either:
+ // - there are new footnotes in the last piece of content, and
+ // there is space to add at least a piece of the first one
+ // - or the previous page break deferred some footnote lines, and
+ // this is the first feasible break; in this case it is allowed
+ // to break and defer, if necessary, old and new footnotes
+ totalIntrusions -= allFootnotes;
+ totalIntrusions += footnoteSplit;
+ insertedFootnotesLength = pageNode.totalFootnotes + footnoteSplit;
+ // listIndex has been set in getFootnoteSplit()
+ // elementIndex has been set in getFootnoteSplit()
+ } else {
+ // there is no space to add even the smallest piece of footnote,
+ // the whole allFootnotes length was added, so this breakpoint
+ // will be discarded
+ }
+ } else {
+ // we are trying to add a piece of content with no footnotes and
+ // it does not fit in the page, because of previous footnote bodies
+ // that cannot be broken:
+ // the whole allFootnotes length was added, so this breakpoint
+ // will be discarded
+ }
+ }
+ } else {
+ // all footnotes have already been placed on previous pages
+ }
+
+ return totalIntrusions;
+ }
+
+ int getDemerits() {
+ int demerits = 0;
+ if (footnotes.listIndex < footnotes.footnotesList.size() - 1) {
+ // add demerits for the deferred footnotes
+ demerits += (footnotes.footnotesList.size() - 1 - footnotes.listIndex)
+ * DEFERRED_FOOTNOTE_DEMERITS;
+ }
+ if (footnotes.elementIndex
+ < getList(footnotes.listIndex).size() - 1) {
+ // add demerits for the footnote split between pages
+ demerits += SPLIT_FOOTNOTE_DEMERITS;
+ }
+ return demerits;
+ }
+
+ boolean deferredFootnotes(int listIndex, int elementIndex, int length) {
+ return ((newFootnotes
+ && firstNewFootnoteIndex != 0
+ && (listIndex < firstNewFootnoteIndex - 1
+ || elementIndex < getList(listIndex).size() - 1))
+ || length < totalLength);
+ }
+
+ int getFootnoteSplit(int availableLength) {
+ return getFootnoteSplit(listIndex,
+ elementIndex,
+ totalLength,
+ availableLength, true);
+ }
+
+ int getFootnoteSplit(KnuthPageNode activeNode, int availableLength,
+ boolean canDeferOldFootnotes) {
+ return getFootnoteSplit(activeNode.footnoteListIndex,
+ activeNode.footnoteElementIndex,
+ activeNode.totalFootnotes,
+ availableLength, canDeferOldFootnotes);
+ }
+
+ int getFootnoteSplit(int prevListIndex, int prevElementIndex, int prevLength,
+ int availableLength, boolean canDeferOldFootnotes) {
+
+ // the split should contain a piece of the last footnote
+ // together with all previous, not yet inserted footnotes;
+ // but if this is not possible, try adding as much content as possible
+ int splitLength = 0;
+ ListIterator noteListIterator;
+ KnuthElement element;
+ boolean somethingAdded = false;
+
+ // prevListIndex and prevElementIndex points to the last footnote element
+ // already placed in a page: advance to the next element
+ int listIndex = prevListIndex;
+ int elementIndex = prevElementIndex;
+ if (elementIndex == getList(listIndex).size() - 1) {
+ listIndex++;
+ elementIndex = 0;
+ } else {
+ elementIndex++;
+ }
+
+ // try adding whole notes
+ if (footnotesList.size() - 1 > listIndex) {
+ // add the previous footnotes: these cannot be broken or deferred
+ if (!canDeferOldFootnotes && newFootnotes && firstNewFootnoteIndex > 0) {
+ splitLength = lengthList.get(firstNewFootnoteIndex - 1) - prevLength;
+ listIndex = firstNewFootnoteIndex;
+ elementIndex = 0;
+ }
+ // try adding the new footnotes
+ while (lengthList.get(listIndex) - prevLength <= availableLength) {
+ splitLength = lengthList.get(listIndex) - prevLength;
+ somethingAdded = true;
+ listIndex++;
+ elementIndex = 0;
+ }
+ // as this method is called only if it is not possible to insert
+ // all footnotes, at this point listIndex and elementIndex points to
+ // an existing element, the next one we will try to insert
+ }
+
+ // try adding a split of the next note
+ noteListIterator = getList(listIndex).listIterator(elementIndex);
+
+ int prevSplitLength = 0;
+ int prevIndex = -1;
+ int index = -1;
+
+ while (!somethingAdded || splitLength <= availableLength) {
+ if (!somethingAdded) {
+ somethingAdded = true;
+ } else {
+ prevSplitLength = splitLength;
+ prevIndex = index;
+ }
+
+ // get a sub-sequence from the note element list
+ boolean boxPreceding = false;
+ while (noteListIterator.hasNext()) {
+ // as this method is called only if it is not possible to insert
+ // all footnotes, and we have already tried (and failed) to insert
+ // this whole footnote, the while loop will never reach the end
+ // of the note sequence
+ element = noteListIterator.next();
+ if (element.isBox()) {
+ // element is a box
+ splitLength += element.getWidth();
+ boxPreceding = true;
+ } else if (element.isGlue()) {
+ // element is a glue
+ if (boxPreceding) {
+ // end of the sub-sequence
+ index = noteListIterator.previousIndex();
+ break;
+ }
+ boxPreceding = false;
+ splitLength += element.getWidth();
+ } else {
+ // element is a penalty
+ if (element.getPenalty() < KnuthElement.INFINITE) {
+ // end of the sub-sequence
+ index = noteListIterator.previousIndex();
+ break;
+ }
+ }
+ }
+ }
+
+ // if prevSplitLength is 0, this means that the available length isn't enough
+ // to insert even the smallest split of the last footnote, so we cannot end a
+ // page here
+ // if prevSplitLength is > 0 we can insert some footnote content in this page
+ // and insert the remaining in the following one
+ //TODO: check this conditional, as the first one is always false...?
+ if (!somethingAdded) {
+ // there was not enough space to add a piece of the first new footnote
+ // this is not a good break
+ prevSplitLength = 0;
+ } else if (prevSplitLength > 0) {
+ // prevIndex is -1 if we have added only some whole footnotes
+ this.listIndex = (prevIndex != -1) ? listIndex : listIndex - 1;
+ this.elementIndex = (prevIndex != -1)
+ ? prevIndex
+ : getList(this.listIndex).size() - 1;
+ }
+ return prevSplitLength;
+ }
+
+ boolean footnotesRemaining() {
+ return insertedFootnotesLength < totalLength;
+ }
+
+ int currentLength() {
+ return lengthList.get(listIndex);
+ }
+
+ List getList(int index) {
+ return footnotesList.get(index);
+ }
+ }
+
+ private Footnotes footnotes;
+
// the method noBreakBetween(int, int) uses these variables
// to store parameters and result of the last call, in order
// to reuse them and take less time
@@ -130,7 +435,8 @@
this.pageProvider = pageProvider;
this.layoutListener = layoutListener;
best = new BestPageRecords();
- this.footnoteSeparatorLength = footnoteSeparatorLength;
+ this.footnotes = new Footnotes();
+ this.footnotes.separatorLength = footnoteSeparatorLength;
this.autoHeight = autoHeight;
this.favorSinglePart = favorSinglePart;
}
@@ -165,7 +471,16 @@
this.footnoteElementIndex = footnoteElementIndex;
}
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(super.toString());
+ sb.deleteCharAt(sb.length() - 1);
+ sb.append(" totalFootnotes:").append(totalFootnotes);
+ sb.append(" footnoteList:").append(footnoteListIndex);
+ sb.append(" footnoteElement:").append(footnoteElementIndex).append('>');
+ return sb.toString();
- }
+ }
+ }
/**
* this class stores information about how the nodes
@@ -183,9 +498,9 @@
super.addRecord(demerits, node, adjust,
availableShrink, availableStretch,
difference, fitness);
- bestFootnotesLength[fitness] = insertedFootnotesLength;
- bestFootnoteListIndex[fitness] = footnoteListIndex;
- bestFootnoteElementIndex[fitness] = footnoteElementIndex;
+ bestFootnotesLength[fitness] = footnotes.insertedFootnotesLength;
+ bestFootnoteListIndex[fitness] = footnotes.listIndex;
+ bestFootnoteElementIndex[fitness] = footnotes.elementIndex;
}
public int getFootnotesLength(int fitness) {
@@ -205,9 +520,7 @@
@Override
protected void initialize() {
super.initialize();
- insertedFootnotesLength = 0;
- footnoteListIndex = 0;
- footnoteElementIndex = -1;
+ footnotes.initialize();
}
/**
@@ -279,7 +592,6 @@
return super.compareNodes(node1, node2);
}
- /** {@inheritDoc} */
@Override
protected KnuthNode createNode(int position, // CSOK: ParameterNumber
int line, int fitness,
@@ -288,12 +600,13 @@
int difference, double totalDemerits, KnuthNode previous) {
return new KnuthPageNode(position, line, fitness,
totalWidth, totalStretch, totalShrink,
- insertedFootnotesLength, footnoteListIndex, footnoteElementIndex,
+ footnotes.insertedFootnotesLength,
+ footnotes.listIndex,
+ footnotes.elementIndex,
adjustRatio, availableShrink, availableStretch,
difference, totalDemerits, previous);
}
- /** {@inheritDoc} */
@Override
protected KnuthNode createNode(int position, int line, int fitness,
int totalWidth, int totalStretch, int totalShrink) {
@@ -317,10 +630,10 @@
super.handleBox(box);
if (box instanceof KnuthBlockBox
&& ((KnuthBlockBox) box).hasAnchors()) {
- handleFootnotes(((KnuthBlockBox) box).getElementLists());
- if (!newFootnotes) {
- newFootnotes = true;
- firstNewFootnoteIndex = footnotesList.size() - 1;
+ footnotes.handleFootnotes(((KnuthBlockBox) box).getElementLists());
+ if (!footnotes.newFootnotes) {
+ footnotes.newFootnotes = true;
+ footnotes.firstNewFootnoteIndex = footnotes.footnotesList.size() - 1;
}
}
}
@@ -348,87 +661,13 @@
}
}
- /**
- * Handles the footnotes cited inside a block-level box. Updates footnotesList and the
- * value of totalFootnotesLength with the lengths of the given footnotes.
- * @param elementLists list of KnuthElement sequences corresponding to the footnotes
- * bodies
- */
- private void handleFootnotes(List> elementLists) {
- // initialization
- if (!footnotesPending) {
- footnotesPending = true;
- footnotesList = new ArrayList>();
- lengthList = new ArrayList();
- totalFootnotesLength = 0;
- }
- if (!newFootnotes) {
- newFootnotes = true;
- firstNewFootnoteIndex = footnotesList.size();
- }
-
- // compute the total length of the footnotes
- for (List noteList : elementLists) {
-
- //Space resolution (Note: this does not respect possible stacking constraints
- //between footnotes!)
- SpaceResolver.resolveElementList(noteList);
-
- int noteLength = 0;
- footnotesList.add(noteList);
- for (KnuthElement element : noteList) {
- if (element.isBox() || element.isGlue()) {
- noteLength += element.getWidth();
- }
- }
- int prevLength = (lengthList == null || lengthList.isEmpty())
- ? 0
- : ListUtil.getLast(lengthList);
- if (lengthList != null) {
- lengthList.add(prevLength + noteLength);
- }
- totalFootnotesLength += noteLength;
- }
- }
-
- /** {@inheritDoc} */
@Override
protected int restartFrom(KnuthNode restartingNode, int currentIndex) {
int returnValue = super.restartFrom(restartingNode, currentIndex);
- newFootnotes = false;
- if (footnotesPending) {
- // remove from footnotesList the note lists that will be met
- // after the restarting point
- for (int j = currentIndex; j >= restartingNode.position; j--) {
- final KnuthElement resetElement = getElement(j);
- if (resetElement instanceof KnuthBlockBox
- && ((KnuthBlockBox) resetElement).hasAnchors()) {
- resetFootnotes(((KnuthBlockBox) resetElement).getElementLists());
- }
- }
- }
+ footnotes.resetFootnotes(restartingNode.position, currentIndex);
return returnValue;
}
- private void resetFootnotes(List> elementLists) {
- for (int i = 0; i < elementLists.size(); i++) {
- ListUtil.removeLast(footnotesList);
- ListUtil.removeLast(lengthList);
-
- // update totalFootnotesLength
- if (!lengthList.isEmpty()) {
- totalFootnotesLength = ListUtil.getLast(lengthList);
- } else {
- totalFootnotesLength = 0;
- }
- }
- // update footnotesPending;
- if (footnotesList.size() == 0) {
- footnotesPending = false;
- }
- }
-
- /** {@inheritDoc} */
@Override
protected void considerLegalBreak(KnuthElement element, int elementIdx) {
if (element.isPenalty()) {
@@ -454,10 +693,9 @@
}
}
super.considerLegalBreak(element, elementIdx);
- newFootnotes = false;
+ footnotes.newFootnotes = false;
}
- /** {@inheritDoc} */
@Override
protected boolean elementCanEndLine(KnuthElement element, int line, int difference) {
if (!(element.isPenalty()) || pageProvider == null) {
@@ -490,74 +728,36 @@
}
}
- /** {@inheritDoc} */
@Override
protected int computeDifference(KnuthNode activeNode, KnuthElement element,
int elementIndex) {
- KnuthPageNode pageNode = (KnuthPageNode) activeNode;
- int actualWidth = totalWidth - pageNode.totalWidth;
- int footnoteSplit;
- boolean canDeferOldFN;
- if (element.isPenalty()) {
- actualWidth += element.getWidth();
+
+ int diff = super.computeDifference(activeNode, element, elementIndex);
+ //getLineWidth() for auto-height parts return 0 so the diff will be negative
+ //...but we don't want to shrink in this case. Stick to optimum.
+ return (autoHeight && (diff < 0) ? 0 : diff);
- }
+ }
- if (footnotesPending) {
- // compute the total length of the footnotes not yet inserted
- int allFootnotes = totalFootnotesLength - pageNode.totalFootnotes;
- if (allFootnotes > 0) {
- // this page contains some footnote citations
- // add the footnote separator width
- actualWidth += footnoteSeparatorLength.getOpt();
- if (actualWidth + allFootnotes <= getLineWidth(activeNode.line)) {
- // there is enough space to insert all footnotes:
- // add the whole allFootnotes length
- actualWidth += allFootnotes;
- insertedFootnotesLength = pageNode.totalFootnotes + allFootnotes;
- footnoteListIndex = footnotesList.size() - 1;
- footnoteElementIndex
- = getFootnoteList(footnoteListIndex).size() - 1;
- } else if (((canDeferOldFN = canDeferOldFootnotes // CSOK: InnerAssignment
- (pageNode, elementIndex))
- || newFootnotes)
- && (footnoteSplit = getFootnoteSplit // CSOK: InnerAssignment
- (pageNode, getLineWidth(activeNode.line) - actualWidth,
- canDeferOldFN)) > 0) {
- // it is allowed to break or even defer footnotes if either:
- // - there are new footnotes in the last piece of content, and
- // there is space to add at least a piece of the first one
- // - or the previous page break deferred some footnote lines, and
- // this is the first feasible break; in this case it is allowed
- // to break and defer, if necessary, old and new footnotes
- actualWidth += footnoteSplit;
- insertedFootnotesLength = pageNode.totalFootnotes + footnoteSplit;
- // footnoteListIndex has been set in getFootnoteSplit()
- // footnoteElementIndex has been set in getFootnoteSplit()
- } else {
- // there is no space to add the smallest piece of footnote,
- // or we are trying to add a piece of content with no footnotes and
- // it does not fit in the page, because of previous footnote bodies
- // that cannot be broken:
- // add the whole allFootnotes length, so this breakpoint will be discarded
- actualWidth += allFootnotes;
- insertedFootnotesLength = pageNode.totalFootnotes + allFootnotes;
- footnoteListIndex = footnotesList.size() - 1;
- footnoteElementIndex
- = getFootnoteList(footnoteListIndex).size() - 1;
+
+ @Override
+ protected boolean hasOutOfLineIntrusions() {
+ return footnotes.footnotesPending;
- }
+ }
+
+ @Override
+ protected int getOutOfLineIntrusions(KnuthNode activeNode, int elementIndex) {
+
+ assert (activeNode != null);
+ KnuthPageNode pageNode = (KnuthPageNode) activeNode;
+ int totalIntrusions = 0;
+
+ if (footnotes.footnotesPending) {
+ totalIntrusions += footnotes.getDifference(pageNode, elementIndex);
- } else {
+ } else {
- // all footnotes have already been placed on previous pages
- }
- } else {
// there are no footnotes
}
- int diff = getLineWidth(activeNode.line) - actualWidth;
- if (autoHeight && diff < 0) {
- //getLineWidth() for auto-height parts return 0 so the diff will be negative
- return 0; //...but we don't want to shrink in this case. Stick to optimum.
- } else {
- return diff;
+
+ return totalIntrusions;
- }
+ }
- }
/**
* Checks whether footnotes from preceding pages may be deferred to the page after
@@ -569,8 +769,8 @@
*/
private boolean canDeferOldFootnotes(KnuthPageNode node, int contentElementIndex) {
return (noBreakBetween(node.position, contentElementIndex)
- && deferredFootnotes(node.footnoteListIndex,
+ && footnotes.deferredFootnotes(node.footnoteListIndex,
- node.footnoteElementIndex, node.totalFootnotes));
+ node.footnoteElementIndex, node.totalFootnotes));
}
/**
@@ -624,166 +824,14 @@
return storedValue;
}
- /**
- * Returns true if their are (pieces of) footnotes to be typeset on the current page.
- * @param listIndex index of the last inserted footnote for the currently considered
- * active node
- * @param elementIndex index of the last element of the last inserted footnote
- * @param length total length of all footnotes inserted so far
- */
- private boolean deferredFootnotes(int listIndex, int elementIndex, int length) {
- return ((newFootnotes
- && firstNewFootnoteIndex != 0
- && (listIndex < firstNewFootnoteIndex - 1
- || elementIndex < getFootnoteList(listIndex).size() - 1))
- || length < totalFootnotesLength);
- }
-
- /**
- * Tries to split the flow of footnotes to put one part on the current page.
- * @param activeNode currently considered previous page break
- * @param availableLength available space for footnotes
- * @param canDeferOldFootnotes
- * @return ...
- */
- private int getFootnoteSplit(KnuthPageNode activeNode, int availableLength,
- boolean canDeferOldFootnotes) {
- return getFootnoteSplit(activeNode.footnoteListIndex,
- activeNode.footnoteElementIndex,
- activeNode.totalFootnotes,
- availableLength, canDeferOldFootnotes);
- }
-
- /**
- * Tries to split the flow of footnotes to put one part on the current page.
- * @param prevListIndex index of the last footnote on the previous page
- * @param prevElementIndex index of the last element of the last footnote
- * @param prevLength total length of footnotes inserted so far
- * @param availableLength available space for footnotes on this page
- * @param canDeferOldFootnotes
- * @return ...
- */
- private int getFootnoteSplit(int prevListIndex, int prevElementIndex, int prevLength,
- int availableLength, boolean canDeferOldFootnotes) {
- if (availableLength <= 0) {
- return 0;
- } else {
- // the split should contain a piece of the last footnote
- // together with all previous, not yet inserted footnotes;
- // but if this is not possible, try adding as much content as possible
- int splitLength = 0;
- ListIterator noteListIterator;
- KnuthElement element;
- boolean somethingAdded = false;
-
- // prevListIndex and prevElementIndex points to the last footnote element
- // already placed in a page: advance to the next element
- int listIndex = prevListIndex;
- int elementIndex = prevElementIndex;
- if (elementIndex == getFootnoteList(listIndex).size() - 1) {
- listIndex++;
- elementIndex = 0;
- } else {
- elementIndex++;
- }
-
- // try adding whole notes
- if (footnotesList.size() - 1 > listIndex) {
- // add the previous footnotes: these cannot be broken or deferred
- if (!canDeferOldFootnotes && newFootnotes && firstNewFootnoteIndex > 0) {
- splitLength = lengthList.get(firstNewFootnoteIndex - 1) - prevLength;
- listIndex = firstNewFootnoteIndex;
- elementIndex = 0;
- }
- // try adding the new footnotes
- while (lengthList.get(listIndex) - prevLength
- <= availableLength) {
- splitLength = lengthList.get(listIndex) - prevLength;
- somethingAdded = true;
- listIndex++;
- elementIndex = 0;
- }
- // as this method is called only if it is not possible to insert
- // all footnotes, at this point listIndex and elementIndex points to
- // an existing element, the next one we will try to insert
- }
-
- // try adding a split of the next note
- noteListIterator = getFootnoteList(listIndex).listIterator(elementIndex);
-
- int prevSplitLength = 0;
- int prevIndex = -1;
- int index = -1;
-
- while (!(somethingAdded && splitLength > availableLength)) {
- if (!somethingAdded) {
- somethingAdded = true;
- } else {
- prevSplitLength = splitLength;
- prevIndex = index;
- }
- // get a sub-sequence from the note element list
- boolean boxPreceding = false;
- while (noteListIterator.hasNext()) {
- // as this method is called only if it is not possible to insert
- // all footnotes, and we have already tried (and failed) to insert
- // this whole footnote, the while loop will never reach the end
- // of the note sequence
- element = noteListIterator.next();
- if (element.isBox()) {
- // element is a box
- splitLength += element.getWidth();
- boxPreceding = true;
- } else if (element.isGlue()) {
- // element is a glue
- if (boxPreceding) {
- // end of the sub-sequence
- index = noteListIterator.previousIndex();
- break;
- }
- boxPreceding = false;
- splitLength += element.getWidth();
- } else {
- // element is a penalty
- if (element.getPenalty() < KnuthElement.INFINITE) {
- // end of the sub-sequence
- index = noteListIterator.previousIndex();
- break;
- }
- }
- }
- }
-
- // if prevSplitLength is 0, this means that the available length isn't enough
- // to insert even the smallest split of the last footnote, so we cannot end a
- // page here
- // if prevSplitLength is > 0 we can insert some footnote content in this page
- // and insert the remaining in the following one
- //TODO: check this conditional, as the first one is always false...?
- if (!somethingAdded) {
- // there was not enough space to add a piece of the first new footnote
- // this is not a good break
- prevSplitLength = 0;
- } else if (prevSplitLength > 0) {
- // prevIndex is -1 if we have added only some whole footnotes
- footnoteListIndex = (prevIndex != -1) ? listIndex : listIndex - 1;
- footnoteElementIndex = (prevIndex != -1)
- ? prevIndex
- : getFootnoteList(footnoteListIndex).size() - 1;
- }
- return prevSplitLength;
- }
- }
-
- /** {@inheritDoc} */
@Override
protected double computeAdjustmentRatio(KnuthNode activeNode, int difference) {
// compute the adjustment ratio
if (difference > 0) {
int maxAdjustment = totalStretch - activeNode.totalStretch;
// add the footnote separator stretch if some footnote content will be added
- if (((KnuthPageNode) activeNode).totalFootnotes < totalFootnotesLength) {
- maxAdjustment += footnoteSeparatorLength.getStretch();
+ if (((KnuthPageNode) activeNode).totalFootnotes < footnotes.totalLength) {
+ maxAdjustment += footnotes.separatorLength.getStretch();
}
if (maxAdjustment > 0) {
return (double) difference / maxAdjustment;
@@ -793,8 +841,8 @@
} else if (difference < 0) {
int maxAdjustment = totalShrink - activeNode.totalShrink;
// add the footnote separator shrink if some footnote content will be added
- if (((KnuthPageNode) activeNode).totalFootnotes < totalFootnotesLength) {
- maxAdjustment += footnoteSeparatorLength.getShrink();
+ if (((KnuthPageNode) activeNode).totalFootnotes < footnotes.totalLength) {
+ maxAdjustment += footnotes.separatorLength.getShrink();
}
if (maxAdjustment > 0) {
return (double) difference / maxAdjustment;
@@ -806,7 +854,6 @@
}
}
- /** {@inheritDoc} */
@Override
protected double computeDemerits(KnuthNode activeNode, KnuthElement element,
int fitnessClass, double r) {
@@ -814,19 +861,16 @@
// compute demerits
double f = Math.abs(r);
f = 1 + 100 * f * f * f;
+
if (element.isPenalty()) {
double penalty = element.getPenalty();
if (penalty >= 0) {
f += penalty;
- demerits = f * f;
} else if (!element.isForcedBreak()) {
- demerits = f * f - penalty * penalty;
- } else {
- demerits = f * f;
+ demerits -= penalty * penalty;
}
- } else {
- demerits = f * f;
}
+ demerits += f * f;
if (element.isPenalty() && ((KnuthPenalty) element).isPenaltyFlagged()
&& getElement(activeNode.position).isPenalty()
@@ -840,34 +884,20 @@
demerits += incompatibleFitnessDemerit;
}
- if (footnotesPending) {
- if (footnoteListIndex < footnotesList.size() - 1) {
- // add demerits for the deferred footnotes
- demerits += (footnotesList.size() - 1 - footnoteListIndex)
- * deferredFootnoteDemerits;
+ if (footnotes.footnotesPending) {
+ demerits += footnotes.getDemerits();
- }
+ }
- if (footnoteListIndex < footnotesList.size()) {
- if (footnoteElementIndex
- < getFootnoteList(footnoteListIndex).size() - 1) {
- // add demerits for the footnote split between pages
- demerits += splitFootnoteDemerits;
- }
- } else {
- //TODO Why can this happen in the first place? Does anybody know? See #44160
- }
- }
demerits += activeNode.totalDemerits;
return demerits;
}
- /** {@inheritDoc} */
@Override
protected void finish() {
for (int i = startLine; i < endLine; i++) {
for (KnuthPageNode node = (KnuthPageNode) getNode(i);
node != null;
node = (KnuthPageNode) node.next) {
- if (node.totalFootnotes < totalFootnotesLength) {
+ if (node.totalFootnotes < footnotes.totalLength) {
// layout remaining footnote bodies
createFootnotePages(node);
}
@@ -877,50 +907,52 @@
private void createFootnotePages(KnuthPageNode lastNode) {
- insertedFootnotesLength = lastNode.totalFootnotes;
- footnoteListIndex = lastNode.footnoteListIndex;
- footnoteElementIndex = lastNode.footnoteElementIndex;
+ footnotes.insertedFootnotesLength = lastNode.totalFootnotes;
+ footnotes.listIndex = lastNode.footnoteListIndex;
+ footnotes.elementIndex = lastNode.footnoteElementIndex;
int availableBPD = getLineWidth(lastNode.line);
- int split = 0;
KnuthPageNode prevNode = lastNode;
// create pages containing the remaining footnote bodies
- while (insertedFootnotesLength < totalFootnotesLength) {
- final int tmpLength = lengthList.get(footnoteListIndex);
+ while (footnotes.footnotesRemaining()) {
+ final int remaining = footnotes.currentLength() - footnotes.insertedFootnotesLength;
+
// try adding some more content
- if ((tmpLength - insertedFootnotesLength) <= availableBPD) {
- // add a whole footnote
- availableBPD -= tmpLength - insertedFootnotesLength;
- insertedFootnotesLength = tmpLength;
- footnoteElementIndex
- = getFootnoteList(footnoteListIndex).size() - 1;
- } else if ((split = getFootnoteSplit // CSOK: InnerAssignment
- (footnoteListIndex, footnoteElementIndex,
- insertedFootnotesLength, availableBPD, true)) > 0) {
+ if (remaining <= availableBPD) {
+ // all fits, so just add everything
+ availableBPD -= remaining;
+ footnotes.insertedFootnotesLength = footnotes.currentLength();
+ footnotes.elementIndex
+ = getFootnoteList(footnotes.listIndex).size() - 1;
+ } else {
+ int split = footnotes.getFootnoteSplit(availableBPD);
+ if (split > 0) {
- // add a piece of a footnote
- availableBPD -= split;
+ // add a piece of a footnote
+ availableBPD -= split;
- insertedFootnotesLength += split;
- // footnoteListIndex has already been set in getFootnoteSplit()
- // footnoteElementIndex has already been set in getFootnoteSplit()
+ footnotes.insertedFootnotesLength += split;
+ // listIndex has already been set in getFootnoteSplit()
+ // elementIndex has already been set in getFootnoteSplit()
- } else {
- // cannot add any content: create a new node and start again
- KnuthPageNode node = (KnuthPageNode)
- createNode(lastNode.position, prevNode.line + 1, 1,
+ } else {
+ // cannot add any content: create a new node and start again
+ KnuthPageNode node = (KnuthPageNode)
+ createNode(lastNode.position, prevNode.line + 1, 1,
- insertedFootnotesLength - prevNode.totalFootnotes,
+ footnotes.insertedFootnotesLength - prevNode.totalFootnotes,
- 0, 0,
- 0, 0, 0,
- 0, 0, prevNode);
- addNode(node.line, node);
- removeNode(prevNode.line, prevNode);
+ 0, 0,
+ 0, 0, 0,
+ 0, 0, prevNode);
+ addNode(node.line, node);
+ removeNode(prevNode.line, prevNode);
- prevNode = node;
- availableBPD = getLineWidth(node.line);
- }
- }
+ prevNode = node;
+ availableBPD = getLineWidth(node.line);
+ }
+ }
+ }
+
// create the last node
KnuthPageNode node = (KnuthPageNode)
createNode(lastNode.position, prevNode.line + 1, 1,
- totalFootnotesLength - prevNode.totalFootnotes, 0, 0,
+ footnotes.totalLength - prevNode.totalFootnotes, 0, 0,
0, 0, 0,
0, 0, prevNode);
addNode(node.line, node);
@@ -960,12 +992,10 @@
pageBreaks.subList(0, pageBreaks.size() - 1).clear();
}
- /** {@inheritDoc} */
@Override
public void updateData1(int total, double demerits) {
}
- /** {@inheritDoc} */
@Override
public void updateData2(KnuthNode bestActiveNode,
KnuthSequence sequence,
@@ -1011,7 +1041,7 @@
// compute the indexes of the first footnote list and the first element in that list
int firstListIndex = ((KnuthPageNode) bestActiveNode.previous).footnoteListIndex;
int firstElementIndex = ((KnuthPageNode) bestActiveNode.previous).footnoteElementIndex;
- if (footnotesList != null
+ if (footnotes.footnotesList != null
&& firstElementIndex == getFootnoteList(firstListIndex).size() - 1) {
// advance to the next list
firstListIndex++;
@@ -1034,7 +1064,6 @@
ratio, difference));
}
- /** {@inheritDoc} */
@Override
protected int filterActiveNodes() {
// leave only the active node with fewest total demerits
@@ -1066,7 +1095,7 @@
* @return the element-list
*/
protected final List getFootnoteList(int index) {
- return footnotesList.get(index);
+ return footnotes.getList(index);
}
/** @return the associated top-level formatting object. */
@@ -1074,7 +1103,6 @@
return topLevelLM.getFObj();
}
- /** {@inheritDoc} */
@Override
protected int getLineWidth(int line) {
int bpd;
@@ -1104,13 +1132,11 @@
}
- /** {@inheritDoc} */
@Override
protected int getIPDdifference() {
return ipdDifference;
}
- /** {@inheritDoc} */
@Override
protected int handleIpdChange() {
log.trace("Best node for ipd change:" + bestNodeForIPDChange);
Index: src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java
===================================================================
--- src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java (revision 1079013)
+++ src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java (revision )
@@ -304,13 +304,19 @@
this.previous = previous;
}
- /** {@inheritDoc} */
+ @Override
public String toString() {
- return "";
+ return new StringBuilder(64).append("<")
+ .append(this.getClass().getSimpleName())
+ .append(" at ").append(position)
+ .append(" ").append(totalWidth)
+ .append("+").append(totalStretch)
+ .append("-").append(totalShrink)
+ .append(" line:").append(line)
+ .append(" prev:").append(previous != null ? previous.position : -1)
+ .append(" dem:").append(totalDemerits)
+ .append(" fitness:").append(FitnessClasses.NAMES[fitness])
+ .append(">").toString();
}
}
@@ -640,7 +646,7 @@
this.par.add(0, KnuthPenalty.DUMMY_ZERO_PENALTY);
}
}
-
+
// content would overflow, insert empty line/page and try again
return createNode(
lastTooLong.previous.position, lastTooLong.previous.line + 1, 1,
@@ -1168,20 +1174,49 @@
*/
protected int computeDifference(KnuthNode activeNode, KnuthElement element,
int elementIndex) {
- // compute the adjustment ratio
- int actualWidth = totalWidth - activeNode.totalWidth;
+ // compute the difference
+ int diff = getLineWidth(activeNode.line);
+ diff -= (totalWidth - activeNode.totalWidth);
if (element.isPenalty()) {
- actualWidth += element.getWidth();
+ diff -= element.getWidth();
}
- return getLineWidth() - actualWidth;
+ if (hasOutOfLineIntrusions()) {
+ diff -= getOutOfLineIntrusions(activeNode, elementIndex);
- }
+ }
+ return diff;
+ }
/**
+ * Hook for subclasses to handle out-of-line content (floats/footnotes).
+ * Default implementation returns {@code false}. Should be overridden
+ * by subclasses if/when appropriate.
+ * @return {@code true} if the currently active node would have out-of-line
+ * content intruding on the line
+ */
+ protected boolean hasOutOfLineIntrusions() {
+ return false;
+ }
+
+ /**
+ * Hook for subclasses to handle out-of-line content (floats/footnotes).
+ * This method is called by {@link #computeDifference(KnuthNode, KnuthElement, int)},
+ * if {@link #hasOutOfLineIntrusions()} returns {@code true}.
+ *
+ * @param activeNode the currently active node
+ * @param elementIndex the element's index
+ * @return the width corresponding to the total out-of-line intrusions
+ * (including static separators)
+ */
+ protected int getOutOfLineIntrusions(KnuthNode activeNode, int elementIndex) {
+ return 0;
+ }
+
+ /**
* Return the adjustment ratio needed to make up for the difference. A ratio of
*
*
0 means that the break has the exact right width
*
>= -1 && < 0 means that the break is wider than the line,
- * but within the minimim values of the glues.
+ * but within the minimum values of the glues.
*
>0 && < 1 means that the break is smaller than the line width,
* but within the maximum values of the glues.
*
> 1 means that the break is too small to make up for the glues.