View | Details | Raw Unified | Return to bug 51304
Collapse All | Expand All

(-)test/layoutengine/standard-testcases/region-body_column-count_footnote_2.xml (+158 lines)
Line 0 Link Here
1
<?xml version="1.0" encoding="UTF-8"?>
2
<!--
3
  Licensed to the Apache Software Foundation (ASF) under one or more
4
  contributor license agreements.  See the NOTICE file distributed with
5
  this work for additional information regarding copyright ownership.
6
  The ASF licenses this file to You under the Apache License, Version 2.0
7
  (the "License"); you may not use this file except in compliance with
8
  the License.  You may obtain a copy of the License at
9
10
       http://www.apache.org/licenses/LICENSE-2.0
11
12
  Unless required by applicable law or agreed to in writing, software
13
  distributed under the License is distributed on an "AS IS" BASIS,
14
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
  See the License for the specific language governing permissions and
16
  limitations under the License.
17
-->
18
<!-- $Id$ -->
19
<testcase>
20
  <info>
21
    <p>
22
      This test checks multi-column documents. This test: footnotes in multi-column documents (no spans).
23
    </p>
24
  </info>
25
  <fo>
26
    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
27
      <fo:layout-master-set>
28
          <!-- height = 6 x 12pt (content) + 10pt (footnote-separator) -->
29
          <fo:simple-page-master master-name="3-column" page-height="82pt" page-width="8in">
30
              <fo:region-body column-count="3" />
31
          </fo:simple-page-master>
32
       </fo:layout-master-set>
33
       <fo:page-sequence master-reference="3-column" font-size="10pt">
34
          <fo:static-content flow-name="xsl-footnote-separator">
35
              <fo:block-container border-top-color="#000000" border-top-style="solid" border-top-width="1pt" margin-bottom="3pt" margin-top="6pt" width="100%">
36
                  <fo:block/>
37
              </fo:block-container>
38
          </fo:static-content>
39
          <fo:flow flow-name="xsl-region-body">
40
            <fo:block id="block-2" break-before="page">
41
              <fo:block>line 1</fo:block>
42
              <fo:block>line 2</fo:block>
43
              <fo:block>line 3
44
                <fo:footnote><fo:inline font-size="6pt" vertical-align="top">[*]</fo:inline>
45
                  <fo:footnote-body>
46
                    <fo:block>
47
                      <fo:list-block>
48
                        <fo:list-item>
49
                          <fo:list-item-label end-indent="label-end()" start-indent="0">
50
                            <fo:block>
51
                              <fo:inline font-size="6pt" vertical-align="top">[*]</fo:inline>
52
                            </fo:block>
53
                          </fo:list-item-label>
54
                          <fo:list-item-body start-indent="0.4cm">
55
                            <fo:block>footnote footnote footnote footnote footnote footnote footnote footnote</fo:block>
56
                          </fo:list-item-body>
57
                        </fo:list-item>
58
                      </fo:list-block>
59
                    </fo:block>
60
                  </fo:footnote-body>
61
                </fo:footnote>
62
              </fo:block>
63
              <fo:block>line 4</fo:block>
64
              <fo:block>line 5</fo:block>
65
              <fo:block>line 6</fo:block>
66
              <fo:block>line 7</fo:block>
67
              <fo:block>line 8</fo:block>
68
              <fo:block>line 9
69
                <fo:footnote><fo:inline font-size="6pt" vertical-align="top">[*]</fo:inline>
70
                  <fo:footnote-body>
71
                    <fo:block>
72
                      <fo:list-block>
73
                        <fo:list-item>
74
                          <fo:list-item-label end-indent="label-end()" start-indent="0">
75
                            <fo:block>
76
                              <fo:inline font-size="6pt" vertical-align="top">[*]</fo:inline>
77
                            </fo:block>
78
                          </fo:list-item-label>
79
                          <fo:list-item-body start-indent="0.4cm">
80
                            <fo:block>footnote footnote footnote footnote footnote footnote footnote footnote</fo:block>
81
                          </fo:list-item-body>
82
                        </fo:list-item>
83
                      </fo:list-block>
84
                    </fo:block>
85
                  </fo:footnote-body>
86
                </fo:footnote>
87
              </fo:block>
88
              <fo:block>line 10</fo:block>
89
              <fo:block>line 11</fo:block>
90
              <fo:block>line 12</fo:block>
91
              <fo:block>line 13</fo:block>
92
              <fo:block>line 14</fo:block>
93
              <fo:block>line 15</fo:block>
94
              <fo:block>line 16</fo:block>
95
              <fo:block>line 17
96
                <fo:footnote><fo:inline font-size="6pt" vertical-align="top">[*]</fo:inline>
97
                  <fo:footnote-body>
98
                    <fo:block>
99
                      <fo:list-block>
100
                        <fo:list-item>
101
                          <fo:list-item-label end-indent="label-end()" start-indent="0">
102
                            <fo:block>
103
                              <fo:inline font-size="6pt" vertical-align="top">[*]</fo:inline>
104
                            </fo:block>
105
                          </fo:list-item-label>
106
                          <fo:list-item-body start-indent="0.4cm">
107
                            <fo:block>footnote footnote footnote footnote footnote footnote footnote footnote</fo:block>
108
                          </fo:list-item-body>
109
                        </fo:list-item>
110
                      </fo:list-block>
111
                    </fo:block>
112
                  </fo:footnote-body>
113
                </fo:footnote>
114
              </fo:block>
115
              <fo:block>line 18</fo:block>
116
              <fo:block>line 19</fo:block>
117
              <fo:block>line 20</fo:block>
118
              <fo:block>line 21</fo:block>
119
              <fo:block>line 22</fo:block>
120
              <fo:block>line 23</fo:block>
121
              <fo:block>line 24</fo:block>
122
              <fo:block>line 25</fo:block>
123
              <fo:block>line 26</fo:block>
124
              <fo:block>line 27</fo:block>
125
              <fo:block>line 28</fo:block>
126
              <fo:block>line 29</fo:block>
127
              <fo:block>line 30</fo:block>
128
              <fo:block>line 31</fo:block>
129
              <fo:block>line 32</fo:block>
130
              <fo:block>line 33</fo:block>
131
              <fo:block>line 34</fo:block>
132
              <fo:block>line 35</fo:block>
133
            </fo:block>
134
          </fo:flow>
135
       </fo:page-sequence>
136
     </fo:root>
137
   </fo>
138
   <checks>
139
    <!-- Page 1 -->
140
    <!-- 2 lines of footnote content -->
141
    <eval expected="2" xpath="count((/*/*/*/page)[1]//footnote//lineArea[not(.//text/word='[*]')])" />
142
    <!-- 4 lines in first column -->
143
    <!--eval expected="4" xpath="count((/*/*/*/page)[1]//flow[1]//lineArea)"/-->
144
    <!-- 4 lines in second column -->
145
    <eval expected="4" xpath="count((/*/*/*/page)[1]//flow[2]//lineArea)"/>
146
    <!-- 4 lines in third column -->
147
    <eval expected="4" xpath="count((/*/*/*/page)[1]//flow[3]//lineArea)"/>
148
    <!-- Page 2 -->
149
    <!-- 1 line of footnote content -->
150
    <eval expected="1" xpath="count((/*/*/*/page)[2]//footnote//lineArea[not(.//text/word='[*]')])" />
151
    <!-- 5 lines in first column -->
152
    <eval expected="5" xpath="count((/*/*/*/page)[2]//flow[1]//lineArea)"/>
153
    <!-- 5 lines in second column -->
154
    <eval expected="5" xpath="count((/*/*/*/page)[2]//flow[2]//lineArea)"/>
155
    <!-- 5 lines in third column -->
156
    <eval expected="5" xpath="count((/*/*/*/page)[2]//flow[3]//lineArea)"/>
157
   </checks>
158
</testcase>
(-)src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java (-413 / +439 lines)
Lines 20-26 Link Here
20
package org.apache.fop.layoutmgr;
20
package org.apache.fop.layoutmgr;
21
21
22
import java.util.ArrayList;
22
import java.util.ArrayList;
23
import java.util.Iterator;
24
import java.util.LinkedList;
23
import java.util.LinkedList;
25
import java.util.List;
24
import java.util.List;
26
import java.util.ListIterator;
25
import java.util.ListIterator;
Lines 45-84 Link Here
45
    /** List of PageBreakPosition elements. */
