KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > fop > layoutmgr > AbstractBreaker


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 /* $Id: AbstractBreaker.java 428750 2006-08-04 15:13:53Z jeremias $ */
19
20 package org.apache.fop.layoutmgr;
21
22 import java.util.LinkedList JavaDoc;
23 import java.util.List JavaDoc;
24 import java.util.ListIterator JavaDoc;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.apache.fop.fo.Constants;
29 import org.apache.fop.traits.MinOptMax;
30
31 /**
32  * Abstract base class for breakers (page breakers, static region handlers etc.).
33  */

34 public abstract class AbstractBreaker {
35
36     /** logging instance */
37     protected static Log log = LogFactory.getLog(AbstractBreaker.class);
38
39     public static class PageBreakPosition extends LeafPosition {
40         double bpdAdjust; // Percentage to adjust (stretch or shrink)
41
int difference;
42         int footnoteFirstListIndex;
43         int footnoteFirstElementIndex;
44         int footnoteLastListIndex;
45         int footnoteLastElementIndex;
46
47         PageBreakPosition(LayoutManager lm, int iBreakIndex,
48                           int ffli, int ffei, int flli, int flei,
49                           double bpdA, int diff) {
50             super(lm, iBreakIndex);
51             bpdAdjust = bpdA;
52             difference = diff;
53             footnoteFirstListIndex = ffli;
54             footnoteFirstElementIndex = ffei;
55             footnoteLastListIndex = flli;
56             footnoteLastElementIndex = flei;
57         }
58     }
59
60     public class BlockSequence extends BlockKnuthSequence {
61
62         /** Number of elements to ignore at the beginning of the list. */
63         public int ignoreAtStart = 0;
64         /** Number of elements to ignore at the end of the list. */
65         public int ignoreAtEnd = 0;
66
67         /**
68          * startOn represents where on the page/which page layout
69          * should start for this BlockSequence. Acceptable values:
70          * Constants.EN_ANY (can continue from finished location
71          * of previous BlockSequence?), EN_COLUMN, EN_ODD_PAGE,
72          * EN_EVEN_PAGE.
73          */

74         private int startOn;
75
76         private int displayAlign;
77         
78         /**
79          * Creates a new BlockSequence.
80          * @param iStartOn the kind of page the sequence should start on. One of EN_ANY, EN_COLUMN,
81          * EN_ODD_PAGE, EN_EVEN_PAGE.
82          * @param displayAlign the value for the display-align property
83          */

84         public BlockSequence(int iStartOn, int displayAlign) {
85             super();
86             startOn = iStartOn;
87             this.displayAlign = displayAlign;
88         }
89         
90         /**
91          * @return the kind of page the sequence should start on. One of EN_ANY, EN_COLUMN,
92          * EN_ODD_PAGE, EN_EVEN_PAGE.
93          */

94         public int getStartOn() {
95             return this.startOn;
96         }
97
98         /** @return the value for the display-align property */
99         public int getDisplayAlign() {
100             return this.displayAlign;
101         }
102         /**
103          * Finalizes a Knuth sequence.
104          * @return a finalized sequence.
105          */

106         public KnuthSequence endSequence() {
107             return endSequence(null);
108         }
109         
110         /**
111          * Finalizes a Knuth sequence.
112          * @param breakPosition a Position instance for the last penalty (may be null)
113          * @return a finalized sequence.
114          */

115         public KnuthSequence endSequence(Position breakPosition) {
116             // remove glue and penalty item at the end of the paragraph
117
while (this.size() > ignoreAtStart
118                    && !((KnuthElement)this.get(this.size() - 1)).isBox()) {
119                 this.remove(this.size() - 1);
120             }
121             if (this.size() > ignoreAtStart) {
122                 // add the elements representing the space at the end of the last line
123
// and the forced break
124
if (getDisplayAlign() == Constants.EN_X_DISTRIBUTE && isSinglePartFavored()) {
125                     this.add(new KnuthPenalty(0, -KnuthElement.INFINITE,
126                                 false, breakPosition, false));
127                     ignoreAtEnd = 1;
128                 } else {
129                     this.add(new KnuthPenalty(0, KnuthElement.INFINITE,
130                                 false, null, false));
131                     this.add(new KnuthGlue(0, 10000000, 0, null, false));
132                     this.add(new KnuthPenalty(0, -KnuthElement.INFINITE,
133                                 false, breakPosition, false));
134                     ignoreAtEnd = 3;
135                 }
136                 return this;
137             } else {
138                 this.clear();
139                 return null;
140             }
141         }
142
143         public BlockSequence endBlockSequence(Position breakPosition) {
144             KnuthSequence temp = endSequence(breakPosition);
145             if (temp != null) {
146                 BlockSequence returnSequence = new BlockSequence(startOn, displayAlign);
147                 returnSequence.addAll(temp);
148                 returnSequence.ignoreAtEnd = this.ignoreAtEnd;
149                 return returnSequence;
150             } else {
151                 return null;
152             }
153         }
154
155     }
156
157     /** blockListIndex of the current BlockSequence in blockLists */
158     private int blockListIndex = 0;
159
160     private List JavaDoc blockLists = null;
161
162     protected int alignment;
163     private int alignmentLast;
164
165     protected MinOptMax footnoteSeparatorLength = new MinOptMax(0);
166
167     protected abstract int getCurrentDisplayAlign();
168     protected abstract boolean hasMoreContent();
169     protected abstract void addAreas(PositionIterator posIter, LayoutContext context);
170     protected abstract LayoutManager getTopLevelLM();
171     protected abstract LayoutManager getCurrentChildLM();
172     
173     /**
174      * Controls the behaviour of the algorithm in cases where the first element of a part
175      * overflows a line/page.
176      * @return true if the algorithm should try to send the element to the next line/page.
177      */

178     protected boolean isPartOverflowRecoveryActivated() {
179         return true;
180     }
181
182     /**
183      * @return true if one a single part should be produced if possible (ex. for block-containers)
184      */

185     protected boolean isSinglePartFavored() {
186         return false;
187     }
188     
189     /**
190      * Returns the PageProvider if any. PageBreaker overrides this method because each
191      * page may have a different available BPD which needs to be accessible to the breaking
192      * algorithm.
193      * @return the applicable PageProvider, or null if not applicable
194      */

195     protected PageSequenceLayoutManager.PageProvider getPageProvider() {
196         return null;
197     }
198     
199     /**
200      * Returns a PageBreakingLayoutListener for the PageBreakingAlgorithm to notify about layout
201      * problems.
202      * @return the listener instance or null if no notifications are needed
203      */

204     protected PageBreakingAlgorithm.PageBreakingLayoutListener getLayoutListener() {
205         return null;
206     }
207     
208     /*
209      * This method is to contain the logic to determine the LM's
210      * getNextKnuthElements() implementation(s) that are to be called.
211      * @return LinkedList of Knuth elements.
212      */

213     protected abstract LinkedList JavaDoc getNextKnuthElements(LayoutContext context, int alignment);
214
215     /** @return true if there's no content that could be handled. */
216     public boolean isEmpty() {
217         return (blockLists.size() == 0);
218     }
219     
220     protected void startPart(BlockSequence list, int breakClass) {
221         //nop
222
}
223     
224     /**
225      * This method is called when no content is available for a part. Used to force empty pages.
226      */

227     protected void handleEmptyContent() {
228         //nop
229
}
230     
231     protected abstract void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp);
232
233     /**
234      * Creates the top-level LayoutContext for the breaker operation.
235      * @return the top-level LayoutContext
236      */

