]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
FOP-2106: Footnote put on earlier page than the one that contains the footnote call
authorVincent Hennebert <vhennebert@apache.org>
Tue, 8 Oct 2013 11:29:05 +0000 (11:29 +0000)
committerVincent Hennebert <vhennebert@apache.org>
Tue, 8 Oct 2013 11:29:05 +0000 (11:29 +0000)
Patch by Alexey Neyman, committed with minor modifications

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1530232 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
test/layoutengine/standard-testcases/footnote_jira2106.xml [new file with mode: 0644]

index 59145dd72add3b045715ab95d57f72d79a130eb0..a5084bac52264c15181cbe396438aff931dcd938 100644 (file)
@@ -140,7 +140,10 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
      */
     protected class KnuthPageNode extends KnuthNode {
 
-        /** Additional length due to footnotes. */
+        /** Additional length due to already inserted footnotes. */
+        public int insertedFootnotes;
+
+        /** Total length of the footnotes. */
         public int totalFootnotes;
 
         /** Index of the last inserted footnote. */
@@ -152,7 +155,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
         public KnuthPageNode(int position,
                              int line, int fitness,
                              int totalWidth, int totalStretch, int totalShrink,
-                             int totalFootnotes, int footnoteListIndex, int footnoteElementIndex,
+                             int insertedFootnotes, int totalFootnotes,
+                             int footnoteListIndex, int footnoteElementIndex,
                              double adjustRatio, int availableShrink, int availableStretch,
                              int difference, double totalDemerits, KnuthNode previous) {
             super(position, line, fitness,
@@ -160,6 +164,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
                   adjustRatio, availableShrink, availableStretch,
                   difference, totalDemerits, previous);
             this.totalFootnotes = totalFootnotes;
+            this.insertedFootnotes = insertedFootnotes;
             this.footnoteListIndex = footnoteListIndex;
             this.footnoteElementIndex = footnoteElementIndex;
         }
@@ -172,7 +177,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
      */
     protected class BestPageRecords extends BestRecords {
 
-        private int[] bestFootnotesLength = new int[4];
+        private int[] bestInsertedFootnotesLength = new int[4];
+        private int[] bestTotalFootnotesLength = new int[4];
         private int[] bestFootnoteListIndex = new int[4];
         private int[] bestFootnoteElementIndex = new int[4];
 
@@ -182,13 +188,18 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
             super.addRecord(demerits, node, adjust,
                             availableShrink, availableStretch,
                             difference, fitness);
-            bestFootnotesLength[fitness] = insertedFootnotesLength;
+            bestInsertedFootnotesLength[fitness] = insertedFootnotesLength;
+            bestTotalFootnotesLength[fitness] = totalFootnotesLength;
             bestFootnoteListIndex[fitness] = footnoteListIndex;
             bestFootnoteElementIndex[fitness] = footnoteElementIndex;
         }
 
-        public int getFootnotesLength(int fitness) {
-            return bestFootnotesLength[fitness];
+        public int getInsertedFootnotesLength(int fitness) {
+            return bestInsertedFootnotesLength[fitness];
+        }
+
+        public int getTotalFootnotesLength(int fitness) {
+            return bestTotalFootnotesLength[fitness];
         }
 
         public int getFootnoteListIndex(int fitness) {
@@ -287,7 +298,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
                                    int difference, double totalDemerits, KnuthNode previous) {
         return new KnuthPageNode(position, line, fitness,
                                  totalWidth, totalStretch, totalShrink,
-                                 insertedFootnotesLength, footnoteListIndex, footnoteElementIndex,
+                                 insertedFootnotesLength, totalFootnotesLength,
+                                 footnoteListIndex, footnoteElementIndex,
                                  adjustRatio, availableShrink, availableStretch,
                                  difference, totalDemerits, previous);
     }
@@ -298,7 +310,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
                                    int totalWidth, int totalStretch, int totalShrink) {
         return new KnuthPageNode(position, line, fitness,
                                  totalWidth, totalStretch, totalShrink,
-                                 ((BestPageRecords) best).getFootnotesLength(fitness),
+                                 ((BestPageRecords) best).getInsertedFootnotesLength(fitness),
+                                 ((BestPageRecords) best).getTotalFootnotesLength(fitness),
                                  ((BestPageRecords) best).getFootnoteListIndex(fitness),
                                  ((BestPageRecords) best).getFootnoteElementIndex(fitness),
                                  best.getAdjust(fitness), best.getAvailableShrink(fitness),
@@ -405,6 +418,12 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
                     resetFootnotes(((KnuthBlockBox) resetElement).getElementLists());
                 }
             }
+            assert restartingNode instanceof KnuthPageNode;
+            KnuthPageNode restartingPageNode = (KnuthPageNode) restartingNode;
+            footnoteElementIndex = restartingPageNode.footnoteElementIndex;
+            footnoteListIndex = restartingPageNode.footnoteListIndex;
+            totalFootnotesLength = restartingPageNode.totalFootnotes;
+            insertedFootnotesLength = restartingPageNode.insertedFootnotes;
         }
         return returnValue;
     }