44
    /** List of PageBreakPosition elements. */
46
    private LinkedList<PageBreakPosition> pageBreaks = null;
45
    private LinkedList<PageBreakPosition> pageBreaks = null;
47
46
48
    /** Footnotes which are cited between the currently considered active node (previous
47
    private final class Footnotes {
49
     * break) and the current considered break. Its type is
48
50
     * List&lt;List&lt;KnuthElement&gt;&gt;, it contains the sequences of KnuthElement
49
        // demerits for a page break that splits a footnote
51
     * representing the footnotes bodies.
50
        static final int SPLIT_FOOTNOTE_DEMERITS = 5000;
52
     */
51
        // demerits for a page break that defers a whole footnote to the following page
53
    private List<List<KnuthElement>> footnotesList = null;
52
        static final int DEFERRED_FOOTNOTE_DEMERITS = 10000;
54
    /** Cumulated bpd of unhandled footnotes. */
53
55
    private List<Integer> lengthList = null;
54
        // the length of the footnote separator
56
    /** Length of all the footnotes which will be put on the current page. */
55
        private MinOptMax separatorLength = null;
56
57
    private int totalFootnotesLength = 0;
57
        /** Footnotes which are cited between the currently considered active node (previous
58
         * break) and the current considered break. Its type is
59
         * List&lt;List&lt;KnuthElement&gt;&gt;, it contains the sequences of KnuthElement
60
         * representing the footnotes bodies.
61
         */
62
        private List<List<KnuthElement>> footnotesList = null;
63
        /** Cumulated bpd of unhandled footnotes. */
64
        private List<Integer> lengthList = null;
65
        /** Length of all the footnotes which will be put on the current page. */
58
    /**
66
        private int totalLength = 0;
59
     * Length of all the footnotes which have already been inserted, up to the currently
67
60
     * considered element. That is, footnotes from the currently considered page plus
61
     * footnotes from its preceding pages.
62
     */
63
    private int insertedFootnotesLength = 0;
68
        /**
69
         * Length of all the footnotes which have already been inserted, up to the currently
70
         * considered element. That is, footnotes from the currently considered page plus
71
         * footnotes from its preceding pages.
72
         */
73
        private int insertedFootnotesLength;
64
74
65
    /** True if footnote citations have been met since the beginning of the page sequence. */
75
        /** True if footnote citations have been met since the beginning of the page sequence. */
66
    private boolean footnotesPending = false;
76
        private boolean footnotesPending = false;
67
    /** True if the elements met after the previous break point contain footnote citations. */
77
        /** True if the elements met after the previous break point contain footnote citations. */
68
    private boolean newFootnotes = false;
78
        private boolean newFootnotes = false;
69
    /** Index of the first footnote met after the previous break point. */
79
        /** Index of the first footnote met after the previous break point. */
70
    private int firstNewFootnoteIndex = 0;
80
        private int firstNewFootnoteIndex = 0;
71
    /** Index of the last footnote inserted on the current page. */
81
        /** Index of the last footnote inserted on the current page. */
72
    private int footnoteListIndex = 0;
82
        private int listIndex;
73
    /** Index of the last element of the last footnote inserted on the current page. */
83
        /** Index of the last element of the last footnote inserted on the current page. */
74
    private int footnoteElementIndex = -1;
84
        private int elementIndex;
75
85
76
    // demerits for a page break that splits a footnote
86
        private Footnotes() {
77
    private int splitFootnoteDemerits = 5000;
87
        }
78
    // demerits for a page break that defers a whole footnote to the following page
79
    private int deferredFootnoteDemerits = 10000;
80
    private MinOptMax footnoteSeparatorLength = null;
81
88
89
        void initialize() {
90
            footnotes.insertedFootnotesLength = 0;
91
            footnotes.listIndex = 0;
92
            footnotes.elementIndex = -1;
93
        }
94
95
        /**
96
         * Handles the footnotes cited inside a block-level box. Updates footnotesList and the
97
         * value of totalLength with the lengths of the given footnotes.
98
         * @param elementLists list of KnuthElement sequences corresponding to the footnotes
99
         * bodies
100
         */
101
        void handleFootnotes(List<List<KnuthElement>> elementLists) {
102
            // initialization
103
            if (!footnotesPending) {
104
                footnotesPending = true;
105
                footnotesList = new ArrayList<List<KnuthElement>>();
106
                lengthList = new ArrayList<Integer>();
107
                totalLength = 0;
108
            }
109
            if (!newFootnotes) {
110
                newFootnotes = true;
111
                firstNewFootnoteIndex = footnotesList.size();
112
            }
113
114
            // compute the total length of the footnotes
115
            for (List<KnuthElement> noteList : elementLists) {
116
117
                //Space resolution (Note: this does not respect possible stacking constraints
118
                //between footnotes!)
119
                SpaceResolver.resolveElementList(noteList);
120
121
                footnotesList.add(noteList);
122
                int noteLength = ElementListUtils.calcContentLength(noteList);
123
                int prevLength = (lengthList == null || lengthList.isEmpty())
124
                        ? 0
125
                        : ListUtil.getLast(lengthList);
126
                if (lengthList != null) {
127
                    lengthList.add(prevLength + noteLength);
128
                }
129
                totalLength += noteLength;
130
            }
131
        }
132
133
        void resetFootnotes(int fromIndex, int toIndex) {
134
            newFootnotes = false;
135
            if (footnotesPending) {
136
                // remove from footnotesList the note lists that will be met
137
                // after the restarting point
138
                KnuthElement resetElement;
139
                int start = footnotesList.size();
140
                int end = start;
141
                for (int j = toIndex; j >= fromIndex; j--) {
142
                    resetElement = getElement(j);
143
                    if (resetElement instanceof KnuthBlockBox
144
                            && ((KnuthBlockBox) resetElement).hasAnchors()) {
145
                        start -= ((KnuthBlockBox) resetElement).getElementLists().size();
146
                    }
147
                }
148
                footnotesList.subList(start, end).clear();
149
                lengthList.subList(start, end).clear();
150
                // update totalLength
151
                totalLength = (lengthList.isEmpty() ? 0 : ListUtil.getLast(lengthList));
152
                // update footnotesPending;
153
                footnotesPending = !footnotesList.isEmpty();
154
                listIndex = footnotesList.size() - 1;
155
                elementIndex = (listIndex < 0
156
                        ? -1 : getList(listIndex).size() - 1);
157
            }
158
        }
159
160
        int getDifference(KnuthPageNode pageNode, int elementIndex) {
161
162
            int contentWidth = totalWidth - pageNode.totalWidth;
163
164
            int totalIntrusions = 0;
165
166
            // compute the total length of the footnotes not yet inserted
167
            int allFootnotes = totalLength;
168
            KnuthPageNode lastPageNode = pageNode;
169
            while (lastPageNode != null
170
                    && !pageProvider.endPage(lastPageNode.line - 1)) {
171
                lastPageNode = (KnuthPageNode) lastPageNode.previous;
172
            }
173
            allFootnotes -= (lastPageNode == null ? 0 : lastPageNode.totalFootnotes);
174
175
            if (allFootnotes > 0) {
176
                // this page/column contains some footnote citations
177
                // add the footnote separator width
178
                totalIntrusions += separatorLength.getOpt();
179
                totalIntrusions += allFootnotes;
180
                insertedFootnotesLength = totalLength;
181
                listIndex = footnotesList.size() - 1;
182
                this.elementIndex = getList(listIndex).size() - 1;
183
184
                int availableWidth = getLineWidth(pageNode.line);
185
                if ((contentWidth + totalIntrusions) > availableWidth) {
186
                    // see if part of the footnotes can be deferred
187
                    boolean canDeferOldFN = canDeferOldFootnotes(pageNode, elementIndex);
188
                    if (canDeferOldFN || newFootnotes) {
189
                        availableWidth -= (contentWidth + separatorLength.getOpt());
190
                        int footnoteSplit = (availableWidth <= 0 ? 0
191
                                : getFootnoteSplit(pageNode, availableWidth, canDeferOldFN));
192
                        if (footnoteSplit > 0) {
193
                            // the total of content + separator + all footnotes does not fit, but
194
                            // it is allowed to break or even defer footnotes if either:
195
                            //  - there are new footnotes in the last piece of content, and
196
                            //    there is space to add at least a piece of the first one
197
                            //  - or the previous page break deferred some footnote lines, and
198
                            //    this is the first feasible break; in this case it is allowed
199
                            //    to break and defer, if necessary, old and new footnotes
200
                            totalIntrusions -= allFootnotes;
201
                            totalIntrusions += footnoteSplit;
202
                            insertedFootnotesLength = pageNode.totalFootnotes + footnoteSplit;
203
                            // listIndex has been set in getFootnoteSplit()
204
                            // elementIndex has been set in getFootnoteSplit()
205
                        } else {
206
                            // there is no space to add even the smallest piece of footnote,
207
                            // the whole allFootnotes length was added, so this breakpoint
208
                            // will be discarded
209
                        }
210
                    } else {
211
                        // we are trying to add a piece of content with no footnotes and
212
                        // it does not fit in the page, because of previous footnote bodies
213
                        // that cannot be broken:
214
                        // the whole allFootnotes length was added, so this breakpoint
215
                        // will be discarded
216
                    }
217
                }
218
            } else {
219
                // all footnotes have already been placed on previous pages
220
            }
221
222
            return totalIntrusions;
223
        }