237     protected LayoutContext createLayoutContext() {
238         return new LayoutContext(0);
239     }
240     
241     /**
242      * Used to update the LayoutContext in subclasses prior to starting a new element list.
243      * @param context the LayoutContext to update
244      */

245     protected void updateLayoutContext(LayoutContext context) {
246         //nop
247
}
248     
249     /**
250      * Used for debugging purposes. Notifies all registered observers about the element list.
251      * Override to set different parameters.
252      * @param elementList the Knuth element list
253      */

254     protected void observeElementList(List JavaDoc elementList) {
255         ElementListObserver.observe(elementList, "breaker", null);
256     }
257     
258     /**
259      * Starts the page breaking process.
260      * @param flowBPD the constant available block-progression-dimension (used for every part)
261      */

262     public void doLayout(int flowBPD) {
263         doLayout(flowBPD, false);
264     }
265     
266     /**
267      * Starts the page breaking process.
268      * @param flowBPD the constant available block-progression-dimension (used for every part)
269      * @param autoHeight true if warnings about overflows should be disabled because the
270      * the BPD is really undefined (for footnote-separators, for example)
271      */

272     public void doLayout(int flowBPD, boolean autoHeight) {
273         LayoutContext childLC = createLayoutContext();
274         childLC.setStackLimit(new MinOptMax(flowBPD));
275
276         if (getCurrentDisplayAlign() == Constants.EN_X_FILL) {
277             //EN_X_FILL is non-standard (by LF)
278
alignment = Constants.EN_JUSTIFY;
279         } else if (getCurrentDisplayAlign() == Constants.EN_X_DISTRIBUTE) {
280             //EN_X_DISTRIBUTE is non-standard (by LF)
281
alignment = Constants.EN_JUSTIFY;
282         } else {
283             alignment = Constants.EN_START;
284         }
285         alignmentLast = Constants.EN_START;
286         if (isSinglePartFavored() && alignment == Constants.EN_JUSTIFY) {
287             alignmentLast = Constants.EN_JUSTIFY;
288         }
289         childLC.setBPAlignment(alignment);
290
291         BlockSequence blockList;
292         blockLists = new java.util.ArrayList JavaDoc();
293
294         log.debug("PLM> flow BPD =" + flowBPD);
295         
296         //*** Phase 1: Get Knuth elements ***
297
int nextSequenceStartsOn = Constants.EN_ANY;
298         while (hasMoreContent()) {
299             blockLists.clear();
300
301             nextSequenceStartsOn = getNextBlockList(childLC, nextSequenceStartsOn, blockLists);
302
303             //*** Phase 2: Alignment and breaking ***
304
log.debug("PLM> blockLists.size() = " + blockLists.size());
305             for (blockListIndex = 0; blockListIndex < blockLists.size(); blockListIndex++) {
306                 blockList = (BlockSequence) blockLists.get(blockListIndex);
307                 
308                 //debug code start
309
if (log.isDebugEnabled()) {
310                     log.debug(" blockListIndex = " + blockListIndex);
311                     String JavaDoc pagina = (blockList.startOn == Constants.EN_ANY) ? "any page"
312                             : (blockList.startOn == Constants.EN_ODD_PAGE) ? "odd page"
313                                     : "even page";
314                     log.debug(" sequence starts on " + pagina);
315                 }
316                 observeElementList(blockList);
317                 //debug code end
318

319                 log.debug("PLM> start of algorithm (" + this.getClass().getName()
320                         + "), flow BPD =" + flowBPD);
321                 PageBreakingAlgorithm alg = new PageBreakingAlgorithm(getTopLevelLM(),
322                         getPageProvider(), getLayoutListener(),
323                         alignment, alignmentLast, footnoteSeparatorLength,
324                         isPartOverflowRecoveryActivated(), autoHeight, isSinglePartFavored());
325                 int iOptPageCount;
326
327                 BlockSequence effectiveList;
328                 if (getCurrentDisplayAlign() == Constants.EN_X_FILL) {
329                     /* justification */
330                     effectiveList = justifyBoxes(blockList, alg, flowBPD);
331                 } else {
332                     /* no justification */
333                     effectiveList = blockList;
334                 }
335
336                 //iOptPageCount = alg.firstFit(effectiveList, flowBPD, 1, true);
337
alg.setConstantLineWidth(flowBPD);
338                 iOptPageCount = alg.findBreakingPoints(effectiveList, /*flowBPD,*/
339                             1, true, BreakingAlgorithm.ALL_BREAKS);
340                 log.debug("PLM> iOptPageCount= " + iOptPageCount
341                         + " pageBreaks.size()= " + alg.getPageBreaks().size());
342
343                 
344                 //*** Phase 3: Add areas ***
345
doPhase3(alg, iOptPageCount, blockList, effectiveList);
346             }
347         }
348
349     }
350
351     /**
352      * Phase 3 of Knuth algorithm: Adds the areas
353      * @param alg PageBreakingAlgorithm instance which determined the breaks
354      * @param partCount number of parts (pages) to be rendered
355      * @param originalList original Knuth element list
356      * @param effectiveList effective Knuth element list (after adjustments)
357      */

