On Mon, 26 Mar 2007, Luca Furini wrote:
I'm attaching a diff file showing the changes ....
Well, *now* I'm attaching bla bla :-)
Regards
LucaIndex: src/java/org/apache/fop/layoutmgr/breaking/FootnotesRecord.java
===================================================================
--- src/java/org/apache/fop/layoutmgr/breaking/FootnotesRecord.java
(revision 521755)
+++ src/java/org/apache/fop/layoutmgr/breaking/FootnotesRecord.java
(working copy)
@@ -91,6 +91,21 @@
addSeparator();
}
}
+
+ /**
+ *
+ */
+ public void handleDeferredFootnotes(int requestedLastIndex) {
+ boolean separatorAlreadyAdded = (alreadyInserted.getLength() >
0);
+ // check if we must add more footnotes
+ while (lastInsertedIndex < requestedLastIndex) {
+ next();
+ }
+ // if needed, add the separator
+ if (!separatorAlreadyAdded && alreadyInserted.getLength() > 0) {
+ addSeparator();
+ }
+ }
/**
* If the current page is a float-only page, handles the splitting of
the last
Index: src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java
===================================================================
--- src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java (revision
521755)
+++ src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java (working copy)
@@ -545,6 +545,17 @@
log.debug("Could not find a set of breaking points " +
threshold);
return 0;
}
+ // lastDeactivated was a "good" break, while lastTooShort and
lastTooLong
+ // were "bad" breaks since the beginning;
+ // if it is not the node we just restarted from,
lastDeactivated can
+ // replace either lastTooShort or lastTooLong
+ if (lastDeactivated != null && lastDeactivated != lastForced) {
+ if (lastDeactivated.adjustRatio > 0) {
+ lastTooShort = lastDeactivated;
+ } else {
+ lastTooLong = lastDeactivated;
+ }
+ }
if (lastTooShort == null || lastForced.position ==
lastTooShort.position) {
if (isPartOverflowRecoveryActivated()) {
if (this.lastRecovered == null) {
Index: src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java
===================================================================
--- src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java
(revision 521755)
+++ src/java/org/apache/fop/layoutmgr/inline/TextLayoutManager.java
(working copy)
@@ -1285,7 +1285,12 @@
(new KnuthGlue(lineStartBAP, 0, 0,
new LeafPosition(this, -1), false));
} else {
+ // the first penalty is necessary in order to avoid the glue
to be a feasible break
+ // while we are ignoring hyphenated breaks
hyphenElements.add
+ (new KnuthPenalty(0, KnuthElement.INFINITE, false,
+ new LeafPosition(this, -1), false));
+ hyphenElements.add
(new KnuthGlue(0, 3 *
LineLayoutManager.DEFAULT_SPACE_WIDTH, 0,
new LeafPosition(this, -1), false));
hyphenElements.add
Index: src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
===================================================================
--- src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
(revision 521755)
+++ src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
(working copy)
@@ -77,7 +77,7 @@
/**
* Are footnotes-only pages allowed?
*/
- public static final boolean FOOTNOTES_ONLY_PAGES_ALLOWED = true;
+ public static final boolean FOOTNOTES_ONLY_PAGES_ALLOWED = false;
/**
* Additional demerits for an underfull page, which however has an
acceptable fill ratio.
@@ -115,6 +115,14 @@
private BeforeFloatsRecord beforeFloatsRecord;
private FootnotesRecord.FootnotesProgress footnotesProgress;
private BeforeFloatsRecord.BeforeFloatsProgress beforeFloatsProgress;
+ // number of new footnotes met since the last feasible break
+ private int newFootnotesCount = 0;
+ // 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
+ private int storedPrevBreakIndex = -1;
+ private int storedBreakIndex = -1;
+ private boolean storedValue = false;
private ActiveNodeRecorder activeNodeRecorder = new ActiveNodeRecorder();
@@ -682,6 +690,7 @@
if (box instanceof KnuthBlockBox
&& ((KnuthBlockBox) box).hasFootnoteAnchors()) {
footnotesRecord.add(((KnuthBlockBox)
box).getFootnoteElementLists());
+ newFootnotesCount += ((KnuthBlockBox)
box).getFootnoteElementLists().size();
}
if (box instanceof KnuthBlockBox
&& ((KnuthBlockBox) box).hasFloatAnchors()) {
@@ -701,6 +710,8 @@
&& ((KnuthBlockBox)
resetElement).hasFootnoteAnchors()) {
footnotesRecord.reset(((KnuthBlockBox)
resetElement).getFootnoteElementLists());
}
+ // reset newFootnotesCount
+ newFootnotesCount = 0;
if (resetElement instanceof KnuthBlockBox
&& ((KnuthBlockBox) resetElement).hasFloatAnchors()) {
beforeFloatsRecord.reset(((KnuthBlockBox)
resetElement).getFloatElementLists());
@@ -731,6 +742,12 @@
footnotesProgress.setPrevious(node.footnotesProgress);
beforeFloatsProgress.setPrevious(node.beforeFloatsProgress);
footnotesProgress.handleSplit();
+
footnotesProgress.handleDeferredFootnotes(findMininumFootnoteIndex(
+ node.position,
+ elementIdx,
+ newFootnotesCount,
+ node.footnotesProgress.getLastInsertedIndex(),
+ node.footnotesProgress.getNbOfDeferred()));
beforeFloatsProgress.handleSplit();
normalContentProgress.insertedDims.set(totalShrink -
node.totalShrink,
totalWidth - node.totalWidth, totalStretch -
node.totalStretch);
@@ -752,9 +769,81 @@
}
addBreaks(line, elementIdx);
}
+ // reset newFootnotesCount
+ newFootnotesCount = 0;
}
- // TODO vh: refactor
+ /**
+ * Compute the minimum footnote index that is acceptable for a break:
+ * this should prevent footnotes to be unnecessarily deferred.
+ */
+ private int findMininumFootnoteIndex(int prevBreakIndex, int
currBreakIndex, int newFootnotesCount, int lastInsertedIndex, int
deferredFootnotesCount) {
+ int minimumIndex = lastInsertedIndex;
+ if (thereIsABreakBetween(prevBreakIndex, currBreakIndex)) {
+ // we must enforce a more strict constraint, to limit
deferred footnotes:
+ // we want at least a bit of the first new note (if
any),
+ // otherwise a bit of the last one
+ minimumIndex += deferredFootnotesCount -
newFootnotesCount;
+ }
+ /*log.debug("content from " + prevBreakIndex + " to " + currBreakIndex
+ + " lastInsertedIndex=" + lastInsertedIndex
+ + " deferredFCount=" + deferredFootnotesCount
+ + " newFCount=" + newFootnotesCount + "
MininumFootnoteIndex " + minimumIndex);*/
+ return minimumIndex;
+ }
+
+ /**
+ * Returns true if there may be a breakpoint between the two given
elements.
+ * @param prevBreakIndex index of the element from the currently
considered active
+ * node
+ * @param breakIndex index of the currently considered breakpoint
+ * @return true if a element between the two can be a breakpoint
+ */
+ private boolean thereIsABreakBetween(int prevBreakIndex, int breakIndex) {
+ // this method stores the parameters and the return value from
previous calls
+ // in order to avoid scanning the element list unnecessarily:
+ // - if there is no break between element #i and element #j
+ // there will not be a break between #(i+h) and #j too
+ // - if there is a break between element #i and element #j
+ // there will be a break between #(i-h) and #(j+k) too
+ if (storedPrevBreakIndex != -1
+ && ((prevBreakIndex >= storedPrevBreakIndex
+ && breakIndex == storedBreakIndex
+ && !storedValue)
+ || (prevBreakIndex <= storedPrevBreakIndex
+ && breakIndex >= storedBreakIndex
+ && storedValue))) {
+ // use the stored value, do nothing
+ } else {
+ // compute the new value
+ int index;
+ // ignore suppressed elements
+ for (index = prevBreakIndex + 1;
+ index < par.size() && !par.getElement(index).isBox();
+ index++) {
+ //nop
+ }
+ // find the next break
+ for (;
+ index < breakIndex;
+ index++) {
+ if (index == par.size()
+ || par.getElement(index).isGlue() &&
par.getElement(index - 1).isBox()
+ || par.getElement(index).isPenalty()
+ && ((KnuthElement) par.getElement(index)).getP() <
KnuthElement.INFINITE) {
+ // break found (or end of sequence reached)
+ break;
+ }
+ }
+ // update stored parameters and value
+ storedPrevBreakIndex = prevBreakIndex;
+ storedBreakIndex = breakIndex;
+ storedValue = (index < breakIndex);
+ }
+ return storedValue;
+ }
+
+ // TODO vh: refactor
// It may happen that several successive float-only pages are created; in
such cases
// the progress informations must be saved as they'll still be used after
this method
// call
Index:
test/layoutengine/standard-testcases/block-container_content_size_percentage.xml
===================================================================
---
test/layoutengine/standard-testcases/block-container_content_size_percentage.xml
(revision 521755)
+++
test/layoutengine/standard-testcases/block-container_content_size_percentage.xml
(working copy)
@@ -61,9 +61,9 @@
<!-- from the spec: If that dimension is not specified explicitly (i.e.,
it depends on
content's blockprogression-dimension), the value is interpreted as
"auto". -->
<!-- The 10% are ignored in this case. -->
- <eval expected="28800" xpath="//flow/block[2]/@bpd"/> <!-- 2 lines -->
+ <eval expected="43200" xpath="//flow/block[2]/@bpd"/> <!-- 3 lines -->
<eval expected="100000" xpath="//flow/block[2]/@ipd"/>
- <eval expected="28800" xpath="//flow/block[2]/block[1]/block[1]/@bpd"/>
+ <eval expected="43200" xpath="//flow/block[2]/block[1]/block[1]/@bpd"/>
<eval expected="50000" xpath="//flow/block[2]/block[1]/block[1]/@ipd"/>
<!-- absolute -->
@@ -76,9 +76,11 @@
<!-- from the spec: If that dimension is not specified explicitly (i.e.,
it depends on
content's blockprogression-dimension), the value is interpreted as
"auto". -->
<!-- The 10% are ignored in this case. -->
- <eval expected="43200" xpath="//flow/block[4]/@bpd"/> <!-- 3 lines -->
+ <eval expected="57600" xpath="//flow/block[4]/@bpd"/> <!-- 4 lines -->
<eval expected="100000" xpath="//flow/block[4]/@ipd"/>
- <eval expected="43200" xpath="//flow/block[4]/block[1]/block[1]/@bpd"/>
+ <eval expected="28800" xpath="//flow/block[4]/block[1]/block[1]/@bpd"/>
<!-- the first 2 lines ... -->
<eval expected="50000" xpath="//flow/block[4]/block[1]/block[1]/@ipd"/>
+ <eval expected="28800" xpath="//flow/block[4]/block[1]/block[2]/@bpd"/>
<!-- ... and the other 2 lines -->
+ <eval expected="50000" xpath="//flow/block[4]/block[1]/block[2]/@ipd"/>
</checks>
</testcase>