224
225
        int getDemerits() {
226
            int demerits = 0;
227
            if (footnotes.listIndex < footnotes.footnotesList.size() - 1) {
228
                // add demerits for the deferred footnotes
229
                demerits += (footnotes.footnotesList.size() - 1 - footnotes.listIndex)
230
                                * DEFERRED_FOOTNOTE_DEMERITS;
231
            }
232
            if (footnotes.elementIndex
233
                    < getList(footnotes.listIndex).size() - 1) {
234
                // add demerits for the footnote split between pages
235
                demerits += SPLIT_FOOTNOTE_DEMERITS;
236
            }
237
            return demerits;
238
        }
239
240
        boolean deferredFootnotes(int listIndex, int elementIndex, int length) {
241
            return ((newFootnotes
242
                     && firstNewFootnoteIndex != 0
243
                     && (listIndex < firstNewFootnoteIndex - 1
244
                         || elementIndex < getList(listIndex).size() - 1))
245
                    || length < totalLength);
246
        }
247
248
        int getFootnoteSplit(int availableLength) {
249
            return getFootnoteSplit(listIndex,
250
                    elementIndex,
251
                    totalLength,
252
                                    availableLength, true);
253
        }
254
255
        int getFootnoteSplit(KnuthPageNode activeNode, int availableLength,
256
                    boolean canDeferOldFootnotes) {
257
            return getFootnoteSplit(activeNode.footnoteListIndex,
258
                                    activeNode.footnoteElementIndex,
259
                                    activeNode.totalFootnotes,
260
                                    availableLength, canDeferOldFootnotes);
261
        }
262
263
        int getFootnoteSplit(int prevListIndex, int prevElementIndex, int prevLength,
264
                                     int availableLength, boolean canDeferOldFootnotes) {
265
266
            // the split should contain a piece of the last footnote
267
            // together with all previous, not yet inserted footnotes;
268
            // but if this is not possible, try adding as much content as possible
269
            int splitLength = 0;
270
            ListIterator<KnuthElement> noteListIterator;
271
            KnuthElement element;
272
            boolean somethingAdded = false;
273
274
            // prevListIndex and prevElementIndex points to the last footnote element
275
            // already placed in a page: advance to the next element
276
            int listIndex = prevListIndex;
277
            int elementIndex = prevElementIndex;
278
            if (elementIndex == getList(listIndex).size() - 1) {
279
                listIndex++;
280
                elementIndex = 0;
281
            } else {
282
                elementIndex++;
283
            }
284
285
            // try adding whole notes
286
            if (footnotesList.size() - 1 > listIndex) {
287
                // add the previous footnotes: these cannot be broken or deferred
288
                if (!canDeferOldFootnotes && newFootnotes && firstNewFootnoteIndex > 0) {
289
                    splitLength = lengthList.get(firstNewFootnoteIndex - 1) - prevLength;
290
                    listIndex = firstNewFootnoteIndex;
291
                    elementIndex = 0;
292
                }
293
                // try adding the new footnotes
294
                while (lengthList.get(listIndex) - prevLength <= availableLength) {
295
                    splitLength = lengthList.get(listIndex) - prevLength;
296
                    somethingAdded = true;
297
                    listIndex++;
298
                    elementIndex = 0;
299
                }
300
                // as this method is called only if it is not possible to insert
301
                // all footnotes, at this point listIndex and elementIndex points to
302
                // an existing element, the next one we will try to insert
303
            }
304
305
            // try adding a split of the next note
306
            noteListIterator = getList(listIndex).listIterator(elementIndex);
307
308
            int prevSplitLength = 0;
309
            int prevIndex = -1;
310
            int index = -1;
311
312
            while (!somethingAdded || splitLength <= availableLength) {
313
                if (!somethingAdded) {
314
                    somethingAdded = true;
315
                } else {
316
                    prevSplitLength = splitLength;
317
                    prevIndex = index;
318
                }
319
320
                // get a sub-sequence from the note element list
321
                boolean boxPreceding = false;
322
                while (noteListIterator.hasNext()) {
323
                    // as this method is called only if it is not possible to insert
324
                    // all footnotes, and we have already tried (and failed) to insert
325
                    // this whole footnote, the while loop will never reach the end
326
                    // of the note sequence
327
                    element = noteListIterator.next();
328
                    if (element.isBox()) {
329
                        // element is a box
330
                        splitLength += element.getWidth();
331
                        boxPreceding = true;
332
                    } else if (element.isGlue()) {
333
                        // element is a glue
334
                        if (boxPreceding) {
335
                            // end of the sub-sequence
336
                            index = noteListIterator.previousIndex();
337
                            break;
338
                        }
339
                        boxPreceding = false;
340
                        splitLength += element.getWidth();
341
                    } else {
342
                        // element is a penalty
343
                        if (element.getPenalty() < KnuthElement.INFINITE) {
344
                            // end of the sub-sequence
345
                            index = noteListIterator.previousIndex();
346
                            break;
347
                        }
348
                    }
349
                }
350
            }
351
352
            // if prevSplitLength is 0, this means that the available length isn't enough
353
            // to insert even the smallest split of the last footnote, so we cannot end a
354
            // page here
355
            // if prevSplitLength is > 0 we can insert some footnote content in this page
356
            // and insert the remaining in the following one
357
            //TODO: check this conditional, as the first one is always false...?
358
            if (!somethingAdded) {
359
                // there was not enough space to add a piece of the first new footnote
360
                // this is not a good break
361
                prevSplitLength = 0;
362
            } else if (prevSplitLength > 0) {
363
                // prevIndex is -1 if we have added only some whole footnotes
364
                this.listIndex = (prevIndex != -1) ? listIndex : listIndex - 1;
365
                this.elementIndex = (prevIndex != -1)
366
                    ? prevIndex
367
                    : getList(this.listIndex).size() - 1;
368
            }
369
            return prevSplitLength;
370
        }
371
372
        boolean footnotesRemaining() {
373
            return insertedFootnotesLength < totalLength;
374
        }
375
376
        int currentLength() {
377
            return lengthList.get(listIndex);
378
        }
379
380
        List<KnuthElement> getList(int index) {
381
            return footnotesList.get(index);
382
        }
383
    }
384
385
    private Footnotes footnotes;
386
82
    // the method noBreakBetween(int, int) uses these variables
387
    // the method noBreakBetween(int, int) uses these variables
83
    // to store parameters and result of the last call, in order
388
    // to store parameters and result of the last call, in order
84
    // to reuse them and take less time
389
    // to reuse them and take less time
Lines 130-136 Link Here
130
        this.pageProvider = pageProvider;
435
        this.pageProvider = pageProvider;
131
        this.layoutListener = layoutListener;