358     protected abstract void doPhase3(PageBreakingAlgorithm alg, int partCount,
359             BlockSequence originalList, BlockSequence effectiveList);
360     
361     /**
362      * Phase 3 of Knuth algorithm: Adds the areas
363      * @param alg PageBreakingAlgorithm instance which determined the breaks
364      * @param partCount number of parts (pages) to be rendered
365      * @param originalList original Knuth element list
366      * @param effectiveList effective Knuth element list (after adjustments)
367      */

368     protected void addAreas(PageBreakingAlgorithm alg, int partCount,
369             BlockSequence originalList, BlockSequence effectiveList) {
370         addAreas(alg, 0, partCount, originalList, effectiveList);
371     }
372     
373     /**
374      * Phase 3 of Knuth algorithm: Adds the areas
375      * @param alg PageBreakingAlgorithm instance which determined the breaks
376      * @param startPart index of the first part (page) to be rendered
377      * @param partCount number of parts (pages) to be rendered
378      * @param originalList original Knuth element list
379      * @param effectiveList effective Knuth element list (after adjustments)
380      */

381     protected void addAreas(PageBreakingAlgorithm alg, int startPart, int partCount,
382             BlockSequence originalList, BlockSequence effectiveList) {
383         LayoutContext childLC;
384         // add areas
385
ListIterator JavaDoc effectiveListIterator = effectiveList.listIterator();
386         int startElementIndex = 0;
387         int endElementIndex = 0;
388         int lastBreak = -1;
389         for (int p = startPart; p < startPart + partCount; p++) {
390             PageBreakPosition pbp = (PageBreakPosition) alg.getPageBreaks().get(p);
391
392             //Check the last break position for forced breaks
393
int lastBreakClass;
394             if (p == 0) {
395                 lastBreakClass = effectiveList.getStartOn();
396             } else {
397                 ListElement lastBreakElement = effectiveList.getElement(endElementIndex);
398                 if (lastBreakElement.isPenalty()) {
399                     KnuthPenalty pen = (KnuthPenalty)lastBreakElement;
400                     lastBreakClass = pen.getBreakClass();
401                 } else {
402                     lastBreakClass = Constants.EN_COLUMN;
403                 }
404             }
405             
406             //the end of the new part
407
endElementIndex = pbp.getLeafPos();
408
409             // ignore the first elements added by the
410
// PageSequenceLayoutManager
411
startElementIndex += (startElementIndex == 0)
412                     ? effectiveList.ignoreAtStart
413                     : 0;
414
415             log.debug("PLM> part: " + (p + 1)
416                     + ", start at pos " + startElementIndex
417                     + ", break at pos " + endElementIndex
418                     + ", break class = " + lastBreakClass);
419
420             startPart(effectiveList, lastBreakClass);
421             
422             int displayAlign = getCurrentDisplayAlign();
423             
424             //The following is needed by SpaceResolver.performConditionalsNotification()
425
//further down as there may be important Position elements in the element list trailer
426
int notificationEndElementIndex = endElementIndex;
427
428             // ignore the last elements added by the
429
// PageSequenceLayoutManager
430
endElementIndex -= (endElementIndex == (originalList.size() - 1))
431                     ? effectiveList.ignoreAtEnd
432                     : 0;
433
434             // ignore the last element in the page if it is a KnuthGlue
435
// object
436
if (((KnuthElement) effectiveList.get(endElementIndex))
437                     .isGlue()) {
438                 endElementIndex--;
439             }
440
441             // ignore KnuthGlue and KnuthPenalty objects
442
// at the beginning of the line
443
effectiveListIterator = effectiveList
444                     .listIterator(startElementIndex);
445             KnuthElement firstElement;
446             while (effectiveListIterator.hasNext()
447                     && !(firstElement = (KnuthElement) effectiveListIterator.next())
448                             .isBox()) {
449                 /*
450                 if (firstElement.isGlue() && firstElement.getLayoutManager() != null) {
451                     // discard the space representd by the glue element
452                     ((BlockLevelLayoutManager) firstElement
453                             .getLayoutManager())
454                             .discardSpace((KnuthGlue) firstElement);
455                 }*/

456                 startElementIndex++;
457             }
458
459             if (startElementIndex <= endElementIndex) {
460                 if (log.isDebugEnabled()) {
461                     log.debug(" addAreas from " + startElementIndex
462                             + " to " + endElementIndex);
463                 }
464                 childLC = new LayoutContext(0);
465                 // set the space adjustment ratio
466
childLC.setSpaceAdjust(pbp.bpdAdjust);
467                 // add space before if display-align is center or bottom
468
// add space after if display-align is distribute and
469
// this is not the last page
470
if (pbp.difference != 0 && displayAlign == Constants.EN_CENTER) {
471                     childLC.setSpaceBefore(pbp.difference / 2);
472                 } else if (pbp.difference != 0 && displayAlign == Constants.EN_AFTER) {
473                     childLC.setSpaceBefore(pbp.difference);
474                 } else if (pbp.difference != 0 && displayAlign == Constants.EN_X_DISTRIBUTE
475                         && p < (partCount - 1)) {
476                     // count the boxes whose width is not 0
477
int boxCount = 0;
478                     effectiveListIterator = effectiveList
479                             .listIterator(startElementIndex);
480                     while (effectiveListIterator.nextIndex() <= endElementIndex) {
481                         KnuthElement tempEl = (KnuthElement)effectiveListIterator.next();
482                         if (tempEl.isBox() && tempEl.getW() > 0) {
483                             boxCount++;
484                         }
485                     }
486                     // split the difference
487
if (boxCount >= 2) {
488                         childLC.setSpaceAfter(pbp.difference / (boxCount - 1));
489                     }
490                 }
491
492                 /* *** *** non-standard extension *** *** */
493                 if (displayAlign == Constants.EN_X_FILL) {
494                     int averageLineLength = optimizeLineLength(effectiveList,
495                             startElementIndex, endElementIndex);
496                     if (averageLineLength != 0) {
497                         childLC.setStackLimit(new MinOptMax(averageLineLength));
498                     }
499                 }
500                 /* *** *** non-standard extension *** *** */
501
502                 // Handle SpaceHandling(Break)Positions, see SpaceResolver!
503
SpaceResolver.performConditionalsNotification(effectiveList,
504                         startElementIndex, notificationEndElementIndex, lastBreak);
505                 
506                 // Add areas now!
507
addAreas(new KnuthPossPosIter(effectiveList,
508                         startElementIndex, endElementIndex + 1), childLC);
509             } else {
510                 //no content for this part
511
handleEmptyContent();
512             }
513
514             finishPart(alg, pbp);
515
516             lastBreak = endElementIndex;
517             startElementIndex = pbp.getLeafPos() + 1;
518         }
519     }
520     /**
521      * Notifies the layout managers about the space and conditional length situation based on
522      * the break decisions.
523      * @param effectiveList Element list to be painted
524      * @param startElementIndex start index of the part
525      * @param endElementIndex end index of the part
526      * @param lastBreak index of the last break element
527      */