@@ -413,13 +432,6 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
         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) {
@@ -502,7 +514,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
         }
         if (footnotesPending) {
             // compute the total length of the footnotes not yet inserted
-            int allFootnotes = totalFootnotesLength - pageNode.totalFootnotes;
+            int allFootnotes = totalFootnotesLength - pageNode.insertedFootnotes;
             if (allFootnotes > 0) {
                 // this page contains some footnote citations
                 // add the footnote separator width
@@ -511,7 +523,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
                     // there is enough space to insert all footnotes:
                     // add the whole allFootnotes length
                     actualWidth += allFootnotes;
-                    insertedFootnotesLength = pageNode.totalFootnotes + allFootnotes;
+                    insertedFootnotesLength = pageNode.insertedFootnotes + allFootnotes;
                     footnoteListIndex = footnotesList.size() - 1;
                     footnoteElementIndex
                         = getFootnoteList(footnoteListIndex).size() - 1;
@@ -528,7 +540,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
                     //    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;
+                    insertedFootnotesLength = pageNode.insertedFootnotes + footnoteSplit;
                     // footnoteListIndex has been set in getFootnoteSplit()
                     // footnoteElementIndex has been set in getFootnoteSplit()
                 } else {
@@ -538,7 +550,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
                     // that cannot be broken:
                     // add the whole allFootnotes length, so this breakpoint will be discarded
                     actualWidth += allFootnotes;
-                    insertedFootnotesLength = pageNode.totalFootnotes + allFootnotes;
+                    insertedFootnotesLength = pageNode.insertedFootnotes + allFootnotes;
                     footnoteListIndex = footnotesList.size() - 1;
                     footnoteElementIndex
                         = getFootnoteList(footnoteListIndex).size() - 1;
@@ -569,7 +581,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
     private boolean canDeferOldFootnotes(KnuthPageNode node, int contentElementIndex) {
         return (noBreakBetween(node.position, contentElementIndex)
                 && deferredFootnotes(node.footnoteListIndex,
-                        node.footnoteElementIndex, node.totalFootnotes));
+                        node.footnoteElementIndex, node.insertedFootnotes));
     }
 
     /**
@@ -649,7 +661,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
                 boolean canDeferOldFootnotes) {
         return getFootnoteSplit(activeNode.footnoteListIndex,
                                 activeNode.footnoteElementIndex,
-                                activeNode.totalFootnotes,
+                                activeNode.insertedFootnotes,
                                 availableLength, canDeferOldFootnotes);
     }
 
@@ -714,10 +726,8 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
             int prevIndex = -1;
             int index = -1;
 
-            while (!(somethingAdded && splitLength > availableLength)) {
-                if (!somethingAdded) {
-                    somethingAdded = true;
-                } else {
+            while (splitLength <= availableLength) {
+                if (somethingAdded) {
                     prevSplitLength = splitLength;
                     prevIndex = index;
                 }
@@ -733,6 +743,10 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
                         // element is a box
                         splitLength += element.getWidth();
                         boxPreceding = true;
+                        if (splitLength > prevSplitLength) {
+                            // and it is non-empty
+                            somethingAdded = true;
+                        }
                     } else if (element.isGlue()) {
                         // element is a glue
                         if (boxPreceding) {
@@ -749,6 +763,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
                             index = noteListIterator.previousIndex();
                             break;
                         }
+                        boxPreceding = false;
                     }
                 }
             }
@@ -758,7 +773,6 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
             // 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
@@ -781,7 +795,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
         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) {
+            if (((KnuthPageNode) activeNode).insertedFootnotes < totalFootnotesLength) {
                 maxAdjustment += footnoteSeparatorLength.getStretch();
             }
             if (maxAdjustment > 0) {
@@ -792,7 +806,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
         } 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) {
+            if (((KnuthPageNode) activeNode).insertedFootnotes < totalFootnotesLength) {
                 maxAdjustment += footnoteSeparatorLength.getShrink();
             }
             if (maxAdjustment > 0) {
@@ -866,7 +880,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
             for (KnuthPageNode node = (KnuthPageNode) getNode(i);
                  node != null;
                  node = (KnuthPageNode) node.next) {
-                if (node.totalFootnotes < totalFootnotesLength) {
+                if (node.insertedFootnotes < totalFootnotesLength) {
                     // layout remaining footnote bodies
                     createFootnotePages(node);
                 }
@@ -876,7 +890,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
 
     private void createFootnotePages(KnuthPageNode lastNode) {
 
-        insertedFootnotesLength = lastNode.totalFootnotes;
+        insertedFootnotesLength = lastNode.insertedFootnotes;
         footnoteListIndex = lastNode.footnoteListIndex;
         footnoteElementIndex = lastNode.footnoteElementIndex;
         int availableBPD = getLineWidth(lastNode.line);
@@ -902,7 +916,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
                 // cannot add any content: create a new node and start again
                 KnuthPageNode node = (KnuthPageNode)
                                      createNode(lastNode.position, prevNode.line + 1, 1,
-                                                insertedFootnotesLength - prevNode.totalFootnotes,
+                                                insertedFootnotesLength - prevNode.insertedFootnotes,
                                                 0, 0,
                                                 0, 0, 0,
                                                 0, 0, prevNode);
@@ -916,7 +930,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
         // create the last node
         KnuthPageNode node = (KnuthPageNode)
                              createNode(lastNode.position, prevNode.line + 1, 1,
-                                        totalFootnotesLength - prevNode.totalFootnotes, 0, 0,
+                                        totalFootnotesLength - prevNode.insertedFootnotes, 0, 0,
                                         0, 0, 0,
                                         0, 0, prevNode);
         addNode(node.line, node);
diff --git a/test/layoutengine/standard-testcases/footnote_jira2106.xml b/test/layoutengine/standard-testcases/footnote_jira2106.xml
new file mode 100644 (file)
index 0000000..8acc91f
--- /dev/null
@@ -0,0 +1,52 @@
+<?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>
+      Test for FOP-2106: footnote must be positioned on the same page as the inline reference (not 
+      on the page before).
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" font-size="10pt">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="body" page-width="100pt" page-height="35pt">
+          <fo:region-body/>
+        </fo:simple-page-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="body">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block space-before.optimum="1em" space-before.minimum="0.8em" space-before.maximum="1.2em">Page 1 line 1</fo:block>
+          <fo:block space-before.optimum="1em" space-before.minimum="0.8em" space-before.maximum="1.2em">Page 1 line 2</fo:block>
+          <fo:block space-before.optimum="1em" space-before.minimum="0.8em" space-before.maximum="1.2em">Page 2 line 1</fo:block>
+          <fo:block space-before.optimum="1em" space-before.minimum="0.8em" space-before.maximum="1.2em">Page 3 line 
+            1<fo:footnote><fo:inline>*</fo:inline><fo:footnote-body><fo:block 
+                  font-size="6pt">Footnote should be on page 
+                  3</fo:block></fo:footnote-body></fo:footnote></fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <!-- The block with footnote reference is on page 3 -->
+    <eval expected="Page 3 line 1" xpath="//pageViewport[3]//mainReference//text"/>
+    <!-- ... and so is the footnote itself -->
+    <eval expected="Footnote" xpath="//pageViewport[3]//footnote//word[1]"/>
+  </checks>
+</testcase>