436
        this.layoutListener = layoutListener;
132
        best = new BestPageRecords();
437
        best = new BestPageRecords();
133
        this.footnoteSeparatorLength = footnoteSeparatorLength;
438
        this.footnotes = new Footnotes();
439
        this.footnotes.separatorLength = footnoteSeparatorLength;
134
        this.autoHeight = autoHeight;
440
        this.autoHeight = autoHeight;
135
        this.favorSinglePart = favorSinglePart;
441
        this.favorSinglePart = favorSinglePart;
136
    }
442
    }
Lines 165-171 Link Here
165
            this.footnoteElementIndex = footnoteElementIndex;
471
            this.footnoteElementIndex = footnoteElementIndex;
166
        }
472
        }
167
473
168
    }
474
        @Override
475
        public String toString() {
476
            StringBuilder sb = new StringBuilder(super.toString());
477
            sb.deleteCharAt(sb.length() - 1);
478
            sb.append(" totalFootnotes:").append(totalFootnotes);
479
            sb.append(" footnoteList:").append(footnoteListIndex);
480
            sb.append(" footnoteElement:").append(footnoteElementIndex).append('>');
481
            return sb.toString();
482
        }
483
    }
169
484
170
    /**
485
    /**
171
     * this class stores information about how the nodes
486
     * this class stores information about how the nodes
Lines 183-191 Link Here
183
            super.addRecord(demerits, node, adjust,
498
            super.addRecord(demerits, node, adjust,
184
                            availableShrink, availableStretch,
499
                            availableShrink, availableStretch,
185
                            difference, fitness);
500
                            difference, fitness);
186
            bestFootnotesLength[fitness] = insertedFootnotesLength;
501
            bestFootnotesLength[fitness] = footnotes.insertedFootnotesLength;
187
            bestFootnoteListIndex[fitness] = footnoteListIndex;
502
            bestFootnoteListIndex[fitness] = footnotes.listIndex;
188
            bestFootnoteElementIndex[fitness] = footnoteElementIndex;
503
            bestFootnoteElementIndex[fitness] = footnotes.elementIndex;
189
        }
504
        }
190
505
191
        public int getFootnotesLength(int fitness) {
506
        public int getFootnotesLength(int fitness) {
Lines 205-213 Link Here
205
    @Override
520
    @Override
206
    protected void initialize() {
521
    protected void initialize() {
207
        super.initialize();
522
        super.initialize();
208
        insertedFootnotesLength = 0;
523
        footnotes.initialize();
209
        footnoteListIndex = 0;
210
        footnoteElementIndex = -1;
211
    }
524
    }
212
525
213
    /**
526
    /**
Lines 279-285 Link Here
279
        return super.compareNodes(node1, node2);
592
        return super.compareNodes(node1, node2);
280
    }
593
    }
281
594
282
    /** {@inheritDoc} */
283
    @Override
595
    @Override
284
    protected KnuthNode createNode(int position,                // CSOK: ParameterNumber
596
    protected KnuthNode createNode(int position,                // CSOK: ParameterNumber
285
                                   int line, int fitness,
597
                                   int line, int fitness,
Lines 288-299 Link Here
288
                                   int difference, double totalDemerits, KnuthNode previous) {
600
                                   int difference, double totalDemerits, KnuthNode previous) {
289
        return new KnuthPageNode(position, line, fitness,
601
        return new KnuthPageNode(position, line, fitness,
290
                                 totalWidth, totalStretch, totalShrink,
602
                                 totalWidth, totalStretch, totalShrink,
291
                                 insertedFootnotesLength, footnoteListIndex, footnoteElementIndex,
603
                                 footnotes.insertedFootnotesLength,
604
                                 footnotes.listIndex,
605
                                 footnotes.elementIndex,
292
                                 adjustRatio, availableShrink, availableStretch,
606
                                 adjustRatio, availableShrink, availableStretch,
293
                                 difference, totalDemerits, previous);
607
                                 difference, totalDemerits, previous);
294
    }
608
    }
295
609
296
    /** {@inheritDoc} */
297
    @Override
610
    @Override
298
    protected KnuthNode createNode(int position, int line, int fitness,
611
    protected KnuthNode createNode(int position, int line, int fitness,
299
                                   int totalWidth, int totalStretch, int totalShrink) {
612
                                   int totalWidth, int totalStretch, int totalShrink) {
Lines 317-326 Link Here
317
        super.handleBox(box);
630
        super.handleBox(box);
318
        if (box instanceof KnuthBlockBox
631
        if (box instanceof KnuthBlockBox
319
            && ((KnuthBlockBox) box).hasAnchors()) {
632
            && ((KnuthBlockBox) box).hasAnchors()) {
320
            handleFootnotes(((KnuthBlockBox) box).getElementLists());
633
            footnotes.handleFootnotes(((KnuthBlockBox) box).getElementLists());
321
            if (!newFootnotes) {
634
            if (!footnotes.newFootnotes) {
322
                newFootnotes = true;
635
                footnotes.newFootnotes = true;
323
                firstNewFootnoteIndex = footnotesList.size() - 1;
636
                footnotes.firstNewFootnoteIndex = footnotes.footnotesList.size() - 1;
324
            }
637
            }
325
        }
638
        }
326
    }
639
    }
Lines 348-434 Link Here
348
        }
661
        }
349
    }
662
    }
350
663
351
    /**
352
     * Handles the footnotes cited inside a block-level box. Updates footnotesList and the
353
     * value of totalFootnotesLength with the lengths of the given footnotes.
354
     * @param elementLists list of KnuthElement sequences corresponding to the footnotes
355
     * bodies
356
     */
357
    private void handleFootnotes(List<List<KnuthElement>> elementLists) {
358
        // initialization
359
        if (!footnotesPending) {
360
            footnotesPending = true;
361
            footnotesList = new ArrayList<List<KnuthElement>>();
362
            lengthList = new ArrayList<Integer>();
363
            totalFootnotesLength = 0;
364
        }
365
        if (!newFootnotes) {
366
            newFootnotes = true;
367
            firstNewFootnoteIndex = footnotesList.size();
368
        }
369
370
        // compute the total length of the footnotes
371
        for (List<KnuthElement> noteList : elementLists) {
372
373
            //Space resolution (Note: this does not respect possible stacking constraints
374
            //between footnotes!)
375
            SpaceResolver.resolveElementList(noteList);
376
377
            int noteLength = 0;
378
            footnotesList.add(noteList);
379
            for (KnuthElement element : noteList) {
380
                if (element.isBox() || element.isGlue()) {
381
                    noteLength += element.getWidth();
382
                }
383
            }
384
            int prevLength = (lengthList == null || lengthList.isEmpty())
385
                    ? 0
386
                    : ListUtil.getLast(lengthList);
387
            if (lengthList != null) {
388
                lengthList.add(prevLength + noteLength);
389
            }
390
            totalFootnotesLength += noteLength;
391
        }
392
    }
393
394
    /** {@inheritDoc} */
395
    @Override
664
    @Override
396
    protected int restartFrom(KnuthNode restartingNode, int currentIndex) {
665
    protected int restartFrom(KnuthNode restartingNode, int currentIndex) {
397
        int returnValue = super.restartFrom(restartingNode, currentIndex);
666
        int returnValue = super.restartFrom(restartingNode, currentIndex);
398
        newFootnotes = false;
667
        footnotes.resetFootnotes(restartingNode.position, currentIndex);
399
        if (footnotesPending) {
400
            // remove from footnotesList the note lists that will be met
401
            // after the restarting point
402
            for (int j = currentIndex; j >= restartingNode.position; j--) {
403
                final KnuthElement resetElement = getElement(j);
404
                if (resetElement instanceof KnuthBlockBox
405
                        && ((KnuthBlockBox) resetElement).hasAnchors()) {
406
                    resetFootnotes(((KnuthBlockBox) resetElement).getElementLists());
407
                }
408
            }
409
        }
410
        return returnValue;
668
        return returnValue;
411
    }
669
    }