528     /**
529      * Handles span changes reported through the <code>LayoutContext</code>.
530      * Only used by the PSLM and called by <code>getNextBlockList()</code>.
531      * @param childLC the LayoutContext
532      * @param nextSequenceStartsOn previous value for break handling
533      * @return effective value for break handling
534      */

535     protected int handleSpanChange(LayoutContext childLC, int nextSequenceStartsOn) {
536         return nextSequenceStartsOn;
537     }
538     /**
539      * Gets the next block list (sequence) and adds it to a list of block lists if it's not empty.
540      * @param childLC LayoutContext to use
541      * @param nextSequenceStartsOn indicates on what page the next sequence should start
542      * @param blockLists list of block lists (sequences)
543      * @return the page on which the next content should appear after a hard break
544      */

545     protected int getNextBlockList(LayoutContext childLC,
546             int nextSequenceStartsOn,
547             List JavaDoc blockLists) {
548         updateLayoutContext(childLC);
549         //Make sure the span change signal is reset
550
childLC.signalSpanChange(Constants.NOT_SET);
551         
552         LinkedList JavaDoc returnedList;
553         BlockSequence blockList;
554         if ((returnedList = getNextKnuthElements(childLC, alignment)) != null) {
555             if (returnedList.size() == 0) {
556                 nextSequenceStartsOn = handleSpanChange(childLC, nextSequenceStartsOn);
557                 return nextSequenceStartsOn;
558             }
559             blockList = new BlockSequence(nextSequenceStartsOn, getCurrentDisplayAlign());
560             
561             //Only implemented by the PSLM
562
nextSequenceStartsOn = handleSpanChange(childLC, nextSequenceStartsOn);
563             
564             Position breakPosition = null;
565             if (((KnuthElement) returnedList.getLast()).isPenalty()
566                     && ((KnuthPenalty) returnedList.getLast()).getP() == -KnuthElement.INFINITE) {
567                 KnuthPenalty breakPenalty = (KnuthPenalty) returnedList
568                         .removeLast();
569                 breakPosition = breakPenalty.getPosition();
570                 switch (breakPenalty.getBreakClass()) {
571                 case Constants.EN_PAGE:
572                     log.debug("PLM> break - PAGE");
573                     nextSequenceStartsOn = Constants.EN_ANY;
574                     break;
575                 case Constants.EN_COLUMN:
576                     log.debug("PLM> break - COLUMN");
577                     //TODO Fix this when implementing multi-column layout
578
nextSequenceStartsOn = Constants.EN_COLUMN;
579                     break;
580                 case Constants.EN_ODD_PAGE:
581                     log.debug("PLM> break - ODD PAGE");
582                     nextSequenceStartsOn = Constants.EN_ODD_PAGE;
583                     break;
584                 case Constants.EN_EVEN_PAGE:
585                     log.debug("PLM> break - EVEN PAGE");
586                     nextSequenceStartsOn = Constants.EN_EVEN_PAGE;
587                     break;
588                 default:
589                     throw new IllegalStateException JavaDoc("Invalid break class: "
590                             + breakPenalty.getBreakClass());
591                 }
592             }
593             blockList.addAll(returnedList);
594             BlockSequence seq = null;
595             seq = blockList.endBlockSequence(breakPosition);
596             if (seq != null) {
597                 blockLists.add(seq);
598             }
599         }
600         return nextSequenceStartsOn;
601     }
602
603     /**
604      * Returns the average width of all the lines in the given range.
605      * @param effectiveList effective block list to work on
606      * @param startElementIndex
607      * @param endElementIndex
608      * @return the average line length, 0 if there's no content
609      */

