Source Code Cross Referenced for PageBreakingAlgorithm.java in  » Graphic-Library » fop » org » apache » fop » layoutmgr » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Graphic Library » fop » org.apache.fop.layoutmgr 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * Licensed to the Apache Software Foundation (ASF) under one or more
003:         * contributor license agreements.  See the NOTICE file distributed with
004:         * this work for additional information regarding copyright ownership.
005:         * The ASF licenses this file to You under the Apache License, Version 2.0
006:         * (the "License"); you may not use this file except in compliance with
007:         * the License.  You may obtain a copy of the License at
008:         *
009:         *      http://www.apache.org/licenses/LICENSE-2.0
010:         *
011:         * Unless required by applicable law or agreed to in writing, software
012:         * distributed under the License is distributed on an "AS IS" BASIS,
013:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014:         * See the License for the specific language governing permissions and
015:         * limitations under the License.
016:         */
017:
018:        /* $Id: PageBreakingAlgorithm.java 554094 2007-07-07 00:04:25Z adelmelle $ */
019:
020:        package org.apache.fop.layoutmgr;
021:
022:        import java.util.ArrayList;
023:        import java.util.LinkedList;
024:        import java.util.ListIterator;
025:
026:        import org.apache.commons.logging.Log;
027:        import org.apache.commons.logging.LogFactory;
028:        import org.apache.fop.fo.Constants;
029:        import org.apache.fop.fo.FONode;
030:        import org.apache.fop.fo.FObj;
031:        import org.apache.fop.layoutmgr.AbstractBreaker.PageBreakPosition;
032:
033:        import org.apache.fop.traits.MinOptMax;
034:
035:        class PageBreakingAlgorithm extends BreakingAlgorithm {
036:
037:            /** the logger for the class */
038:            private static Log log = LogFactory
039:                    .getLog(PageBreakingAlgorithm.class);
040:
041:            private LayoutManager topLevelLM;
042:            private PageProvider pageProvider;
043:            private PageBreakingLayoutListener layoutListener;
044:            /** List of PageBreakPosition elements. */
045:            private LinkedList pageBreaks = null;
046:
047:            /** Footnotes which are cited between the currently considered active node (previous
048:             * break) and the current considered break. Its type is
049:             * List<List<KnuthElement>>, it contains the sequences of KnuthElement
050:             * representing the footnotes bodies.
051:             */
052:            private ArrayList footnotesList = null;
053:            /** Cumulated bpd of unhandled footnotes. */
054:            private ArrayList lengthList = null;
055:            /** Length of all the footnotes which will be put on the current page. */
056:            private int totalFootnotesLength = 0;
057:            /**
058:             * Length of all the footnotes which have already been inserted, up to the currently
059:             * considered element. That is, footnotes from the currently considered page plus
060:             * footnotes from its preceding pages.
061:             */
062:            private int insertedFootnotesLength = 0;
063:            /** True if footnote citations have been met since the beginning of the page sequence. */
064:            private boolean footnotesPending = false;
065:            /**
066:             * True if the elements met after the previous break point contain footnote citations.
067:             */
068:            private boolean newFootnotes = false;
069:            /**
070:             * Index of the first footnote met after the previous break point.
071:             */
072:            private int firstNewFootnoteIndex = 0;
073:            /** Index of the last footnote inserted on the current page. */
074:            private int footnoteListIndex = 0;
075:            /** Index of the last element of the last footnote inserted on the current page. */
076:            private int footnoteElementIndex = -1;
077:
078:            // demerits for a page break that splits a footnote 
079:            private int splitFootnoteDemerits = 5000;
080:            // demerits for a page break that defers a whole footnote to the following page 
081:            private int deferredFootnoteDemerits = 10000;
082:            private MinOptMax footnoteSeparatorLength = null;
083:
084:            // the method noBreakBetween(int, int) uses these variables 
085:            // to store parameters and result of the last call, in order
086:            // to reuse them and take less time
087:            private int storedPrevBreakIndex = -1;
088:            private int storedBreakIndex = -1;
089:            private boolean storedValue = false;
090:
091:            //Controls whether overflows should be warned about or not
092:            private boolean autoHeight = false;
093:
094:            //Controls whether a single part should be forced if possible (ex. block-container)
095:            private boolean favorSinglePart = false;
096:
097:            public PageBreakingAlgorithm(LayoutManager topLevelLM,
098:                    PageProvider pageProvider,
099:                    PageBreakingLayoutListener layoutListener, int alignment,
100:                    int alignmentLast, MinOptMax footnoteSeparatorLength,
101:                    boolean partOverflowRecovery, boolean autoHeight,
102:                    boolean favorSinglePart) {
103:                super (alignment, alignmentLast, true, partOverflowRecovery, 0);
104:                this .topLevelLM = topLevelLM;
105:                this .pageProvider = pageProvider;
106:                this .layoutListener = layoutListener;
107:                best = new BestPageRecords();
108:                this .footnoteSeparatorLength = (MinOptMax) footnoteSeparatorLength
109:                        .clone();
110:                // add some stretch, to avoid a restart for every page containing footnotes
111:                if (footnoteSeparatorLength.min == footnoteSeparatorLength.max) {
112:                    footnoteSeparatorLength.max += 10000;
113:                }
114:                this .autoHeight = autoHeight;
115:                this .favorSinglePart = favorSinglePart;
116:            }
117:
118:            /**
119:             * This class represents a feasible breaking point
120:             * with extra information about footnotes.
121:             */
122:            protected class KnuthPageNode extends KnuthNode {
123:
124:                /** Additional length due to footnotes. */
125:                public int totalFootnotes;
126:
127:                /** Index of the last inserted footnote. */
128:                public int footnoteListIndex;
129:
130:                /** Index of the last inserted element of the last inserted footnote. */
131:                public int footnoteElementIndex;
132:
133:                public KnuthPageNode(int position, int line, int fitness,
134:                        int totalWidth, int totalStretch, int totalShrink,
135:                        int totalFootnotes, int footnoteListIndex,
136:                        int footnoteElementIndex, double adjustRatio,
137:                        int availableShrink, int availableStretch,
138:                        int difference, double totalDemerits, KnuthNode previous) {
139:                    super (position, line, fitness, totalWidth, totalStretch,
140:                            totalShrink, adjustRatio, availableShrink,
141:                            availableStretch, difference, totalDemerits,
142:                            previous);
143:                    this .totalFootnotes = totalFootnotes;
144:                    this .footnoteListIndex = footnoteListIndex;
145:                    this .footnoteElementIndex = footnoteElementIndex;
146:                }
147:
148:            }
149:
150:            /**
151:             * this class stores information about how the nodes
152:             * which could start a line ending at the current element
153:             */
154:            protected class BestPageRecords extends BestRecords {
155:
156:                private int[] bestFootnotesLength = new int[4];
157:                private int[] bestFootnoteListIndex = new int[4];
158:                private int[] bestFootnoteElementIndex = new int[4];
159:
160:                public void addRecord(double demerits, KnuthNode node,
161:                        double adjust, int availableShrink,
162:                        int availableStretch, int difference, int fitness) {
163:                    super .addRecord(demerits, node, adjust, availableShrink,
164:                            availableStretch, difference, fitness);
165:                    bestFootnotesLength[fitness] = insertedFootnotesLength;
166:                    bestFootnoteListIndex[fitness] = footnoteListIndex;
167:                    bestFootnoteElementIndex[fitness] = footnoteElementIndex;
168:                }
169:
170:                public int getFootnotesLength(int fitness) {
171:                    return bestFootnotesLength[fitness];
172:                }
173:
174:                public int getFootnoteListIndex(int fitness) {
175:                    return bestFootnoteListIndex[fitness];
176:                }
177:
178:                public int getFootnoteElementIndex(int fitness) {
179:                    return bestFootnoteElementIndex[fitness];
180:                }
181:            }
182:
183:            protected void initialize() {
184:                super .initialize();
185:                insertedFootnotesLength = 0;
186:                footnoteListIndex = 0;
187:                footnoteElementIndex = -1;
188:            }
189:
190:            protected KnuthNode createNode(int position, int line, int fitness,
191:                    int totalWidth, int totalStretch, int totalShrink,
192:                    double adjustRatio, int availableShrink,
193:                    int availableStretch, int difference, double totalDemerits,
194:                    KnuthNode previous) {
195:                return new KnuthPageNode(position, line, fitness, totalWidth,
196:                        totalStretch, totalShrink, insertedFootnotesLength,
197:                        footnoteListIndex, footnoteElementIndex, adjustRatio,
198:                        availableShrink, availableStretch, difference,
199:                        totalDemerits, previous);
200:            }
201:
202:            protected KnuthNode createNode(int position, int line, int fitness,
203:                    int totalWidth, int totalStretch, int totalShrink) {
204:                return new KnuthPageNode(position, line, fitness, totalWidth,
205:                        totalStretch, totalShrink, ((BestPageRecords) best)
206:                                .getFootnotesLength(fitness),
207:                        ((BestPageRecords) best).getFootnoteListIndex(fitness),
208:                        ((BestPageRecords) best)
209:                                .getFootnoteElementIndex(fitness), best
210:                                .getAdjust(fitness), best
211:                                .getAvailableShrink(fitness), best
212:                                .getAvailableStretch(fitness), best
213:                                .getDifference(fitness), best
214:                                .getDemerits(fitness), best.getNode(fitness));
215:            }
216:
217:            /**
218:             * Page-breaking specific handling of the given box. Currently it adds the footnotes
219:             * cited in the given box to the list of to-be-handled footnotes.
220:             * @param box a block-level element possibly containing foonotes citations
221:             */
222:            protected void handleBox(KnuthBox box) {
223:                if (box instanceof  KnuthBlockBox
224:                        && ((KnuthBlockBox) box).hasAnchors()) {
225:                    handleFootnotes(((KnuthBlockBox) box).getElementLists());
226:                    if (!newFootnotes) {
227:                        newFootnotes = true;
228:                        firstNewFootnoteIndex = footnotesList.size() - 1;
229:                    }
230:                }
231:            }
232:
233:            /**
234:             * Handles the footnotes cited inside a block-level box. Updates footnotesList and the
235:             * value of totalFootnotesLength with the lengths of the given footnotes.
236:             * @param elementLists list of KnuthElement sequences corresponding to the footnotes
237:             * bodies
238:             */
239:            private void handleFootnotes(LinkedList elementLists) {
240:                // initialization
241:                if (!footnotesPending) {
242:                    footnotesPending = true;
243:                    footnotesList = new ArrayList();
244:                    lengthList = new ArrayList();
245:                    totalFootnotesLength = 0;
246:                }
247:                if (!newFootnotes) {
248:                    newFootnotes = true;
249:                    firstNewFootnoteIndex = footnotesList.size();
250:                }
251:
252:                // compute the total length of the footnotes
253:                ListIterator elementListsIterator = elementLists.listIterator();
254:                while (elementListsIterator.hasNext()) {
255:                    LinkedList noteList = (LinkedList) elementListsIterator
256:                            .next();
257:
258:                    //Space resolution (Note: this does not respect possible stacking constraints 
259:                    //between footnotes!)
260:                    SpaceResolver.resolveElementList(noteList);
261:
262:                    int noteLength = 0;
263:                    footnotesList.add(noteList);
264:                    ListIterator noteListIterator = noteList.listIterator();
265:                    while (noteListIterator.hasNext()) {
266:                        KnuthElement element = (KnuthElement) noteListIterator
267:                                .next();
268:                        if (element.isBox() || element.isGlue()) {
269:                            noteLength += element.getW();
270:                        }
271:                    }
272:                    int prevLength = (lengthList.size() == 0 ? 0
273:                            : ((Integer) lengthList.get(lengthList.size() - 1))
274:                                    .intValue());
275:                    lengthList.add(new Integer(prevLength + noteLength));
276:                    totalFootnotesLength += noteLength;
277:                }
278:            }
279:
280:            protected int restartFrom(KnuthNode restartingNode, int currentIndex) {
281:                int returnValue = super .restartFrom(restartingNode,
282:                        currentIndex);
283:                newFootnotes = false;
284:                if (footnotesPending) {
285:                    // remove from footnotesList the note lists that will be met
286:                    // after the restarting point
287:                    for (int j = currentIndex; j >= restartingNode.position; j--) {
288:                        KnuthElement resettedElement = getElement(j);
289:                        if (resettedElement instanceof  KnuthBlockBox
290:                                && ((KnuthBlockBox) resettedElement)
291:                                        .hasAnchors()) {
292:                            resetFootnotes(((KnuthBlockBox) resettedElement)
293:                                    .getElementLists());
294:                        }
295:                    }
296:                }
297:                return returnValue;
298:            }
299:
300:            private void resetFootnotes(LinkedList elementLists) {
301:                for (int i = 0; i < elementLists.size(); i++) {
302:                    LinkedList removedList = (LinkedList) footnotesList
303:                            .remove(footnotesList.size() - 1);
304:                    lengthList.remove(lengthList.size() - 1);
305:
306:                    // update totalFootnotesLength
307:                    if (lengthList.size() > 0) {
308:                        totalFootnotesLength = ((Integer) lengthList
309:                                .get(lengthList.size() - 1)).intValue();
310:                    } else {
311:                        totalFootnotesLength = 0;
312:                    }
313:                }
314:                // update footnotesPending;
315:                if (footnotesList.size() == 0) {
316:                    footnotesPending = false;
317:                }
318:            }
319:
320:            protected void considerLegalBreak(KnuthElement element,
321:                    int elementIdx) {
322:                super .considerLegalBreak(element, elementIdx);
323:                newFootnotes = false;
324:            }
325:
326:            protected int computeDifference(KnuthNode activeNode,
327:                    KnuthElement element, int elementIndex) {
328:                KnuthPageNode pageNode = (KnuthPageNode) activeNode;
329:                int actualWidth = totalWidth - pageNode.totalWidth;
330:                int footnoteSplit;
331:                boolean canDeferOldFootnotes;
332:                if (element.isPenalty()) {
333:                    actualWidth += element.getW();
334:                }
335:                if (footnotesPending) {
336:                    // compute the total length of the footnotes not yet inserted
337:                    int allFootnotes = totalFootnotesLength
338:                            - pageNode.totalFootnotes;
339:                    if (allFootnotes > 0) {
340:                        // this page contains some footnote citations
341:                        // add the footnote separator width
342:                        actualWidth += footnoteSeparatorLength.opt;
343:                        if (actualWidth + allFootnotes <= getLineWidth()) {
344:                            // there is enough space to insert all footnotes:
345:                            // add the whole allFootnotes length
346:                            actualWidth += allFootnotes;
347:                            insertedFootnotesLength = pageNode.totalFootnotes
348:                                    + allFootnotes;
349:                            footnoteListIndex = footnotesList.size() - 1;
350:                            footnoteElementIndex = ((LinkedList) footnotesList
351:                                    .get(footnoteListIndex)).size() - 1;
352:                        } else if (((canDeferOldFootnotes = checkCanDeferOldFootnotes(
353:                                pageNode, elementIndex)) || newFootnotes)
354:                                && (footnoteSplit = getFootnoteSplit(pageNode,
355:                                        getLineWidth() - actualWidth,
356:                                        canDeferOldFootnotes)) > 0) {
357:                            // it is allowed to break or even defer footnotes if either:
358:                            //  - there are new footnotes in the last piece of content, and
359:                            //    there is space to add at least a piece of the first one
360:                            //  - or the previous page break deferred some footnote lines, and
361:                            //    this is the first feasible break; in this case it is allowed
362:                            //    to break and defer, if necessary, old and new footnotes
363:                            actualWidth += footnoteSplit;
364:                            insertedFootnotesLength = pageNode.totalFootnotes
365:                                    + footnoteSplit;
366:                            // footnoteListIndex has been set in getFootnoteSplit()
367:                            // footnoteElementIndex has been set in getFootnoteSplit()
368:                        } else {
369:                            // there is no space to add the smallest piece of footnote,
370:                            // or we are trying to add a piece of content with no footnotes and
371:                            // it does not fit in the page, because of previous footnote bodies
372:                            // that cannot be broken:
373:                            // add the whole allFootnotes length, so this breakpoint will be discarded
374:                            actualWidth += allFootnotes;
375:                            insertedFootnotesLength = pageNode.totalFootnotes
376:                                    + allFootnotes;
377:                            footnoteListIndex = footnotesList.size() - 1;
378:                            footnoteElementIndex = ((LinkedList) footnotesList
379:                                    .get(footnoteListIndex)).size() - 1;
380:                        }
381:                    } else {
382:                        // all footnotes have already been placed on previous pages
383:                    }
384:                } else {
385:                    // there are no footnotes
386:                }
387:                return getLineWidth(activeNode.line) - actualWidth;
388:            }
389:
390:            /** Checks whether footnotes from preceding pages may be deferred to the page after
391:             * the given element.
392:             * @param node active node for the preceding page break
393:             * @param contentElementIndex index of the Knuth element considered for the
394:             * current page break
395:             */
396:            private boolean checkCanDeferOldFootnotes(KnuthPageNode node,
397:                    int contentElementIndex) {
398:                return (noBreakBetween(node.position, contentElementIndex) && deferredFootnotes(
399:                        node.footnoteListIndex, node.footnoteElementIndex,
400:                        node.totalFootnotes));
401:            }
402:
403:            /**
404:             * Returns true if there may be no breakpoint between the two given elements.
405:             * @param prevBreakIndex index of the element from the currently considered active
406:             * node
407:             * @param breakIndex index of the currently considered breakpoint
408:             * @return true if no element between the two can be a breakpoint
409:             */
410:            private boolean noBreakBetween(int prevBreakIndex, int breakIndex) {
411:                // this method stores the parameters and the return value from previous calls
412:                // in order to avoid scanning the element list unnecessarily:
413:                //  - if there is no break between element #i and element #j
414:                //    there will not be a break between #(i+h) and #j too
415:                //  - if there is a break between element #i and element #j
416:                //    there will be a break between #(i-h) and #(j+k) too
417:                if (storedPrevBreakIndex != -1
418:                        && ((prevBreakIndex >= storedPrevBreakIndex
419:                                && breakIndex == storedBreakIndex && storedValue) || (prevBreakIndex <= storedPrevBreakIndex
420:                                && breakIndex >= storedBreakIndex && !storedValue))) {
421:                    // use the stored value, do nothing
422:                } else {
423:                    // compute the new value
424:                    int index;
425:                    // ignore suppressed elements
426:                    for (index = prevBreakIndex + 1; !par.getElement(index)
427:                            .isBox(); index++) {
428:                        //nop
429:                    }
430:                    // find the next break
431:                    for (; index < breakIndex; index++) {
432:                        if (par.getElement(index).isGlue()
433:                                && par.getElement(index - 1).isBox()
434:                                || par.getElement(index).isPenalty()
435:                                && ((KnuthElement) par.getElement(index))
436:                                        .getP() < KnuthElement.INFINITE) {
437:                            // break found
438:                            break;
439:                        }
440:                    }
441:                    // update stored parameters and value
442:                    storedPrevBreakIndex = prevBreakIndex;
443:                    storedBreakIndex = breakIndex;
444:                    storedValue = (index == breakIndex);
445:                }
446:                return storedValue;
447:            }
448:
449:            /**
450:             * Returns true if their are (pieces of) footnotes to be typeset on the current page.
451:             * @param listIndex index of the last inserted footnote for the currently considered
452:             * active node
453:             * @param elementIndex index of the last element of the last inserted footnote
454:             * @param length total length of all footnotes inserted so far
455:             */
456:            private boolean deferredFootnotes(int listIndex, int elementIndex,
457:                    int length) {
458:                return ((newFootnotes && firstNewFootnoteIndex != 0 && (listIndex < firstNewFootnoteIndex - 1 || elementIndex < ((LinkedList) footnotesList
459:                        .get(listIndex)).size() - 1)) || length < totalFootnotesLength);
460:            }
461:
462:            /**
463:             * Tries to split the flow of footnotes to put one part on the current page.
464:             * @param activeNode currently considered previous page break
465:             * @param availableLength available space for footnotes
466:             * @param canDeferOldFootnotes
467:             */
468:            private int getFootnoteSplit(KnuthPageNode activeNode,
469:                    int availableLength, boolean canDeferOldFootnotes) {
470:                return getFootnoteSplit(activeNode.footnoteListIndex,
471:                        activeNode.footnoteElementIndex,
472:                        activeNode.totalFootnotes, availableLength,
473:                        canDeferOldFootnotes);
474:            }
475:
476:            /**
477:             * Tries to split the flow of footnotes to put one part on the current page.
478:             * @param prevListIndex index of the last footnote on the previous page
479:             * @param prevElementIndex index of the last element of the last footnote
480:             * @param prevLength total length of footnotes inserted so far
481:             * @param availableLength available space for footnotes on this page
482:             * @param canDeferOldFootnotes
483:             */
484:            private int getFootnoteSplit(int prevListIndex,
485:                    int prevElementIndex, int prevLength, int availableLength,
486:                    boolean canDeferOldFootnotes) {
487:                if (availableLength <= 0) {
488:                    return 0;
489:                } else {
490:                    // the split should contain a piece of the last footnote
491:                    // together with all previous, not yet inserted footnotes;
492:                    // but if this is not possible, try adding as much content as possible
493:                    int splitLength = 0;
494:                    ListIterator noteListIterator = null;
495:                    KnuthElement element = null;
496:                    boolean somethingAdded = false;
497:
498:                    // prevListIndex and prevElementIndex points to the last footnote element
499:                    // already placed in a page: advance to the next element
500:                    int listIndex = prevListIndex;
501:                    int elementIndex = prevElementIndex;
502:                    if (elementIndex == ((LinkedList) footnotesList
503:                            .get(listIndex)).size() - 1) {
504:                        listIndex++;
505:                        elementIndex = 0;
506:                    } else {
507:                        elementIndex++;
508:                    }
509:
510:                    // try adding whole notes
511:                    if (footnotesList.size() - 1 > listIndex) {
512:                        // add the previous footnotes: these cannot be broken or deferred
513:                        if (!canDeferOldFootnotes && newFootnotes
514:                                && firstNewFootnoteIndex > 0) {
515:                            splitLength = ((Integer) lengthList
516:                                    .get(firstNewFootnoteIndex - 1)).intValue()
517:                                    - prevLength;
518:                            listIndex = firstNewFootnoteIndex;
519:                            elementIndex = 0;
520:                        }
521:                        // try adding the new footnotes
522:                        while (((Integer) lengthList.get(listIndex)).intValue()
523:                                - prevLength <= availableLength) {
524:                            splitLength = ((Integer) lengthList.get(listIndex))
525:                                    .intValue()
526:                                    - prevLength;
527:                            somethingAdded = true;
528:                            listIndex++;
529:                            elementIndex = 0;
530:                        }
531:                        // as this method is called only if it is not possible to insert
532:                        // all footnotes, at this point listIndex and elementIndex points to
533:                        // an existing element, the next one we will try to insert 
534:                    }
535:
536:                    // try adding a split of the next note
537:                    noteListIterator = ((LinkedList) footnotesList
538:                            .get(listIndex)).listIterator(elementIndex);
539:
540:                    int prevSplitLength = 0;
541:                    int prevIndex = -1;
542:                    int index = -1;
543:
544:                    while (!(somethingAdded && splitLength > availableLength)) {
545:                        if (!somethingAdded) {
546:                            somethingAdded = true;
547:                        } else {
548:                            prevSplitLength = splitLength;
549:                            prevIndex = index;
550:                        }
551:                        // get a sub-sequence from the note element list
552:                        boolean bPrevIsBox = false;
553:                        while (noteListIterator.hasNext()) {
554:                            // as this method is called only if it is not possible to insert
555:                            // all footnotes, and we have already tried (and failed) to insert
556:                            // this whole footnote, the while loop will never reach the end
557:                            // of the note sequence
558:                            element = (KnuthElement) noteListIterator.next();
559:                            if (element.isBox()) {
560:                                // element is a box
561:                                splitLength += element.getW();
562:                                bPrevIsBox = true;
563:                            } else if (element.isGlue()) {
564:                                // element is a glue
565:                                if (bPrevIsBox) {
566:                                    // end of the sub-sequence
567:                                    index = noteListIterator.previousIndex();
568:                                    break;
569:                                }
570:                                bPrevIsBox = false;
571:                                splitLength += element.getW();
572:                            } else {
573:                                // element is a penalty
574:                                if (element.getP() < KnuthElement.INFINITE) {
575:                                    // end of the sub-sequence
576:                                    index = noteListIterator.previousIndex();
577:                                    break;
578:                                }
579:                            }
580:                        }
581:                    }
582:                    // if prevSplitLength is 0, this means that the available length isn't enough
583:                    // to insert even the smallest split of the last footnote, so we cannot end a
584:                    // page here
585:                    // if prevSplitLength is > 0 we can insert some footnote content in this page
586:                    // and insert the remaining in the following one
587:                    if (!somethingAdded) {
588:                        // there was not enough space to add a piece of the first new footnote
589:                        // this is not a good break
590:                        prevSplitLength = 0;
591:                    } else if (prevSplitLength > 0) {
592:                        // prevIndex is -1 if we have added only some whole footnotes
593:                        footnoteListIndex = (prevIndex != -1) ? listIndex
594:                                : listIndex - 1;
595:                        footnoteElementIndex = (prevIndex != -1) ? prevIndex
596:                                : ((LinkedList) footnotesList
597:                                        .get(footnoteListIndex)).size() - 1;
598:                    }
599:                    return prevSplitLength;
600:                }
601:            }
602:
603:            protected double computeAdjustmentRatio(KnuthNode activeNode,
604:                    int difference) {
605:                // compute the adjustment ratio
606:                if (difference > 0) {
607:                    int maxAdjustment = totalStretch - activeNode.totalStretch;
608:                    // add the footnote separator stretch if some footnote content will be added
609:                    if (((KnuthPageNode) activeNode).totalFootnotes < totalFootnotesLength) {
610:                        maxAdjustment += footnoteSeparatorLength.max
611:                                - footnoteSeparatorLength.opt;
612:                    }
613:                    if (maxAdjustment > 0) {
614:                        return (double) difference / maxAdjustment;
615:                    } else {
616:                        return INFINITE_RATIO;
617:                    }
618:                } else if (difference < 0) {
619:                    int maxAdjustment = totalShrink - activeNode.totalShrink;
620:                    // add the footnote separator shrink if some footnote content will be added
621:                    if (((KnuthPageNode) activeNode).totalFootnotes < totalFootnotesLength) {
622:                        maxAdjustment += footnoteSeparatorLength.opt
623:                                - footnoteSeparatorLength.min;
624:                    }
625:                    if (maxAdjustment > 0) {
626:                        return (double) difference / maxAdjustment;
627:                    } else {
628:                        return -INFINITE_RATIO;
629:                    }
630:                } else {
631:                    return 0;
632:                }
633:            }
634:
635:            protected double computeDemerits(KnuthNode activeNode,
636:                    KnuthElement element, int fitnessClass, double r) {
637:                double demerits = 0;
638:                // compute demerits
639:                double f = Math.abs(r);
640:                f = 1 + 100 * f * f * f;
641:                if (element.isPenalty() && element.getP() >= 0) {
642:                    f += element.getP();
643:                    demerits = f * f;
644:                } else if (element.isPenalty() && !element.isForcedBreak()) {
645:                    double penalty = element.getP();
646:                    demerits = f * f - penalty * penalty;
647:                } else {
648:                    demerits = f * f;
649:                }
650:
651:                if (element.isPenalty()
652:                        && ((KnuthPenalty) element).isFlagged()
653:                        && getElement(activeNode.position).isPenalty()
654:                        && ((KnuthPenalty) getElement(activeNode.position))
655:                                .isFlagged()) {
656:                    // add demerit for consecutive breaks at flagged penalties
657:                    demerits += repeatedFlaggedDemerit;
658:                }
659:                if (Math.abs(fitnessClass - activeNode.fitness) > 1) {
660:                    // add demerit for consecutive breaks
661:                    // with very different fitness classes
662:                    demerits += incompatibleFitnessDemerit;
663:                }
664:
665:                if (footnotesPending) {
666:                    if (footnoteListIndex < footnotesList.size() - 1) {
667:                        // add demerits for the deferred footnotes
668:                        demerits += (footnotesList.size() - 1 - footnoteListIndex)
669:                                * deferredFootnoteDemerits;
670:                    }
671:                    if (footnoteElementIndex < ((LinkedList) footnotesList
672:                            .get(footnoteListIndex)).size() - 1) {
673:                        // add demerits for the footnote split between pages
674:                        demerits += splitFootnoteDemerits;
675:                    }
676:                }
677:                demerits += activeNode.totalDemerits;
678:                return demerits;
679:            }
680:
681:            protected void finish() {
682:                for (int i = startLine; i < endLine; i++) {
683:                    for (KnuthPageNode node = (KnuthPageNode) getNode(i); node != null; node = (KnuthPageNode) node.next) {
684:                        if (node.totalFootnotes < totalFootnotesLength) {
685:                            // layout remaining footnote bodies
686:                            createFootnotePages(node);
687:                        }
688:                    }
689:                }
690:            }
691:
692:            private void createFootnotePages(KnuthPageNode lastNode) {
693:                insertedFootnotesLength = lastNode.totalFootnotes;
694:                footnoteListIndex = lastNode.footnoteListIndex;
695:                footnoteElementIndex = lastNode.footnoteElementIndex;
696:                int availableBPD = getLineWidth();
697:                int split = 0;
698:                KnuthPageNode prevNode = lastNode;
699:
700:                // create pages containing the remaining footnote bodies
701:                while (insertedFootnotesLength < totalFootnotesLength) {
702:                    // try adding some more content
703:                    if (((Integer) lengthList.get(footnoteListIndex))
704:                            .intValue()
705:                            - insertedFootnotesLength <= availableBPD) {
706:                        // add a whole footnote
707:                        availableBPD -= ((Integer) lengthList
708:                                .get(footnoteListIndex)).intValue()
709:                                - insertedFootnotesLength;
710:                        insertedFootnotesLength = ((Integer) lengthList
711:                                .get(footnoteListIndex)).intValue();
712:                        footnoteElementIndex = ((LinkedList) footnotesList
713:                                .get(footnoteListIndex)).size() - 1;
714:                    } else if ((split = getFootnoteSplit(footnoteListIndex,
715:                            footnoteElementIndex, insertedFootnotesLength,
716:                            availableBPD, true)) > 0) {
717:                        // add a piece of a footnote
718:                        availableBPD -= split;
719:                        insertedFootnotesLength += split;
720:                        // footnoteListIndex has already been set in getFootnoteSplit()
721:                        // footnoteElementIndex has already been set in getFootnoteSplit()
722:                    } else {
723:                        // cannot add any content: create a new node and start again
724:                        KnuthPageNode node = (KnuthPageNode) createNode(
725:                                lastNode.position, prevNode.line + 1, 1,
726:                                insertedFootnotesLength
727:                                        - prevNode.totalFootnotes, 0, 0, 0, 0,
728:                                0, 0, 0, prevNode);
729:                        addNode(node.line, node);
730:                        removeNode(prevNode.line, prevNode);
731:
732:                        prevNode = node;
733:                        availableBPD = getLineWidth();
734:                    }
735:                }
736:                // create the last node
737:                KnuthPageNode node = (KnuthPageNode) createNode(
738:                        lastNode.position, prevNode.line + 1, 1,
739:                        totalFootnotesLength - prevNode.totalFootnotes, 0, 0,
740:                        0, 0, 0, 0, 0, prevNode);
741:                addNode(node.line, node);
742:                removeNode(prevNode.line, prevNode);
743:            }
744:
745:            /**
746:             * @return a list of PageBreakPosition elements
747:             */
748:            public LinkedList getPageBreaks() {
749:                return pageBreaks;
750:            }
751:
752:            public void insertPageBreakAsFirst(PageBreakPosition pageBreak) {
753:                if (pageBreaks == null) {
754:                    pageBreaks = new LinkedList();
755:                }
756:                pageBreaks.addFirst(pageBreak);
757:            }
758:
759:            private int getPartCount() {
760:                if (pageBreaks == null) {
761:                    return 0;
762:                } else {
763:                    return pageBreaks.size();
764:                }
765:            }
766:
767:            public void updateData1(int total, double demerits) {
768:            }
769:
770:            public void updateData2(KnuthNode bestActiveNode,
771:                    KnuthSequence sequence, int total) {
772:                //int difference = (bestActiveNode.line < total) 
773:                //      ? bestActiveNode.difference : bestActiveNode.difference + fillerMinWidth;
774:                int difference = bestActiveNode.difference;
775:                if (difference + bestActiveNode.availableShrink < 0) {
776:                    if (!autoHeight) {
777:                        if (layoutListener != null) {
778:                            layoutListener.notifyOverflow(
779:                                    bestActiveNode.line - 1, getFObj());
780:                        } else if (log.isWarnEnabled()) {
781:                            log
782:                                    .warn(FONode
783:                                            .decorateWithContextInfo(
784:                                                    "Part/page "
785:                                                            + (bestActiveNode.line - 1)
786:                                                            + " overflows the available area in block-progression dimension.",
787:                                                    getFObj()));
788:                        }
789:                    }
790:                }
791:                boolean isNonLastPage = (bestActiveNode.line < total);
792:                int blockAlignment = isNonLastPage ? alignment : alignmentLast;
793:                // it is always allowed to adjust space, so the ratio must be set regardless of
794:                // the value of the property display-align; the ratio must be <= 1
795:                double ratio = bestActiveNode.adjustRatio;
796:                if (ratio < 0) {
797:                    // page break with a negative difference:
798:                    // spaces always have enough shrink
799:                    difference = 0;
800:                } else if (ratio <= 1 && isNonLastPage) {
801:                    // not-last page break with a positive difference smaller than the available stretch:
802:                    // spaces can stretch to fill the whole difference
803:                    difference = 0;
804:                } else if (ratio > 1) {
805:                    // not-last page with a positive difference greater than the available stretch
806:                    // spaces can stretch to fill the difference only partially
807:                    ratio = 1;
808:                    difference -= bestActiveNode.availableStretch;
809:                } else {
810:                    // last page with a positive difference:
811:                    // spaces do not need to stretch
812:                    if (blockAlignment != Constants.EN_JUSTIFY) {
813:                        ratio = 0;
814:                    } else {
815:                        //Stretch as much as possible on last page
816:                        difference = 0;
817:                    }
818:                }
819:                // compute the indexes of the first footnote list and the first element in that list
820:                int firstListIndex = ((KnuthPageNode) bestActiveNode.previous).footnoteListIndex;
821:                int firstElementIndex = ((KnuthPageNode) bestActiveNode.previous).footnoteElementIndex;
822:                if (footnotesList != null
823:                        && firstElementIndex == ((LinkedList) footnotesList
824:                                .get(firstListIndex)).size() - 1) {
825:                    // advance to the next list
826:                    firstListIndex++;
827:                    firstElementIndex = 0;
828:                } else {
829:                    firstElementIndex++;
830:                }
831:
832:                // add nodes at the beginning of the list, as they are found
833:                // backwards, from the last one to the first one
834:                if (log.isDebugEnabled()) {
835:                    log.debug("BBA> difference=" + difference + " ratio="
836:                            + ratio + " position=" + bestActiveNode.position);
837:                }
838:                insertPageBreakAsFirst(new PageBreakPosition(this .topLevelLM,
839:                        bestActiveNode.position, firstListIndex,
840:                        firstElementIndex,
841:                        ((KnuthPageNode) bestActiveNode).footnoteListIndex,
842:                        ((KnuthPageNode) bestActiveNode).footnoteElementIndex,
843:                        ratio, difference));
844:            }
845:
846:            protected int filterActiveNodes() {
847:                // leave only the active node with fewest total demerits
848:                KnuthNode bestActiveNode = null;
849:                for (int i = startLine; i < endLine; i++) {
850:                    for (KnuthNode node = getNode(i); node != null; node = node.next) {
851:                        if (favorSinglePart
852:                                && node.line > 1
853:                                && bestActiveNode != null
854:                                && Math.abs(bestActiveNode.difference) < bestActiveNode.availableShrink) {
855:                            //favor current best node, so just skip the current node because it would
856:                            //result in more than one part
857:                        } else {
858:                            bestActiveNode = compareNodes(bestActiveNode, node);
859:                        }
860:                        if (node != bestActiveNode) {
861:                            removeNode(i, node);
862:                        }
863:                    }
864:                }
865:                return bestActiveNode.line;
866:            }
867:
868:            public LinkedList getFootnoteList(int index) {
869:                return (LinkedList) footnotesList.get(index);
870:            }
871:
872:            /** @return the associated top-level formatting object. */
873:            public FObj getFObj() {
874:                return topLevelLM.getFObj();
875:            }
876:
877:            /** @see org.apache.fop.layoutmgr.BreakingAlgorithm#getLineWidth(int) */
878:            protected int getLineWidth(int line) {
879:                int bpd;
880:                if (pageProvider != null) {
881:                    bpd = pageProvider.getAvailableBPD(line);
882:                } else {
883:                    bpd = super .getLineWidth(line);
884:                }
885:                if (log.isTraceEnabled()) {
886:                    log.trace("getLineWidth(" + line + ") -> " + bpd);
887:                }
888:                return bpd;
889:            }
890:
891:            /**
892:             * Interface to notify about layout events during page breaking.
893:             */
894:            public interface PageBreakingLayoutListener {
895:
896:                /**
897:                 * Issued when an overflow is detected
898:                 * @param part the number of the part (page) this happens on
899:                 * @param obj the root FO object where this happens
900:                 */
901:                void notifyOverflow(int part, FObj obj);
902:
903:            }
904:
905:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.