412
670
413
    private void resetFootnotes(List<List<KnuthElement>> elementLists) {
414
        for (int i = 0; i < elementLists.size(); i++) {
415
            ListUtil.removeLast(footnotesList);
416
            ListUtil.removeLast(lengthList);
417
418
            // update totalFootnotesLength
419
            if (!lengthList.isEmpty()) {
420
                totalFootnotesLength = ListUtil.getLast(lengthList);
421
            } else {
422
                totalFootnotesLength = 0;
423
            }
424
        }
425
        // update footnotesPending;
426
        if (footnotesList.size() == 0) {
427
            footnotesPending = false;
428
        }
429
    }
430
431
    /** {@inheritDoc} */
432
    @Override
671
    @Override
433
    protected void considerLegalBreak(KnuthElement element, int elementIdx) {
672
    protected void considerLegalBreak(KnuthElement element, int elementIdx) {
434
        if (element.isPenalty()) {
673
        if (element.isPenalty()) {
Lines 454-463 Link Here
454
            }
693
            }
455
        }
694
        }
456
        super.considerLegalBreak(element, elementIdx);
695
        super.considerLegalBreak(element, elementIdx);
457
        newFootnotes = false;
696
        footnotes.newFootnotes = false;
458
    }
697
    }
459
698
460
    /** {@inheritDoc} */
461
    @Override
699
    @Override
462
    protected boolean elementCanEndLine(KnuthElement element, int line, int difference) {
700
    protected boolean elementCanEndLine(KnuthElement element, int line, int difference) {
463
        if (!(element.isPenalty()) || pageProvider == null) {
701
        if (!(element.isPenalty()) || pageProvider == null) {
Lines 490-563 Link Here
490
        }
728
        }
491
    }
729
    }
492
730
493
    /** {@inheritDoc} */
494
    @Override
731
    @Override
495
    protected int computeDifference(KnuthNode activeNode, KnuthElement element,
732
    protected int computeDifference(KnuthNode activeNode, KnuthElement element,
496
                                    int elementIndex) {
733
                                    int elementIndex) {
497
        KnuthPageNode pageNode = (KnuthPageNode) activeNode;
734
498
        int actualWidth = totalWidth - pageNode.totalWidth;
735
        int diff = super.computeDifference(activeNode, element, elementIndex);
499
        int footnoteSplit;
736
        //getLineWidth() for auto-height parts return 0 so the diff will be negative
500
        boolean canDeferOldFN;
737
        //...but we don't want to shrink in this case. Stick to optimum.
501
        if (element.isPenalty()) {
738
        return (autoHeight && (diff < 0) ? 0 : diff);
502
            actualWidth += element.getWidth();
503
        }
739
    }
504
        if (footnotesPending) {
740
505
            // compute the total length of the footnotes not yet inserted
741
    @Override
506
            int allFootnotes = totalFootnotesLength - pageNode.totalFootnotes;
742
    protected boolean hasOutOfLineIntrusions() {
507
            if (allFootnotes > 0) {
743
        return footnotes.footnotesPending;
508
                // this page contains some footnote citations
509
                // add the footnote separator width
510
                actualWidth += footnoteSeparatorLength.getOpt();
511
                if (actualWidth + allFootnotes <= getLineWidth(activeNode.line)) {
512
                    // there is enough space to insert all footnotes:
513
                    // add the whole allFootnotes length
514
                    actualWidth += allFootnotes;
515
                    insertedFootnotesLength = pageNode.totalFootnotes + allFootnotes;
516
                    footnoteListIndex = footnotesList.size() - 1;
517
                    footnoteElementIndex
518
                        = getFootnoteList(footnoteListIndex).size() - 1;
519
                } else if (((canDeferOldFN = canDeferOldFootnotes // CSOK: InnerAssignment
520
                             (pageNode, elementIndex))
521
                            || newFootnotes)
522
                           && (footnoteSplit = getFootnoteSplit // CSOK: InnerAssignment
523
                               (pageNode, getLineWidth(activeNode.line) - actualWidth,
524
                                canDeferOldFN)) > 0) {
525
                    // it is allowed to break or even defer footnotes if either:
526
                    //  - there are new footnotes in the last piece of content, and
527
                    //    there is space to add at least a piece of the first one
528
                    //  - or the previous page break deferred some footnote lines, and
529
                    //    this is the first feasible break; in this case it is allowed
530
                    //    to break and defer, if necessary, old and new footnotes
531
                    actualWidth += footnoteSplit;
532
                    insertedFootnotesLength = pageNode.totalFootnotes + footnoteSplit;
533
                    // footnoteListIndex has been set in getFootnoteSplit()
534
                    // footnoteElementIndex has been set in getFootnoteSplit()
535
                } else {
536
                    // there is no space to add the smallest piece of footnote,
537
                    // or we are trying to add a piece of content with no footnotes and
538
                    // it does not fit in the page, because of previous footnote bodies
539
                    // that cannot be broken:
540
                    // add the whole allFootnotes length, so this breakpoint will be discarded
541
                    actualWidth += allFootnotes;
542
                    insertedFootnotesLength = pageNode.totalFootnotes + allFootnotes;
543
                    footnoteListIndex = footnotesList.size() - 1;
544
                    footnoteElementIndex
545
                        = getFootnoteList(footnoteListIndex).size() - 1;
546
                }
744
    }
745
746
    @Override
747
    protected int getOutOfLineIntrusions(KnuthNode activeNode, int elementIndex) {
748
749
        assert (activeNode != null);
750
        KnuthPageNode pageNode = (KnuthPageNode) activeNode;
751
        int totalIntrusions = 0;
752
753
        if (footnotes.footnotesPending) {
754
            totalIntrusions += footnotes.getDifference(pageNode, elementIndex);
547
            } else {
755
        } else {
548
                // all footnotes have already been placed on previous pages
549
            }
550
        } else {
551
            // there are no footnotes
756
            // there are no footnotes
552
        }
757
        }
553
        int diff = getLineWidth(activeNode.line) - actualWidth;
758
554
        if (autoHeight && diff < 0) {
759
        return totalIntrusions;
555
            //getLineWidth() for auto-height parts return 0 so the diff will be negative
556
            return 0; //...but we don't want to shrink in this case. Stick to optimum.
557
        } else {
558
            return diff;
559
        }
760
    }
560
    }
561
761
562
    /**
762
    /**
563
     * Checks whether footnotes from preceding pages may be deferred to the page after
763
     * Checks whether footnotes from preceding pages may be deferred to the page after
Lines 569-576 Link Here
569
     */
769
     */
570
    private boolean canDeferOldFootnotes(KnuthPageNode node, int contentElementIndex) {
770
    private boolean canDeferOldFootnotes(KnuthPageNode node, int contentElementIndex) {
571
        return (noBreakBetween(node.position, contentElementIndex)
771
        return (noBreakBetween(node.position, contentElementIndex)
572
                && deferredFootnotes(node.footnoteListIndex,
772
                && footnotes.deferredFootnotes(node.footnoteListIndex,
573
                        node.footnoteElementIndex, node.totalFootnotes));
773
                node.footnoteElementIndex, node.totalFootnotes));
574
    }
774
    }
575
775
576
    /**
776
    /**
Lines 624-789 Link Here
624
        return storedValue;
824
        return storedValue;
625
    }
825
    }
626
826
627
    /**
628
     * Returns true if their are (pieces of) footnotes to be typeset on the current page.
629
     * @param listIndex index of the last inserted footnote for the currently considered
630
     * active node
631
     * @param elementIndex index of the last element of the last inserted footnote
632
     * @param length total length of all footnotes inserted so far
633
     */
634
    private boolean deferredFootnotes(int listIndex, int elementIndex, int length) {
635
        return ((newFootnotes
636
                 && firstNewFootnoteIndex != 0
637
                 && (listIndex < firstNewFootnoteIndex - 1
638
                     || elementIndex < getFootnoteList(listIndex).size() - 1))
639
                || length < totalFootnotesLength);
640
    }
641
642
    /**
643
     * Tries to split the flow of footnotes to put one part on the current page.
644
     * @param activeNode currently considered previous page break
645
     * @param availableLength available space for footnotes
646
     * @param canDeferOldFootnotes
647
     * @return ...
648
     */