610     private int optimizeLineLength(KnuthSequence effectiveList, int startElementIndex, int endElementIndex) {
611         ListIterator JavaDoc effectiveListIterator;
612         // optimize line length
613
int boxCount = 0;
614         int accumulatedLineLength = 0;
615         int greatestMinimumLength = 0;
616         effectiveListIterator = effectiveList
617                 .listIterator(startElementIndex);
618         while (effectiveListIterator.nextIndex() <= endElementIndex) {
619             KnuthElement tempEl = (KnuthElement) effectiveListIterator
620                     .next();
621             if (tempEl instanceof KnuthBlockBox) {
622                 KnuthBlockBox blockBox = (KnuthBlockBox) tempEl;
623                 if (blockBox.getBPD() > 0) {
624                     log.debug("PSLM> nominal length of line = " + blockBox.getBPD());
625                     log.debug(" range = "
626                             + blockBox.getIPDRange());
627                     boxCount++;
628                     accumulatedLineLength += ((KnuthBlockBox) tempEl)
629                             .getBPD();
630                 }
631                 if (blockBox.getIPDRange().min > greatestMinimumLength) {
632                     greatestMinimumLength = blockBox
633                             .getIPDRange().min;
634                 }
635             }
636         }
637         int averageLineLength = 0;
638         if (accumulatedLineLength > 0 && boxCount > 0) {
639             averageLineLength = (int) (accumulatedLineLength / boxCount);
640             log.debug("Average line length = " + averageLineLength);
641             if (averageLineLength < greatestMinimumLength) {
642                 averageLineLength = greatestMinimumLength;
643                 log.debug(" Correction to: " + averageLineLength);
644             }
645         }
646         return averageLineLength;
647     }
648
649     /**
650      * Justifies the boxes and returns them as a new KnuthSequence.
651      * @param blockList block list to justify
652      * @param alg reference to the algorithm instance
653      * @param availableBPD the available BPD
654      * @return the effective list
655      */