649
    private int getFootnoteSplit(KnuthPageNode activeNode, int availableLength,
650
                boolean canDeferOldFootnotes) {
651
        return getFootnoteSplit(activeNode.footnoteListIndex,
652
                                activeNode.footnoteElementIndex,
653
                                activeNode.totalFootnotes,
654
                                availableLength, canDeferOldFootnotes);
655
    }
656
657
    /**
658
     * Tries to split the flow of footnotes to put one part on the current page.
659
     * @param prevListIndex index of the last footnote on the previous page
660
     * @param prevElementIndex index of the last element of the last footnote
661
     * @param prevLength total length of footnotes inserted so far
662
     * @param availableLength available space for footnotes on this page
663
     * @param canDeferOldFootnotes
664
     * @return ...
665
     */
666
    private int getFootnoteSplit(int prevListIndex, int prevElementIndex, int prevLength,
667
                                 int availableLength, boolean canDeferOldFootnotes) {
668
        if (availableLength <= 0) {
669
            return 0;
670
        } else {
671
            // the split should contain a piece of the last footnote
672
            // together with all previous, not yet inserted footnotes;
673
            // but if this is not possible, try adding as much content as possible
674
            int splitLength = 0;
675
            ListIterator<KnuthElement> noteListIterator;
676
            KnuthElement element;
677
            boolean somethingAdded = false;
678
679
            // prevListIndex and prevElementIndex points to the last footnote element
680
            // already placed in a page: advance to the next element
681
            int listIndex = prevListIndex;
682
            int elementIndex = prevElementIndex;
683
            if (elementIndex == getFootnoteList(listIndex).size() - 1) {
684
                listIndex++;
685
                elementIndex = 0;
686
            } else {
687
                elementIndex++;
688
            }
689
690
            // try adding whole notes
691
            if (footnotesList.size() - 1 > listIndex) {
692
                // add the previous footnotes: these cannot be broken or deferred
693
                if (!canDeferOldFootnotes && newFootnotes && firstNewFootnoteIndex > 0) {
694
                    splitLength = lengthList.get(firstNewFootnoteIndex - 1) - prevLength;
695
                    listIndex = firstNewFootnoteIndex;
696
                    elementIndex = 0;
697
                }
698
                // try adding the new footnotes
699
                while (lengthList.get(listIndex) - prevLength
700
                       <= availableLength) {
701
                    splitLength = lengthList.get(listIndex) - prevLength;
702
                    somethingAdded = true;
703
                    listIndex++;
704
                    elementIndex = 0;
705
                }
706
                // as this method is called only if it is not possible to insert
707
                // all footnotes, at this point listIndex and elementIndex points to
708
                // an existing element, the next one we will try to insert
709
            }
710
711
            // try adding a split of the next note
712
            noteListIterator = getFootnoteList(listIndex).listIterator(elementIndex);
713
714
            int prevSplitLength = 0;
715
            int prevIndex = -1;
716
            int index = -1;
717
718
            while (!(somethingAdded && splitLength > availableLength)) {
719
                if (!somethingAdded) {
720
                    somethingAdded = true;
721
                } else {
722
                    prevSplitLength = splitLength;
723
                    prevIndex = index;
724
                }
725
                // get a sub-sequence from the note element list
726
                boolean boxPreceding = false;
727
                while (noteListIterator.hasNext()) {
728
                    // as this method is called only if it is not possible to insert
729
                    // all footnotes, and we have already tried (and failed) to insert
730
                    // this whole footnote, the while loop will never reach the end
731
                    // of the note sequence
732
                    element = noteListIterator.next();
733
                    if (element.isBox()) {
734
                        // element is a box
735
                        splitLength += element.getWidth();
736
                        boxPreceding = true;
737
                    } else if (element.isGlue()) {
738
                        // element is a glue
739
                        if (boxPreceding) {
740
                            // end of the sub-sequence
741
                            index = noteListIterator.previousIndex();
742
                            break;
743
                        }
744
                        boxPreceding = false;
745
                        splitLength += element.getWidth();
746
                    } else {
747
                        // element is a penalty
748
                        if (element.getPenalty() < KnuthElement.INFINITE) {
749
                            // end of the sub-sequence
750
                            index = noteListIterator.previousIndex();
751
                            break;
752
                        }
753
                    }
754
                }
755
            }
756
757
            // if prevSplitLength is 0, this means that the available length isn't enough
758
            // to insert even the smallest split of the last footnote, so we cannot end a
759
            // page here
760
            // if prevSplitLength is > 0 we can insert some footnote content in this page
761
            // and insert the remaining in the following one
762
            //TODO: check this conditional, as the first one is always false...?
763
            if (!somethingAdded) {
764
                // there was not enough space to add a piece of the first new footnote
765
                // this is not a good break
766
                prevSplitLength = 0;
767
            } else if (prevSplitLength > 0) {
768
                // prevIndex is -1 if we have added only some whole footnotes
769
                footnoteListIndex = (prevIndex != -1) ? listIndex : listIndex - 1;
770
                footnoteElementIndex = (prevIndex != -1)
771
                    ? prevIndex
772
                    : getFootnoteList(footnoteListIndex).size() - 1;
773
            }
774
            return prevSplitLength;
775
        }
776
    }
777
778
    /** {@inheritDoc} */
779
    @Override
827
    @Override
780
    protected double computeAdjustmentRatio(KnuthNode activeNode, int difference) {
828
    protected double computeAdjustmentRatio(KnuthNode activeNode, int difference) {
781
        // compute the adjustment ratio
829
        // compute the adjustment ratio
782
        if (difference > 0) {
830
        if (difference > 0) {
783
            int maxAdjustment = totalStretch - activeNode.totalStretch;
831
            int maxAdjustment = totalStretch - activeNode.totalStretch;
784
            // add the footnote separator stretch if some footnote content will be added
832
            // add the footnote separator stretch if some footnote content will be added
785
            if (((KnuthPageNode) activeNode).totalFootnotes < totalFootnotesLength) {
833
            if (((KnuthPageNode) activeNode).totalFootnotes < footnotes.totalLength) {
786
                maxAdjustment += footnoteSeparatorLength.getStretch();
834
                maxAdjustment += footnotes.separatorLength.getStretch();
787
            }
835
            }
788
            if (maxAdjustment > 0) {
836
            if (maxAdjustment > 0) {
789
                return (double) difference / maxAdjustment;
837
                return (double) difference / maxAdjustment;
Lines 793-800 Link Here
793
        } else if (difference < 0) {
841
        } else if (difference < 0) {
794
            int maxAdjustment = totalShrink - activeNode.totalShrink;
842
            int maxAdjustment = totalShrink - activeNode.totalShrink;
795
            // add the footnote separator shrink if some footnote content will be added
843
            // add the footnote separator shrink if some footnote content will be added
796
            if (((KnuthPageNode) activeNode).totalFootnotes < totalFootnotesLength) {
844
            if (((KnuthPageNode) activeNode).totalFootnotes < footnotes.totalLength) {
797
                maxAdjustment += footnoteSeparatorLength.getShrink();
845
                maxAdjustment += footnotes.separatorLength.getShrink();
798
            }
846
            }
799
            if (maxAdjustment > 0) {
847
            if (maxAdjustment > 0) {
800
                return (double) difference / maxAdjustment;
848
                return (double) difference / maxAdjustment;
Lines 806-812 Link Here
806
        }
854
        }
807
    }
855
    }
808
856
809
    /** {@inheritDoc} */
810
    @Override
857
    @Override
811
    protected double computeDemerits(KnuthNode activeNode, KnuthElement element,
858
    protected double computeDemerits(KnuthNode activeNode, KnuthElement element,
812
                                    int fitnessClass, double r) {
859
                                    int fitnessClass, double r) {
Lines 814-832 Link Here
814
        // compute demerits
861
        // compute demerits
815
        double f = Math.abs(r);
862
        double f = Math.abs(r);
816
        f = 1 + 100 * f * f * f;
863
        f = 1 + 100 * f * f * f;
864
817
        if (element.isPenalty()) {
865
        if (element.isPenalty()) {
818
            double penalty = element.getPenalty();
866
            double penalty = element.getPenalty();
819
            if (penalty >= 0) {
867
            if (penalty >= 0) {
820
                f += penalty;
868
                f += penalty;
821
                demerits = f * f;
822
            } else if (!element.isForcedBreak()) {
869
            } else if (!element.isForcedBreak()) {
823
                demerits = f * f - penalty * penalty;
870
                demerits -= penalty * penalty;
824
            } else {
825
                demerits = f * f;
826
            }
871
            }
827
        } else {
828
            demerits = f * f;
829
        }
872
        }
873
        demerits += f * f;
830
874
831
        if (element.isPenalty() && ((KnuthPenalty) element).isPenaltyFlagged()
875
        if (element.isPenalty() && ((KnuthPenalty) element).isPenaltyFlagged()
832
            && getElement(activeNode.position).isPenalty()
876
            && getElement(activeNode.position).isPenalty()
Lines 840-873 Link Here
840
            demerits += incompatibleFitnessDemerit;
884
            demerits += incompatibleFitnessDemerit;
841
        }
885
        }
842
886
843
        if (footnotesPending) {
887
        if (footnotes.footnotesPending) {
844
            if (footnoteListIndex < footnotesList.size() - 1) {
888
            demerits += footnotes.getDemerits();
845
                // add demerits for the deferred footnotes
846
                demerits += (footnotesList.size() - 1 - footnoteListIndex)
847
                                * deferredFootnoteDemerits;
848
            }
889
        }
849
            if (footnoteListIndex < footnotesList.size()) {
850
                if (footnoteElementIndex
851
                        < getFootnoteList(footnoteListIndex).size() - 1) {
852
                    // add demerits for the footnote split between pages
853
                    demerits += splitFootnoteDemerits;
854
                }
855
            } else {
856
                //TODO Why can this happen in the first place? Does anybody know? See #44160
857
            }
858
        }
859
        demerits += activeNode.totalDemerits;
890
        demerits += activeNode.totalDemerits;
860
        return demerits;
891
        return demerits;
861
    }
892
    }
862
893
863
    /** {@inheritDoc} */
864
    @Override
894
    @Override
865
    protected void finish() {
895
    protected void finish() {
866
        for (int i = startLine; i < endLine; i++) {
896
        for (int i = startLine; i < endLine; i++) {
867
            for (KnuthPageNode node = (KnuthPageNode) getNode(i);
897
            for (KnuthPageNode node = (KnuthPageNode) getNode(i);
868
                 node != null;
898
                 node != null;
869
                 node = (KnuthPageNode) node.next) {
899
                 node = (KnuthPageNode) node.next) {
870
                if (node.totalFootnotes < totalFootnotesLength) {
900
                if (node.totalFootnotes < footnotes.totalLength) {
871
                    // layout remaining footnote bodies
901
                    // layout remaining footnote bodies
872
                    createFootnotePages(node);
902
                    createFootnotePages(node);
873
                }
903
                }
Lines 877-926 Link Here
877
907
878
    private void createFootnotePages(KnuthPageNode lastNode) {
908
    private void createFootnotePages(KnuthPageNode lastNode) {
879
909
880
        insertedFootnotesLength = lastNode.totalFootnotes;
910
        footnotes.insertedFootnotesLength = lastNode.totalFootnotes;
881
        footnoteListIndex = lastNode.footnoteListIndex;
911
        footnotes.listIndex = lastNode.footnoteListIndex;
882
        footnoteElementIndex = lastNode.footnoteElementIndex;
912
        footnotes.elementIndex = lastNode.footnoteElementIndex;
883
        int availableBPD = getLineWidth(lastNode.line);
913
        int availableBPD = getLineWidth(lastNode.line);
884
        int split = 0;
885
        KnuthPageNode prevNode = lastNode;
914
        KnuthPageNode prevNode = lastNode;
886
915
887
        // create pages containing the remaining footnote bodies
916
        // create pages containing the remaining footnote bodies
888
        while (insertedFootnotesLength < totalFootnotesLength) {
917
        while (footnotes.footnotesRemaining()) {
889
            final int tmpLength = lengthList.get(footnoteListIndex);
918
            final int remaining = footnotes.currentLength() - footnotes.insertedFootnotesLength;
919
890
            // try adding some more content
920
            // try adding some more content
891
            if ((tmpLength - insertedFootnotesLength) <= availableBPD) {
921
            if (remaining <= availableBPD) {
892
                // add a whole footnote
922
                // all fits, so just add everything
893
                availableBPD -= tmpLength - insertedFootnotesLength;
923
                availableBPD -= remaining;
894
                insertedFootnotesLength = tmpLength;
924
                footnotes.insertedFootnotesLength = footnotes.currentLength();
895
                footnoteElementIndex
925
                footnotes.elementIndex
896
                    = getFootnoteList(footnoteListIndex).size() - 1;
926
                    = getFootnoteList(footnotes.listIndex).size() - 1;
897
            } else if ((split = getFootnoteSplit                // CSOK: InnerAssignment
927
            } else {
898
                        (footnoteListIndex, footnoteElementIndex,
928
                int split = footnotes.getFootnoteSplit(availableBPD);
899
                         insertedFootnotesLength, availableBPD, true)) > 0) {
929
                if (split > 0) {
900
                // add a piece of a footnote
930
                    // add a piece of a footnote
901
                availableBPD -= split;
931
                    availableBPD -= split;
902
                insertedFootnotesLength += split;
932
                    footnotes.insertedFootnotesLength += split;
903
                // footnoteListIndex has already been set in getFootnoteSplit()
933
                    // listIndex has already been set in getFootnoteSplit()
904
                // footnoteElementIndex has already been set in getFootnoteSplit()
934
                    // elementIndex has already been set in getFootnoteSplit()
905
            } else {
935
                } else {
906
                // cannot add any content: create a new node and start again
936
                    // cannot add any content: create a new node and start again
907
                KnuthPageNode node = (KnuthPageNode)
937
                    KnuthPageNode node = (KnuthPageNode)
908
                                     createNode(lastNode.position, prevNode.line + 1, 1,
938
                            createNode(lastNode.position, prevNode.line + 1, 1,
909
                                                insertedFootnotesLength - prevNode.totalFootnotes,
939
                                    footnotes.insertedFootnotesLength - prevNode.totalFootnotes,
910
                                                0, 0,
940
                                    0, 0,
911
                                                0, 0, 0,
941
                                    0, 0, 0,
912
                                                0, 0, prevNode);
942
                                    0, 0, prevNode);
913
                addNode(node.line, node);
943
                    addNode(node.line, node);
914
                removeNode(prevNode.line, prevNode);
944
                    removeNode(prevNode.line, prevNode);
915
945
916
                prevNode = node;
946
                    prevNode = node;
917
                availableBPD = getLineWidth(node.line);
947
                    availableBPD = getLineWidth(node.line);
918
            }
948
                }
919
        }
949
            }
950
        }
951
920
        // create the last node
952
        // create the last node
921
        KnuthPageNode node = (KnuthPageNode)
953
        KnuthPageNode node = (KnuthPageNode)
922
                             createNode(lastNode.position, prevNode.line + 1, 1,
954
                             createNode(lastNode.position, prevNode.line + 1, 1,
923
                                        totalFootnotesLength - prevNode.totalFootnotes, 0, 0,
955
                                        footnotes.totalLength - prevNode.totalFootnotes, 0, 0,
924
                                        0, 0, 0,
956
                                        0, 0, 0,
925
                                        0, 0, prevNode);
957
                                        0, 0, prevNode);
926
        addNode(node.line, node);
958
        addNode(node.line, node);
Lines 960-971 Link Here
960
        pageBreaks.subList(0, pageBreaks.size() - 1).clear();
992
        pageBreaks.subList(0, pageBreaks.size() - 1).clear();
961
    }
993
    }
962
994
963
    /** {@inheritDoc} */
964
    @Override
995
    @Override
965
    public void updateData1(int total, double demerits) {
996
    public void updateData1(int total, double demerits) {
966
    }
997
    }
967
998
968
    /** {@inheritDoc} */
969
    @Override
999
    @Override
970
    public void updateData2(KnuthNode bestActiveNode,
1000
    public void updateData2(KnuthNode bestActiveNode,
971
                            KnuthSequence sequence,
1001
                            KnuthSequence sequence,
Lines 1011-1017 Link Here
1011
        // compute the indexes of the first footnote list and the first element in that list
1041
        // compute the indexes of the first footnote list and the first element in that list
1012
        int firstListIndex = ((KnuthPageNode) bestActiveNode.previous).footnoteListIndex;
1042
        int firstListIndex = ((KnuthPageNode) bestActiveNode.previous).footnoteListIndex;
1013
        int firstElementIndex = ((KnuthPageNode) bestActiveNode.previous).footnoteElementIndex;
1043
        int firstElementIndex = ((KnuthPageNode) bestActiveNode.previous).footnoteElementIndex;
1014
        if (footnotesList != null
1044
        if (footnotes.footnotesList != null
1015
                && firstElementIndex == getFootnoteList(firstListIndex).size() - 1) {
1045
                && firstElementIndex == getFootnoteList(firstListIndex).size() - 1) {
1016
            // advance to the next list
1046
            // advance to the next list
1017
            firstListIndex++;
1047
            firstListIndex++;
Lines 1034-1040 Link Here
1034
                ratio, difference));
1064
                ratio, difference));
1035
    }
1065
    }
1036
1066
1037
    /** {@inheritDoc} */
1038
    @Override
1067
    @Override
1039
    protected int filterActiveNodes() {
1068
    protected int filterActiveNodes() {
1040
        // leave only the active node with fewest total demerits
1069
        // leave only the active node with fewest total demerits
Lines 1066-1072 Link Here
1066
     * @return  the element-list
1095
     * @return  the element-list
1067
     */
1096
     */
1068
    protected final List<KnuthElement> getFootnoteList(int index) {
1097
    protected final List<KnuthElement> getFootnoteList(int index) {
1069
        return footnotesList.get(index);
1098
        return footnotes.getList(index);
1070
    }
1099
    }
1071
1100
1072
    /** @return the associated top-level formatting object. */
1101
    /** @return the associated top-level formatting object. */
Lines 1074-1080 Link Here
1074
        return topLevelLM.getFObj();
1103
        return topLevelLM.getFObj();
1075
    }
1104
    }
1076
1105
1077
    /** {@inheritDoc} */
1078
    @Override
1106
    @Override
1079
    protected int getLineWidth(int line) {
1107
    protected int getLineWidth(int line) {
1080
        int bpd;
1108
        int bpd;
Lines 1104-1116 Link Here
1104
1132
1105
    }
1133
    }
1106
1134
1107
    /** {@inheritDoc} */
1108
    @Override
1135
    @Override
1109
    protected int getIPDdifference() {
1136
    protected int getIPDdifference() {
1110
        return ipdDifference;
1137
        return ipdDifference;
1111
    }
1138
    }
1112
1139
1113
    /** {@inheritDoc} */
1114
    @Override
1140
    @Override
1115
    protected int handleIpdChange() {
1141
    protected int handleIpdChange() {
1116
        log.trace("Best node for ipd change:" + bestNodeForIPDChange);
1142
        log.trace("Best node for ipd change:" + bestNodeForIPDChange);
(-)src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java (-13 / +48 lines)
Lines 304-316 Link Here
304
            this.previous = previous;
304
            this.previous = previous;
305
        }
305
        }
306
306
307
        /** {@inheritDoc} */
307
        @Override
308
        public String toString() {
308
        public String toString() {
309
            return "<KnuthNode at " + position + " "
309
            return new StringBuilder(64).append("<")
310
                    + totalWidth + "+" + totalStretch + "-" + totalShrink
310
                    .append(this.getClass().getSimpleName())
311
                    + " line:" + line + " prev:" + (previous != null ? previous.position : -1)
311
                    .append(" at ").append(position)
312
                    + " dem:" + totalDemerits
312
                    .append(" ").append(totalWidth)
313
                    + " fitness:" + FitnessClasses.NAMES[fitness] + ">";
313
                    .append("+").append(totalStretch)
314
                    .append("-").append(totalShrink)
315
                    .append(" line:").append(line)
316
                    .append(" prev:").append(previous != null ? previous.position : -1)
317
                    .append(" dem:").append(totalDemerits)
318
                    .append(" fitness:").append(FitnessClasses.NAMES[fitness])
319
                    .append(">").toString();
314
        }
320
        }
315
    }
321
    }
316
322
Lines 640-646 Link Here
640
                this.par.add(0, KnuthPenalty.DUMMY_ZERO_PENALTY);
646
                this.par.add(0, KnuthPenalty.DUMMY_ZERO_PENALTY);
641
            }
647
            }
642
        }
648
        }
643
        
649
644
        // content would overflow, insert empty line/page and try again
650
        // content would overflow, insert empty line/page and try again
645
        return createNode(
651
        return createNode(
646
                lastTooLong.previous.position, lastTooLong.previous.line + 1, 1,
652
                lastTooLong.previous.position, lastTooLong.previous.line + 1, 1,
Lines 1168-1187 Link Here
1168
     */
1174
     */
1169
    protected int computeDifference(KnuthNode activeNode, KnuthElement element,
1175
    protected int computeDifference(KnuthNode activeNode, KnuthElement element,
1170
                                    int elementIndex) {
1176
                                    int elementIndex) {
1171
        // compute the adjustment ratio
1177
        // compute the difference
1172
        int actualWidth = totalWidth - activeNode.totalWidth;
1178
        int diff = getLineWidth(activeNode.line);
1179
        diff -= (totalWidth - activeNode.totalWidth);
1173
        if (element.isPenalty()) {
1180
        if (element.isPenalty()) {
1174
            actualWidth += element.getWidth();
1181
            diff -= element.getWidth();
1175
        }
1182
        }
1176
        return getLineWidth() - actualWidth;
1183
        if (hasOutOfLineIntrusions()) {
1184
            diff -= getOutOfLineIntrusions(activeNode, elementIndex);
1177
    }
1185
        }
1186
        return diff;
1187
    }
1178
1188
1179
    /**
1189
    /**
1190
     * Hook for subclasses to handle out-of-line content (floats/footnotes).
1191
     * <br/>Default implementation returns {@code false}. Should be overridden
1192
     * by subclasses if/when appropriate.
1193
     * @return  {@code true} if the currently active node would have out-of-line
1194
     *          content intruding on the line
1195
     */
1196
    protected boolean hasOutOfLineIntrusions() {
1197
        return false;
1198
    }
1199
1200
    /**
1201
     * Hook for subclasses to handle out-of-line content (floats/footnotes).
1202
     * This method is called by {@link #computeDifference(KnuthNode, KnuthElement, int)},
1203
     * if {@link #hasOutOfLineIntrusions()} returns {@code true}.
1204
     *
1205
     * @param activeNode    the currently active node
1206
     * @param elementIndex  the element's index
1207
     * @return  the width corresponding to the total out-of-line intrusions
1208
     *          (including static separators)
1209
     */
1210
    protected int getOutOfLineIntrusions(KnuthNode activeNode, int elementIndex) {
1211
        return 0;
1212
    }
1213
1214
    /**
1180
     * Return the adjustment ratio needed to make up for the difference. A ratio of
1215
     * Return the adjustment ratio needed to make up for the difference. A ratio of
1181
     * <ul>
1216
     * <ul>
1182
     *    <li>0 means that the break has the exact right width</li>
1217
     *    <li>0 means that the break has the exact right width</li>
1183
     *    <li>&gt;= -1 &amp;&amp; &lt; 0  means that the break is wider than the line,
1218
     *    <li>&gt;= -1 &amp;&amp; &lt; 0  means that the break is wider than the line,
1184
     *        but within the minimim values of the glues.</li>
1219
     *        but within the minimum values of the glues.</li>
1185
     *    <li>&gt;0 &amp;&amp; &lt; 1 means that the break is smaller than the line width,
1220
     *    <li>&gt;0 &amp;&amp; &lt; 1 means that the break is smaller than the line width,
1186
     *        but within the maximum values of the glues.</li>
1221
     *        but within the maximum values of the glues.</li>
1187
     *    <li>&gt; 1 means that the break is too small to make up for the glues.</li>
1222
     *    <li>&gt; 1 means that the break is too small to make up for the glues.</li>

Return to bug 51304