656     private BlockSequence justifyBoxes(BlockSequence blockList, PageBreakingAlgorithm alg, int availableBPD) {
657         int iOptPageNumber;
658         alg.setConstantLineWidth(availableBPD);
659         iOptPageNumber = alg.findBreakingPoints(blockList, /*availableBPD,*/
660                 1, true, BreakingAlgorithm.ALL_BREAKS);
661         log.debug("PLM> iOptPageNumber= " + iOptPageNumber);
662
663         //
664
ListIterator JavaDoc sequenceIterator = blockList.listIterator();
665         ListIterator JavaDoc breakIterator = alg.getPageBreaks().listIterator();
666         KnuthElement thisElement = null;
667         PageBreakPosition thisBreak;
668         int accumulatedS; // accumulated stretch or shrink
669
int adjustedDiff; // difference already adjusted
670
int firstElementIndex;
671
672         while (breakIterator.hasNext()) {
673             thisBreak = (PageBreakPosition) breakIterator.next();
674             if (log.isDebugEnabled()) {
675                 log.debug("| first page: break= "
676                         + thisBreak.getLeafPos() + " difference= "
677                         + thisBreak.difference + " ratio= "
678                         + thisBreak.bpdAdjust);
679             }
680             accumulatedS = 0;
681             adjustedDiff = 0;
682
683             // glue and penalty items at the beginning of the page must
684
// be ignored:
685
// the first element returned by sequenceIterator.next()
686
// inside the
687
// while loop must be a box
688
KnuthElement firstElement;
689             while (!(firstElement = (KnuthElement) sequenceIterator
690                     .next()).isBox()) {
691                 //
692
log.debug("PLM> ignoring glue or penalty element "
693                         + "at the beginning of the sequence");
694                 if (firstElement.isGlue()) {
695                     ((BlockLevelLayoutManager) firstElement
696                             .getLayoutManager())
697                             .discardSpace((KnuthGlue) firstElement);
698                 }
699             }
700             firstElementIndex = sequenceIterator.previousIndex();
701             sequenceIterator.previous();
702
703             // scan the sub-sequence representing a page,
704
// collecting information about potential adjustments
705
MinOptMax lineNumberMaxAdjustment = new MinOptMax(0);
706             MinOptMax spaceMaxAdjustment = new MinOptMax(0);
707             double spaceAdjustmentRatio = 0.0;
708             LinkedList JavaDoc blockSpacesList = new LinkedList JavaDoc();
709             LinkedList JavaDoc unconfirmedList = new LinkedList JavaDoc();
710             LinkedList JavaDoc adjustableLinesList = new LinkedList JavaDoc();
711             boolean bBoxSeen = false;
712             while (sequenceIterator.hasNext()
713                     && sequenceIterator.nextIndex() <= thisBreak
714                             .getLeafPos()) {
715                 thisElement = (KnuthElement) sequenceIterator.next();
716                 if (thisElement.isGlue()) {
717                     // glue elements are used to represent adjustable
718
// lines
719
// and adjustable spaces between blocks
720
switch (((KnuthGlue) thisElement)
721                             .getAdjustmentClass()) {
722                     case BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT:
723                     // fall through
724
case BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT:
725                         // potential space adjustment
726
// glue items before the first box or after the
727
// last one
728
// must be ignored
729
unconfirmedList.add(thisElement);
730                         break;
731                     case BlockLevelLayoutManager.LINE_NUMBER_ADJUSTMENT:
732                         // potential line number adjustment
733
lineNumberMaxAdjustment.max += ((KnuthGlue) thisElement)
734                                 .getY();
735                         lineNumberMaxAdjustment.min -= ((KnuthGlue) thisElement)
736                                 .getZ();
737                         adjustableLinesList.add(thisElement);
738                         break;
739                     case BlockLevelLayoutManager.LINE_HEIGHT_ADJUSTMENT:
740                         // potential line height adjustment
741
break;
742                     default:
743                     // nothing
744
}
745                 } else if (thisElement.isBox()) {
746                     if (!bBoxSeen) {
747                         // this is the first box met in this page
748
bBoxSeen = true;
749                     } else if (unconfirmedList.size() > 0) {
750                         // glue items in unconfirmedList were not after
751
// the last box
752
// in this page; they must be added to
753
// blockSpaceList
754
while (unconfirmedList.size() > 0) {
755                             KnuthGlue blockSpace = (KnuthGlue) unconfirmedList
756                                     .removeFirst();
757                             spaceMaxAdjustment.max += ((KnuthGlue) blockSpace)
758                                     .getY();
759                             spaceMaxAdjustment.min -= ((KnuthGlue) blockSpace)
760                                     .getZ();
761                             blockSpacesList.add(blockSpace);
762                         }
763                     }
764                 }
765             }
766             log.debug("| line number adj= "
767                     + lineNumberMaxAdjustment);
768             log.debug("| space adj = "
769                     + spaceMaxAdjustment);
770
771             if (thisElement.isPenalty() && thisElement.getW() > 0) {
772                 log.debug(" mandatory variation to the number of lines!");
773                 ((BlockLevelLayoutManager) thisElement
774                         .getLayoutManager()).negotiateBPDAdjustment(
775                         thisElement.getW(), thisElement);
776             }
777
778             if (thisBreak.bpdAdjust != 0
779                     && (thisBreak.difference > 0 && thisBreak.difference <= spaceMaxAdjustment.max)
780                     || (thisBreak.difference < 0 && thisBreak.difference >= spaceMaxAdjustment.min)) {
781                 // modify only the spaces between blocks
782
spaceAdjustmentRatio = ((double) thisBreak.difference / (thisBreak.difference > 0 ? spaceMaxAdjustment.max
783                         : spaceMaxAdjustment.min));
784                 adjustedDiff += adjustBlockSpaces(
785                         blockSpacesList,
786                         thisBreak.difference,
787                         (thisBreak.difference > 0 ? spaceMaxAdjustment.max
788                                 : -spaceMaxAdjustment.min));
789                 log.debug("single space: "
790                         + (adjustedDiff == thisBreak.difference
791                                 || thisBreak.bpdAdjust == 0 ? "ok"
792                                 : "ERROR"));
793             } else if (thisBreak.bpdAdjust != 0) {
794                 adjustedDiff += adjustLineNumbers(
795                         adjustableLinesList,
796                         thisBreak.difference,
797                         (thisBreak.difference > 0 ? lineNumberMaxAdjustment.max
798                                 : -lineNumberMaxAdjustment.min));
799                 adjustedDiff += adjustBlockSpaces(
800                         blockSpacesList,
801                         thisBreak.difference - adjustedDiff,
802                         ((thisBreak.difference - adjustedDiff) > 0 ? spaceMaxAdjustment.max
803                                 : -spaceMaxAdjustment.min));
804                 log.debug("lines and space: "
805                         + (adjustedDiff == thisBreak.difference
806                                 || thisBreak.bpdAdjust == 0 ? "ok"
807                                 : "ERROR"));
808
809             }
810         }
811
812         // create a new sequence: the new elements will contain the
813
// Positions
814
// which will be used in the addAreas() phase
815
BlockSequence effectiveList = new BlockSequence(blockList.getStartOn(),
816                                                 blockList.getDisplayAlign());
817         effectiveList.addAll(getCurrentChildLM().getChangedKnuthElements(
818                 blockList.subList(0, blockList.size() - blockList.ignoreAtEnd),
819                 /* 0, */0));
820         //effectiveList.add(new KnuthPenalty(0, -KnuthElement.INFINITE,
821
// false, new Position(this), false));
822
effectiveList.endSequence();
823
824         ElementListObserver.observe(effectiveList, "breaker-effective", null);
825
826         alg.getPageBreaks().clear(); //Why this?
827
return effectiveList;
828     }
829
830     private int adjustBlockSpaces(LinkedList JavaDoc spaceList, int difference, int total) {
831         if (log.isDebugEnabled()) {
832             log.debug("AdjustBlockSpaces: difference " + difference + " / " + total
833                     + " on " + spaceList.size() + " spaces in block");
834         }
835         ListIterator JavaDoc spaceListIterator = spaceList.listIterator();
836         int adjustedDiff = 0;
837         int partial = 0;
838         while (spaceListIterator.hasNext()) {
839             KnuthGlue blockSpace = (KnuthGlue)spaceListIterator.next();
840             partial += (difference > 0 ? blockSpace.getY() : blockSpace.getZ());
841             if (log.isDebugEnabled()) {
842                 log.debug("available = " + partial + " / " + total);
843                 log.debug("competenza = "
844                         + (((int)((float) partial * difference / total)) - adjustedDiff)
845                         + " / " + difference);
846             }
847             int newAdjust = ((BlockLevelLayoutManager) blockSpace.getLayoutManager()).negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, blockSpace);
848             adjustedDiff += newAdjust;
849         }
850         return adjustedDiff;
851     }
852
853     private int adjustLineNumbers(LinkedList JavaDoc lineList, int difference, int total) {
854         if (log.isDebugEnabled()) {
855             log.debug("AdjustLineNumbers: difference " + difference + " / " + total + " on " + lineList.size() + " elements");
856         }
857
858 // int adjustedDiff = 0;
859
// int partial = 0;
860
// KnuthGlue prevLine = null;
861
// KnuthGlue currLine = null;
862
// ListIterator lineListIterator = lineList.listIterator();
863
// while (lineListIterator.hasNext()) {
864
// currLine = (KnuthGlue)lineListIterator.next();
865
// if (prevLine != null
866
// && prevLine.getLayoutManager() != currLine.getLayoutManager()) {
867
// int newAdjust = ((BlockLevelLayoutManager) prevLine.getLayoutManager())
868
// .negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, prevLine);
869
// adjustedDiff += newAdjust;
870
// }
871
// partial += (difference > 0 ? currLine.getY() : currLine.getZ());
872
// prevLine = currLine;
873
// }
874
// if (currLine != null) {
875
// int newAdjust = ((BlockLevelLayoutManager) currLine.getLayoutManager())
876
// .negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, currLine);
877
// adjustedDiff += newAdjust;
878
// }
879
// return adjustedDiff;
880

881         ListIterator JavaDoc lineListIterator = lineList.listIterator();
882         int adjustedDiff = 0;
883         int partial = 0;
884         while (lineListIterator.hasNext()) {
885             KnuthGlue line = (KnuthGlue)lineListIterator.next();
886             partial += (difference > 0 ? line.getY() : line.getZ());
887             int newAdjust = ((BlockLevelLayoutManager) line.getLayoutManager()).negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, line);
888             adjustedDiff += newAdjust;
889         }
890         return adjustedDiff;
891     }
892     
893 }
894
Popular Tags