KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > fop > layoutmgr > inline > LineLayoutManager


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: LineLayoutManager.java 453899 2006-10-07 13:17:57Z jeremias $ */
19
20 package org.apache.fop.layoutmgr.inline;
21
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24 import org.apache.fop.datatypes.Length;
25 import org.apache.fop.datatypes.Numeric;
26 import org.apache.fop.fo.Constants;
27 import org.apache.fop.fo.FONode;
28 import org.apache.fop.fo.flow.Block;
29 import org.apache.fop.fo.properties.CommonHyphenation;
30 import org.apache.fop.hyphenation.Hyphenation;
31 import org.apache.fop.hyphenation.Hyphenator;
32 import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
33 import org.apache.fop.layoutmgr.BreakElement;
34 import org.apache.fop.layoutmgr.BreakingAlgorithm;
35 import org.apache.fop.layoutmgr.ElementListObserver;
36 import org.apache.fop.layoutmgr.InlineKnuthSequence;
37 import org.apache.fop.layoutmgr.KnuthBlockBox;
38 import org.apache.fop.layoutmgr.KnuthBox;
39 import org.apache.fop.layoutmgr.KnuthElement;
40 import org.apache.fop.layoutmgr.KnuthGlue;
41 import org.apache.fop.layoutmgr.KnuthPenalty;
42 import org.apache.fop.layoutmgr.KnuthPossPosIter;
43 import org.apache.fop.layoutmgr.KnuthSequence;
44 import org.apache.fop.layoutmgr.LayoutContext;
45 import org.apache.fop.layoutmgr.LayoutManager;
46 import org.apache.fop.layoutmgr.LeafPosition;
47 import org.apache.fop.layoutmgr.ListElement;
48 import org.apache.fop.layoutmgr.NonLeafPosition;
49 import org.apache.fop.layoutmgr.Position;
50 import org.apache.fop.layoutmgr.PositionIterator;
51 import org.apache.fop.layoutmgr.SpaceSpecifier;
52 import org.apache.fop.area.Area;
53 import org.apache.fop.area.LineArea;
54 import org.apache.fop.area.inline.InlineArea;
55
56 import java.util.ListIterator JavaDoc;
57 import java.util.List JavaDoc;
58 import java.util.ArrayList JavaDoc;
59 import java.util.LinkedList JavaDoc;
60 import org.apache.fop.area.Trait;
61 import org.apache.fop.fonts.Font;
62
63 import org.apache.fop.traits.MinOptMax;
64
65 /**
66  * LayoutManager for lines. It builds one or more lines containing
67  * inline areas generated by its sub layout managers.
68  * A break is found for each line which may contain one of more
69  * breaks from the child layout managers.
70  * Once a break is found then it is return for the parent layout
71  * manager to handle.
72  * When the areas are being added to the page this manager
73  * creates a line area to contain the inline areas added by the
74  * child layout managers.
75  */

76 public class LineLayoutManager extends InlineStackingLayoutManager
77                                implements BlockLevelLayoutManager {
78
79     /**
80      * logging instance
81      */

82     private static Log log = LogFactory.getLog(LineLayoutManager.class);
83
84     private Block fobj;
85     private boolean isFirstInBlock;
86     
87     /** @see org.apache.fop.layoutmgr.LayoutManager#initialize() */
88     public void initialize() {
89         textAlignment = fobj.getTextAlign();
90         textAlignmentLast = fobj.getTextAlignLast();
91         textIndent = fobj.getTextIndent();
92         lastLineEndIndent = fobj.getLastLineEndIndent();
93         hyphenationProperties = fobj.getCommonHyphenation();
94         hyphenationLadderCount = fobj.getHyphenationLadderCount();
95         wrapOption = fobj.getWrapOption();
96         whiteSpaceTreament = fobj.getWhitespaceTreatment();
97         //
98
effectiveAlignment = getEffectiveAlignment(textAlignment, textAlignmentLast);
99         isFirstInBlock = (this == getParent().getChildLMs().get(0));
100     }
101
102     private int getEffectiveAlignment(int alignment, int alignmentLast) {
103         if (textAlignment != EN_JUSTIFY && textAlignmentLast == EN_JUSTIFY) {
104             return 0;
105         } else {
106             return textAlignment;
107         }
108     }
109     
110     /**
111      * Private class to store information about inline breaks.
112      * Each value holds the start and end indexes into a List of
113      * inline break positions.
114      */

115     private static class LineBreakPosition extends LeafPosition {
116         private int iParIndex; // index of the Paragraph this Position refers to
117
private int iStartIndex; //index of the first element this Position refers to
118
private int availableShrink;
119         private int availableStretch;
120         private int difference;
121         private double dAdjust; // Percentage to adjust (stretch or shrink)
122
private double ipdAdjust; // Percentage to adjust (stretch or shrink)
123
private int startIndent;
124         private int lineHeight;
125         private int lineWidth;
126         private int spaceBefore;
127         private int spaceAfter;
128         private int baseline;
129
130         LineBreakPosition(LayoutManager lm, int index, int iStartIndex, int iBreakIndex,
131                           int shrink, int stretch, int diff,
132                           double ipdA, double adjust, int ind,
133                           int lh, int lw, int sb, int sa, int bl) {
134             super(lm, iBreakIndex);
135             availableShrink = shrink;
136             availableStretch = stretch;
137             difference = diff;
138             iParIndex = index;
139             this.iStartIndex = iStartIndex;
140             ipdAdjust = ipdA;
141             dAdjust = adjust;
142             startIndent = ind;
143             lineHeight = lh;
144             lineWidth = lw;
145             spaceBefore = sb;
146             spaceAfter = sa;
147             baseline = bl;
148         }
149         
150     }
151
152
153     private int textAlignment = EN_JUSTIFY;
154     private int textAlignmentLast;
155     private int effectiveAlignment;
156     private Length textIndent;
157     private Length lastLineEndIndent;
158     private CommonHyphenation hyphenationProperties;
159     private Numeric hyphenationLadderCount;
160     private int wrapOption = EN_WRAP;
161     private int whiteSpaceTreament;
162     //private LayoutProps layoutProps;
163

164     private Length lineHeight;
165     private int lead;
166     private int follow;
167     private AlignmentContext alignmentContext = null;
168
169     private List JavaDoc knuthParagraphs = null;
170     private int iReturnedLBP = 0;
171
172     // parameters of Knuth's algorithm:
173
// penalty value for flagged penalties
174
private int flaggedPenalty = 50;
175
176     private LineLayoutPossibilities lineLayouts;
177     private List JavaDoc lineLayoutsList;
178     private int iLineWidth = 0;
179
180     /**
181      * this constant is used to create elements when text-align is center:
182      * every TextLM descendant of LineLM must use the same value,
183      * otherwise the line breaking algorithm does not find the right
184      * break point
185      */

186     public static final int DEFAULT_SPACE_WIDTH = 3336;
187
188
189     /**
190      * This class is used to remember
191      * which was the first element in the paragraph
192      * returned by each LM.
193      */

194     private class Update {
195         private InlineLevelLayoutManager inlineLM;
196         private int iFirstIndex;
197
198         public Update(InlineLevelLayoutManager lm, int index) {
199             inlineLM = lm;
200             iFirstIndex = index;
201         }
202     }
203
204     // this class represents a paragraph
205
private class Paragraph extends InlineKnuthSequence {
206         /** Number of elements to ignore at the beginning of the list. */
207         private int ignoreAtStart = 0;
208         /** Number of elements to ignore at the end of the list. */
209         private int ignoreAtEnd = 0;
210
211         // space at the end of the last line (in millipoints)
212
private MinOptMax lineFiller;
213         private int textAlignment;
214         private int textAlignmentLast;
215         private int textIndent;
216         private int lastLineEndIndent;
217         private int lineWidth;
218         // the LM which created the paragraph
219
private LineLayoutManager layoutManager;
220
221         public Paragraph(LineLayoutManager llm, int alignment, int alignmentLast,
222                          int indent, int endIndent) {
223             super();
224             layoutManager = llm;
225             textAlignment = alignment;
226             textAlignmentLast = alignmentLast;
227             textIndent = indent;
228             lastLineEndIndent = endIndent;
229         }
230
231         public void startParagraph(int lw) {
232             lineWidth = lw;
233             startSequence();
234         }
235
236         public void startSequence() {
237             // set the minimum amount of empty space at the end of the
238
// last line
239
if (textAlignment == EN_CENTER) {
240                 lineFiller = new MinOptMax(lastLineEndIndent);
241             } else {
242                 lineFiller = new MinOptMax(lastLineEndIndent, lastLineEndIndent, lineWidth);
243             }
244
245             // add auxiliary elements at the beginning of the paragraph
246
if (textAlignment == EN_CENTER && textAlignmentLast != EN_JUSTIFY) {
247                 this.add(new KnuthGlue(0, 3 * DEFAULT_SPACE_WIDTH, 0,
248                                        null, false));
249                 ignoreAtStart++;
250             }
251
252             // add the element representing text indentation
253
// at the beginning of the first paragraph
254
if (isFirstInBlock && knuthParagraphs.size() == 0
255                         && textIndent != 0) {
256                 this.add(new KnuthInlineBox(textIndent, null,
257                                       null, false));
258                 ignoreAtStart++;
259             }
260         }
261
262         public void endParagraph() {
263             KnuthSequence finishedPar = this.endSequence();
264             if (finishedPar != null) {
265                 knuthParagraphs.add(finishedPar);
266             }
267         }
268
269         public KnuthSequence endSequence() {
270             if (this.size() > ignoreAtStart) {
271                 if (textAlignment == EN_CENTER
272                     && textAlignmentLast != EN_JUSTIFY) {
273                     this.add(new KnuthGlue(0, 3 * DEFAULT_SPACE_WIDTH, 0,
274                                            null, false));
275                     this.add(new KnuthPenalty(lineFiller.opt, -KnuthElement.INFINITE,
276                                               false, null, false));
277                     ignoreAtEnd = 2;
278                 } else if (textAlignmentLast != EN_JUSTIFY) {
279                     // add the elements representing the space
280
// at the end of the last line
281
// and the forced break
282
this.add(new KnuthPenalty(0, KnuthElement.INFINITE,
283                                               false, null, false));
284                     this.add(new KnuthGlue(0,
285                             lineFiller.max - lineFiller.opt,
286                             lineFiller.opt - lineFiller.min, null, false));
287                     this.add(new KnuthPenalty(lineFiller.opt, -KnuthElement.INFINITE,
288                                               false, null, false));
289                     ignoreAtEnd = 3;
290                 } else {
291                     // add only the element representing the forced break
292
this.add(new KnuthPenalty(lineFiller.opt, -KnuthElement.INFINITE,
293                                               false, null, false));
294                     ignoreAtEnd = 1;
295                 }
296                 return this;
297             } else {
298                 this.clear();
299                 return null;
300             }
301         }
302
303         /**
304          * @return true if the sequence contains a box
305          */

306         public boolean containsBox() {
307             for (int i = 0; i < this.size(); i++) {
308                 KnuthElement el = (KnuthElement)this.get(i);
309                 if (el.isBox()) {
310                     return true;
311                 }
312             }
313             return false;
314         }
315     }
316
317     private class LineBreakingAlgorithm extends BreakingAlgorithm {
318         private LineLayoutManager thisLLM;
319         private int pageAlignment;
320         private int activePossibility;
321         private int addedPositions;
322         private int textIndent;
323         private int fillerMinWidth;
324         private int lineHeight;
325         private int lead;
326         private int follow;
327         private int maxDiff;
328         private static final double MAX_DEMERITS = 10e6;
329
330         public LineBreakingAlgorithm (int pageAlign,
331                                       int textAlign, int textAlignLast,
332                                       int indent, int fillerWidth,
333                                       int lh, int ld, int fl, boolean first,
334                                       int maxFlagCount, LineLayoutManager llm) {
335             super(textAlign, textAlignLast, first, false, maxFlagCount);
336             pageAlignment = pageAlign;
337             textIndent = indent;
338             fillerMinWidth = fillerWidth;
339             lineHeight = lh;
340             lead = ld;
341             follow = fl;
342             thisLLM = llm;
343             activePossibility = -1;
344             maxDiff = fobj.getWidows() >= fobj.getOrphans()
345                     ? fobj.getWidows()
346                     : fobj.getOrphans();
347         }
348
349         public void updateData1(int lineCount, double demerits) {
350             lineLayouts.addPossibility(lineCount, demerits);
351             if (super.log.isTraceEnabled()) {
352                 super.log.trace(
353                         "Layout possibility in " + lineCount + " lines; break at position:");
354             }
355         }
356
357         public void updateData2(KnuthNode bestActiveNode,
358                                 KnuthSequence par,
359                                 int total) {
360             // compute indent and adjustment ratio, according to
361
// the value of text-align and text-align-last
362
int indent = 0;
363             int difference = bestActiveNode.difference;
364             int textAlign = (bestActiveNode.line < total) ? alignment : alignmentLast;
365             indent += (textAlign == Constants.EN_CENTER)
366                       ? difference / 2 : (textAlign == Constants.EN_END) ? difference : 0;
367             indent += (bestActiveNode.line == 1 && bFirst && isFirstInBlock) ? textIndent : 0;
368             double ratio = (textAlign == Constants.EN_JUSTIFY
369                 || difference < 0 && -difference <= bestActiveNode.availableShrink)
370                         ? bestActiveNode.adjustRatio : 0;
371
372             // add nodes at the beginning of the list, as they are found
373
// backwards, from the last one to the first one
374

375             // the first time this method is called, initialize activePossibility
376
if (activePossibility == -1) {
377                 activePossibility = 0;
378                 addedPositions = 0;
379             }
380
381             if (addedPositions == lineLayouts.getLineCount(activePossibility)) {
382                 activePossibility++;
383                 addedPositions = 0;
384             }
385
386             if (difference + bestActiveNode.availableShrink < 0) {
387                 if (super.log.isWarnEnabled()) {
388                     super.log.warn(FONode.decorateWithContextInfo(
389                             "Line " + (addedPositions + 1)
390                             + " of a paragraph overflows the available area.", getFObj()));
391                 }
392             }
393             
394             //log.debug("LLM> (" + (lineLayouts.getLineNumber(activePossibility) - addedPositions)
395
// + ") difference = " + difference + " ratio = " + ratio);
396
lineLayouts.addBreakPosition(makeLineBreakPosition(par,
397                    (bestActiveNode.line > 1 ? bestActiveNode.previous.position + 1 : 0),
398                    bestActiveNode.position,
399                    bestActiveNode.availableShrink - (addedPositions > 0
400                        ? 0 : ((Paragraph)par).lineFiller.opt - ((Paragraph)par).lineFiller.min),
401                    bestActiveNode.availableStretch,
402                    difference, ratio, indent), activePossibility);
403             addedPositions++;
404         }
405
406         /* reset activePossibility, as if breakpoints have not yet been computed
407          */

408         public void resetAlgorithm() {
409             activePossibility = -1;
410         }
411
412         private LineBreakPosition makeLineBreakPosition(KnuthSequence par,
413                                                         int firstElementIndex,
414                                                         int lastElementIndex,
415                                                         int availableShrink,
416                                                         int availableStretch,
417                                                         int difference,
418                                                         double ratio,
419                                                         int indent) {
420             // line height calculation - spaceBefore may differ from spaceAfter
421
// by 1mpt due to rounding
422
int spaceBefore = (lineHeight - lead - follow) / 2;
423             int spaceAfter = lineHeight - lead - follow - spaceBefore;
424             // height before the main baseline
425
int lineLead = lead;
426             // maximum follow
427
int lineFollow = follow;
428             // true if this line contains only zero-height, auxiliary boxes
429
// and the actual line width is 0; in this case, the line "collapses"
430
// i.e. the line area will have bpd = 0
431
boolean bZeroHeightLine = (difference == iLineWidth);
432
433             // if line-stacking-strategy is "font-height", the line height
434
// is not affected by its content
435
if (fobj.getLineStackingStrategy() != EN_FONT_HEIGHT) {
436                 ListIterator JavaDoc inlineIterator
437                     = par.listIterator(firstElementIndex);
438                 AlignmentContext lastAC = null;
439                 int maxIgnoredHeight = 0; // See spec 7.13
440
for (int j = firstElementIndex;
441                      j <= lastElementIndex;
442                      j++) {
443                     KnuthElement element = (KnuthElement) inlineIterator.next();
444                     if (element instanceof KnuthInlineBox ) {
445                         AlignmentContext ac = ((KnuthInlineBox) element).getAlignmentContext();
446                         if (ac != null && lastAC != ac) {
447                             if (!ac.usesInitialBaselineTable()
448                                 || ac.getAlignmentBaselineIdentifier() != EN_BEFORE_EDGE
449                                    && ac.getAlignmentBaselineIdentifier() != EN_AFTER_EDGE) {
450                                 int alignmentOffset = ac.getTotalAlignmentBaselineOffset();
451                                 if (alignmentOffset + ac.getAltitude() > lineLead) {
452                                     lineLead = alignmentOffset + ac.getAltitude();
453                                 }
454                                 if (ac.getDepth() - alignmentOffset > lineFollow) {
455                                     lineFollow = ac.getDepth() - alignmentOffset;
456                                 }
457                             } else {
458                                 if (ac.getHeight() > maxIgnoredHeight) {
459                                     maxIgnoredHeight = ac.getHeight();
460                                 }
461                             }
462                             lastAC = ac;
463                         }
464                         if (bZeroHeightLine
465                             && (!element.isAuxiliary() || ac != null && ac.getHeight() > 0)) {
466                             bZeroHeightLine = false;
467                         }
468                     }
469                 }
470
471                 if (lineFollow < maxIgnoredHeight - lineLead) {
472                     lineFollow = maxIgnoredHeight - lineLead;
473                 }
474             }
475
476             constantLineHeight = lineLead + lineFollow;
477
478             if (bZeroHeightLine) {
479                 return new LineBreakPosition(thisLLM,
480                                              knuthParagraphs.indexOf(par),
481                                              firstElementIndex, lastElementIndex,
482                                              availableShrink, availableStretch,
483                                              difference, ratio, 0, indent,
484                                              0, iLineWidth, 0, 0, 0);
485             } else {
486                 return new LineBreakPosition(thisLLM,
487                                              knuthParagraphs.indexOf(par),
488                                              firstElementIndex, lastElementIndex,
489                                              availableShrink, availableStretch,
490                                              difference, ratio, 0, indent,
491                                              lineLead + lineFollow,
492                                              iLineWidth, spaceBefore, spaceAfter,
493                                              lineLead);
494             }
495         }
496
497         public int findBreakingPoints(Paragraph par, /*int lineWidth,*/
498                                       double threshold, boolean force,
499                                       int allowedBreaks) {
500             return super.findBreakingPoints(par, /*lineWidth,*/
501                     threshold, force, allowedBreaks);
502         }
503
504         protected int filterActiveNodes() {
505             KnuthNode bestActiveNode = null;
506
507             if (pageAlignment == EN_JUSTIFY) {
508                 // leave all active nodes and find the optimum line number
509
//log.debug("LBA.filterActiveNodes> " + activeNodeCount + " layouts");
510
for (int i = startLine; i < endLine; i++) {
511                     for (KnuthNode node = getNode(i); node != null; node = node.next) {
512                         //log.debug(" + lines = " + node.line + " demerits = " + node.totalDemerits);
513
bestActiveNode = compareNodes(bestActiveNode, node);
514                     }
515                 }
516
517                 // scan the node set once again and remove some nodes
518
//log.debug("LBA.filterActiveList> layout selection");
519
for (int i = startLine; i < endLine; i++) {
520                     for (KnuthNode node = getNode(i); node != null; node = node.next) {
521                         //if (Math.abs(node.line - bestActiveNode.line) > maxDiff) {
522
//if (false) {
523
if (node.line != bestActiveNode.line
524                             && node.totalDemerits > MAX_DEMERITS) {
525                             //log.debug(" XXX lines = " + node.line + " demerits = " + node.totalDemerits);
526
removeNode(i, node);
527                         } else {
528                             //log.debug(" ok lines = " + node.line + " demerits = " + node.totalDemerits);
529
}
530                     }
531                 }
532             } else {
533                 // leave only the active node with fewest total demerits
534
for (int i = startLine; i < endLine; i++) {
535                     for (KnuthNode node = getNode(i); node != null; node = node.next) {
536                         bestActiveNode = compareNodes(bestActiveNode, node);
537                         if (node != bestActiveNode) {
538                             removeNode(i, node);
539                         }
540                     }
541                 }
542             }
543             return bestActiveNode.line;
544         }
545     }
546
547       
548     private int constantLineHeight = 12000;
549       
550
551     /**
552      * Create a new Line Layout Manager.
553      * This is used by the block layout manager to create
554      * line managers for handling inline areas flowing into line areas.
555      * @param block the block formatting object
556      * @param lh the default line height
557      * @param l the default lead, from top to baseline
558      * @param f the default follow, from baseline to bottom
559      */

560     public LineLayoutManager(Block block, Length lh, int l, int f) {
561         super(block);
562         fobj = block;
563         // the child FObj are owned by the parent BlockLM
564
// this LM has all its childLMs preloaded
565
fobjIter = null;
566         lineHeight = lh;
567         lead = l;
568         follow = f;
569     }
570
571     /** @see org.apache.fop.layoutmgr.LayoutManager */
572     public LinkedList JavaDoc getNextKnuthElements(LayoutContext context, int alignment) {
573         Font fs = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo(), this);
574         alignmentContext
575           = new AlignmentContext(fs, lineHeight.getValue(this), context.getWritingMode());
576         context.setAlignmentContext(alignmentContext);
577         // Get a break from currently active child LM
578
// Set up constraints for inline level managers
579

580         // IPD remaining in line
581
MinOptMax availIPD = context.getStackLimit();
582
583         clearPrevIPD();
584
585         //PHASE 1: Create Knuth elements
586
if (knuthParagraphs == null) {
587             // it's the first time this method is called
588
knuthParagraphs = new ArrayList JavaDoc();
589
590             // here starts Knuth's algorithm
591
//TODO availIPD should not really be used here, so we can later support custom line
592
//widths for for each line (side-floats, differing available IPD after page break)
593
collectInlineKnuthElements(context, availIPD);
594         } else {
595             // this method has been called before
596
// all line breaks are already calculated
597
}
598
599         // return finished when there's no content
600
if (knuthParagraphs.size() == 0) {
601             setFinished(true);
602             return null;
603         }
604
605         //PHASE 2: Create line breaks
606
return createLineBreaks(context.getBPAlignment(), context);
607         /*
608         LineBreakPosition lbp = null;
609         if (breakpoints == null) {
610             // find the optimal line breaking points for each paragraph
611             breakpoints = new ArrayList();
612             ListIterator paragraphsIterator
613                 = knuthParagraphs.listIterator(knuthParagraphs.size());
614             Paragraph currPar = null;
615             while (paragraphsIterator.hasPrevious()) {
616                 currPar = (Paragraph) paragraphsIterator.previous();
617                 findBreakingPoints(currPar, context.getStackLimit().opt);
618             }
619         }*/

620
621         //PHASE 3: Return lines
622

623         /*
624         // get a break point from the list
625         lbp = (LineBreakPosition) breakpoints.get(iReturnedLBP ++);
626         if (iReturnedLBP == breakpoints.size()) {
627             setFinished(true);
628         }
629
630         BreakPoss curLineBP = new BreakPoss(lbp);
631         curLineBP.setFlag(BreakPoss.ISLAST, isFinished());
632         curLineBP.setStackingSize(new MinOptMax(lbp.lineHeight));
633         return curLineBP;
634         */

635     }
636
637     /**
638      * Phase 1 of Knuth algorithm: Collect all inline Knuth elements before determining line breaks.
639      * @param context the LayoutContext
640      * @param availIPD available IPD for line (should be removed!)
641      */

642     private void collectInlineKnuthElements(LayoutContext context, MinOptMax availIPD) {
643         LayoutContext inlineLC = new LayoutContext(context);
644         
645         InlineLevelLayoutManager curLM;
646         LinkedList JavaDoc returnedList = null;
647         iLineWidth = context.getStackLimit().opt;
648         
649         // convert all the text in a sequence of paragraphs made
650
// of KnuthBox, KnuthGlue and KnuthPenalty objects
651
boolean bPrevWasKnuthBox = false;
652         
653         StringBuffer JavaDoc trace = new StringBuffer JavaDoc("LineLM:");
654         
655         Paragraph lastPar = null;
656         
657         while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) {
658             returnedList = curLM.getNextKnuthElements(inlineLC, effectiveAlignment);
659             if (returnedList == null) {
660                 // curLM returned null; this can happen
661
// if it has nothing more to layout,
662
// so just iterate once more to see
663
// if there are other children
664
continue;
665             }
666             if (returnedList.size() == 0) {
667                 continue;
668             }
669             
670             if (lastPar != null) {
671                 KnuthSequence firstSeq = (KnuthSequence) returnedList.getFirst();
672                 
673                 // finish last paragraph before a new block sequence
674
if (!firstSeq.isInlineSequence()) {
675                     lastPar.endParagraph();
676                     ElementListObserver.observe(lastPar, "line", null);
677                     lastPar = null;
678                     if (log.isTraceEnabled()) {
679                         trace.append(" ]");
680                     }
681                     bPrevWasKnuthBox = false;
682                 }
683                 
684                 // does the first element of the first paragraph add to an existing word?
685
if (lastPar != null) {
686                     KnuthElement thisElement;
687                     thisElement = (KnuthElement) firstSeq.get(0);
688                     if (thisElement.isBox() && !thisElement.isAuxiliary()
689                             && bPrevWasKnuthBox) {
690                         lastPar.addALetterSpace();
691                     }
692                 }
693             }
694             
695             // loop over the KnuthSequences (and single KnuthElements) in returnedList
696
ListIterator JavaDoc iter = returnedList.listIterator();
697             while (iter.hasNext()) {
698                 KnuthSequence sequence = (KnuthSequence) iter.next();
699                 // the sequence contains inline Knuth elements
700
if (sequence.isInlineSequence()) {
701                     // look at the last element
702
ListElement lastElement;
703                     lastElement = sequence.getLast();
704                     if (lastElement == null) {
705                         throw new NullPointerException JavaDoc(
706                         "Sequence was empty! lastElement is null");
707                     }
708                     bPrevWasKnuthBox = lastElement.isBox() && ((KnuthElement) lastElement).getW() != 0;
709                     
710                     // if last paragraph is open, add the new elements to the paragraph
711
// else this is the last paragraph
712
if (lastPar == null) {
713                         lastPar = new Paragraph(this,
714                                                 textAlignment, textAlignmentLast,
715                                                 textIndent.getValue(this),
716                                                 lastLineEndIndent.getValue(this));
717                         lastPar.startParagraph(availIPD.opt);
718                         if (log.isTraceEnabled()) {
719                             trace.append(" [");
720                         }
721                     } else {
722                         if (log.isTraceEnabled()) {
723                             trace.append(" +");
724                         }
725                     }
726                     lastPar.addAll(sequence);
727                     if (log.isTraceEnabled()) {
728                         trace.append(" I");
729                     }
730                     
731                     // finish last paragraph if it was closed with a linefeed
732
if (lastElement.isPenalty()
733                             && ((KnuthPenalty) lastElement).getP()
734                             == -KnuthPenalty.INFINITE) {
735                         // a penalty item whose value is -inf
736
// represents a preserved linefeed,
737
// which forces a line break
738
lastPar.removeLast();
739                         if (!lastPar.containsBox()) {
740                             //only a forced linefeed on this line
741
//-> compensate with a zero width box
742
lastPar.add(new KnuthInlineBox(0, null, null, false));
743                         }
744                         lastPar.endParagraph();
745                         ElementListObserver.observe(lastPar, "line", null);
746                         lastPar = null;
747                         if (log.isTraceEnabled()) {
748                             trace.append(" ]");
749                         }
750                         bPrevWasKnuthBox = false;
751                     }
752                 } else { // the sequence is a block sequence
753
// the positions will be wrapped with this LM in postProcessLineBreaks
754
knuthParagraphs.add(sequence);
755                     if (log.isTraceEnabled()) {
756                         trace.append(" B");
757                     }
758                 }
759             } // end of loop over returnedList
760
}
761         if (lastPar != null) {
762             lastPar.endParagraph();
763             ElementListObserver.observe(lastPar, "line", fobj.getId());
764             if (log.isTraceEnabled()) {
765                 trace.append(" ]");
766             }
767         }
768         log.trace(trace);
769     }
770     
771     /**
772      * Find a set of breaking points.
773      * This method is called only once by getNextBreakPoss, and it
774      * subsequently calls the other findBreakingPoints() method with
775      * different parameters, until a set of breaking points is found.
776      *
777      * @param par the list of elements that must be parted
778      * into lines
779      * @param lineWidth the desired length ot the lines
780      */

781     /*
782     private void findBreakingPoints(Paragraph par, int lineWidth) {
783         // maximum adjustment ratio permitted
784         float maxAdjustment = 1;
785
786         // first try
787         if (!findBreakingPoints(par, lineWidth, maxAdjustment, false)) {
788             // the first try failed, now try something different
789             log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment);
790             if (hyphenationProperties.hyphenate == Constants.EN_TRUE) {
791                 // consider every hyphenation point as a legal break
792                 findHyphenationPoints(par);
793             } else {
794                 // try with a higher threshold
795                 maxAdjustment = 5;
796             }
797
798             if (!findBreakingPoints(par, lineWidth, maxAdjustment, false)) {
799                 // the second try failed too, try with a huge threshold;
800                 // if this fails too, use a different algorithm
801                 log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment
802                           + (hyphenationProperties.hyphenate == Constants.EN_TRUE ? " and hyphenation" : ""));
803                 maxAdjustment = 20;
804                 if (!findBreakingPoints(par, lineWidth, maxAdjustment, true)) {
805                     log.debug("No set of breaking points found, using first-fit algorithm");
806                 }
807             }
808         }
809     }
810     
811     private boolean findBreakingPoints(Paragraph par, int lineWidth,
812             double threshold, boolean force) {
813         KnuthParagraph knuthPara = new KnuthParagraph(par);
814         int lines = knuthPara.findBreakPoints(lineWidth, threshold, force);
815         if (lines == 0) {
816             return false;
817         }
818         
819         for (int i = lines-1; i >= 0; i--) {
820             int line = i+1;
821             if (log.isTraceEnabled()) {
822                 log.trace("Making line from " + knuthPara.getStart(i) + " to " +
823                            knuthPara.getEnd(i));
824             }
825             // compute indent and adjustment ratio, according to
826             // the value of text-align and text-align-last
827
828             int difference = knuthPara.getDifference(i);
829             if (line == lines) {
830                 difference += par.lineFillerWidth;
831             }
832             int textAlign = (line < lines)
833                 ? textAlignment : textAlignmentLast;
834             int indent = (textAlign == EN_CENTER)
835                 ? difference / 2
836                 : (textAlign == EN_END) ? difference : 0;
837             indent += (line == 1 && knuthParagraphs.indexOf(par) == 0)
838                 ? textIndent.getValue(this) : 0;
839             double ratio = (textAlign == EN_JUSTIFY)
840                 ? knuthPara.getAdjustRatio(i) : 0;
841
842             int start = knuthPara.getStart(i);
843             int end = knuthPara.getEnd(i);
844             makeLineBreakPosition(par, start, end, 0, ratio, indent);
845         }
846         return true;
847     }
848
849     private void makeLineBreakPosition(Paragraph par,
850                                        int firstElementIndex, int lastElementIndex,
851                                        int insertIndex, double ratio, int indent) {
852         // line height calculation
853
854         int halfLeading = (lineHeight - lead - follow) / 2;
855         // height above the main baseline
856         int lineLead = lead + halfLeading;
857         // maximum size of top and bottom alignment
858         int lineFollow = follow + halfLeading;
859
860         ListIterator inlineIterator
861             = par.listIterator(firstElementIndex);
862         for (int j = firstElementIndex;
863              j <= lastElementIndex;
864              j++) {
865             KnuthElement element = (KnuthElement) inlineIterator.next();
866             if (element.isBox()) {
867                 KnuthInlineBox box = (KnuthInlineBox)element;
868                 if (box.getLead() > lineLead) {
869                     lineLead = box.getLead();
870                 }
871                 if (box.getTotal() > lineFollow) {
872                     lineFollow = box.getTotal();
873                 }
874                 if (box.getMiddle() > lineLead + middleShift) {
875                     lineLead += box.getMiddle()
876                                 - lineLead - middleShift;
877                 }
878                 if (box.getMiddle() > middlefollow - middleShift) {
879                     middlefollow += box.getMiddle()
880                                     - middlefollow + middleShift;
881                 }
882             }
883         }
884
885         if (lineFollow - lineLead > middlefollow) {
886                     middlefollow = lineFollow - lineLead;
887         }
888
889         breakpoints.add(insertIndex,
890                         new LineBreakPosition(this,
891                                               knuthParagraphs.indexOf(par),
892                                               lastElementIndex ,
893                                               ratio, 0, indent,
894                                               lineLead + middlefollow,
895                                               lineLead));
896     }*/

897
898     
899     /**
900      * Phase 2 of Knuth algorithm: find optimal break points.
901      * @param alignment alignment in BP direction of the paragraph
902      * @param context the layout context
903      * @return a list of Knuth elements representing broken lines
904      */

905     private LinkedList JavaDoc createLineBreaks(int alignment, LayoutContext context) {
906
907         // find the optimal line breaking points for each paragraph
908
ListIterator JavaDoc paragraphsIterator
909             = knuthParagraphs.listIterator(knuthParagraphs.size());
910         lineLayoutsList = new ArrayList JavaDoc(knuthParagraphs.size());
911         LineLayoutPossibilities llPoss;
912         while (paragraphsIterator.hasPrevious()) {
913             KnuthSequence seq = (KnuthSequence) paragraphsIterator.previous();
914             if (!seq.isInlineSequence()) {
915                 // This set of line layout possibilities does not matter;
916
// we only need an entry in lineLayoutsList.
917
llPoss = new LineLayoutPossibilities();
918             } else {
919                 llPoss = findOptimalBreakingPoints(alignment, (Paragraph) seq);
920             }
921             lineLayoutsList.add(0, llPoss);
922         }
923         
924         setFinished(true);
925     
926         //Post-process the line breaks found
927
return postProcessLineBreaks(alignment, context);
928     }
929
930     /**
931      * Fint the optimal linebreaks for a paragraph
932      * @param alignment alignment of the paragraph
933      * @param currPar the Paragraph for which the linebreaks are found
934      * @return the line layout possibilities for the paragraph
935      */

936     private LineLayoutPossibilities findOptimalBreakingPoints(int alignment, Paragraph currPar) {
937         // use the member lineLayouts, which is read by LineBreakingAlgorithm.updateData1 and 2
938
lineLayouts = new LineLayoutPossibilities();
939         double maxAdjustment = 1;
940         int iBPcount = 0;
941         LineBreakingAlgorithm alg = new LineBreakingAlgorithm(alignment,
942                                         textAlignment, textAlignmentLast,
943                                         textIndent.getValue(this), currPar.lineFiller.opt,
944                                         lineHeight.getValue(this), lead, follow,
945                                         (knuthParagraphs.indexOf(currPar) == 0),
946                                         hyphenationLadderCount.getEnum() == EN_NO_LIMIT
947                                         ? 0 : hyphenationLadderCount.getValue(),
948                                         this);
949    
950         if (hyphenationProperties.hyphenate == EN_TRUE
951                 && fobj.getWrapOption() != EN_NO_WRAP) {
952             findHyphenationPoints(currPar);
953         }
954    
955         // first try
956
int allowedBreaks;
957         if (wrapOption == EN_NO_WRAP) {
958             allowedBreaks = BreakingAlgorithm.ONLY_FORCED_BREAKS;
959         } else {
960             allowedBreaks = BreakingAlgorithm.NO_FLAGGED_PENALTIES;
961         }
962         alg.setConstantLineWidth(iLineWidth);
963         iBPcount = alg.findBreakingPoints(currPar,
964                                           maxAdjustment, false, allowedBreaks);
965         if (iBPcount == 0 || alignment == EN_JUSTIFY) {
966             // if the first try found a set of breaking points, save them
967
if (iBPcount > 0) {
968                 alg.resetAlgorithm();
969                 lineLayouts.savePossibilities(false);
970             } else {
971                 // the first try failed
972
log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment);
973             }
974    
975             // now try something different
976
log.debug("Hyphenation possible? " + (hyphenationProperties.hyphenate == EN_TRUE));
977             if (hyphenationProperties.hyphenate == EN_TRUE
978                 && !(allowedBreaks == BreakingAlgorithm.ONLY_FORCED_BREAKS)) {
979                 // consider every hyphenation point as a legal break
980
allowedBreaks = BreakingAlgorithm.ALL_BREAKS;
981             } else {
982                 // try with a higher threshold
983
maxAdjustment = 5;
984             }
985    
986             if ((iBPcount
987                  = alg.findBreakingPoints(currPar,
988                                           maxAdjustment, false, allowedBreaks)) == 0) {
989                 // the second try failed too, try with a huge threshold
990
// and force the algorithm to find
991
// a set of breaking points
992
log.debug("No set of breaking points found with maxAdjustment = "
993                           + maxAdjustment
994                           + (hyphenationProperties.hyphenate == EN_TRUE
995                                   ? " and hyphenation" : ""));
996                 maxAdjustment = 20;
997                 iBPcount
998                     = alg.findBreakingPoints(currPar,
999                                              maxAdjustment, true, allowedBreaks);
1000            }
1001   
1002            // use non-hyphenated breaks, when possible
1003
lineLayouts.restorePossibilities();
1004   
1005            /* extension (not in the XSL FO recommendation): if vertical alignment
1006               is justify and the paragraph has only one layout, try using
1007               shorter or longer lines */

1008            //TODO This code snippet is disabled. Reenable?
1009
if (false && alignment == EN_JUSTIFY && textAlignment == EN_JUSTIFY) {
1010                //log.debug("LLM.getNextKnuthElements> layouts with more lines? " + lineLayouts.canUseMoreLines());
1011
//log.debug(" layouts with fewer lines? " + lineLayouts.canUseLessLines());
1012
if (!lineLayouts.canUseMoreLines()) {
1013                    alg.resetAlgorithm();
1014                    lineLayouts.savePossibilities(true);
1015                    // try with shorter lines
1016
int savedLineWidth = iLineWidth;
1017                    iLineWidth = (int) (iLineWidth * 0.95);
1018                    iBPcount = alg.findBreakingPoints(currPar,
1019                             maxAdjustment, true, allowedBreaks);
1020                    // use normal lines, when possible
1021
lineLayouts.restorePossibilities();
1022                    iLineWidth = savedLineWidth;
1023                }
1024                if (!lineLayouts.canUseLessLines()) {
1025                    alg.resetAlgorithm();
1026                    lineLayouts.savePossibilities(true);
1027                    // try with longer lines
1028
int savedLineWidth = iLineWidth;
1029                    iLineWidth = (int) (iLineWidth * 1.05);
1030                    alg.setConstantLineWidth(iLineWidth);
1031                    iBPcount = alg.findBreakingPoints(currPar,
1032                            maxAdjustment, true, allowedBreaks);
1033                    // use normal lines, when possible
1034
lineLayouts.restorePossibilities();
1035                    iLineWidth = savedLineWidth;
1036                }
1037                //log.debug("LLM.getNextKnuthElements> now, layouts with more lines? " + lineLayouts.canUseMoreLines());
1038
//log.debug(" now, layouts with fewer lines? " + lineLayouts.canUseLessLines());
1039
}
1040        }
1041        return lineLayouts;
1042    }
1043
1044    /**
1045     * Creates the element list in BP direction for the broken lines.
1046     * @param alignment the currently applicable vertical alignment
1047     * @param context the layout context
1048     * @return the newly built element list
1049     */

1050    private LinkedList JavaDoc postProcessLineBreaks(int alignment, LayoutContext context) {
1051    
1052        LinkedList JavaDoc returnList = new LinkedList JavaDoc();
1053        
1054        for (int p = 0; p < knuthParagraphs.size(); p++) {
1055            // null penalty between paragraphs
1056
if (p > 0 && !((BlockLevelLayoutManager) parentLM).mustKeepTogether()) {
1057                returnList.add(new BreakElement(
1058                        new Position(this), 0, context));
1059                //returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
1060
}
1061        
1062            LineLayoutPossibilities llPoss;
1063            llPoss = (LineLayoutPossibilities) lineLayoutsList.get(p);
1064            KnuthSequence seq = (KnuthSequence) knuthParagraphs.get(p);
1065
1066            if (!seq.isInlineSequence()) {
1067                LinkedList JavaDoc targetList = new LinkedList JavaDoc();
1068                ListIterator JavaDoc listIter = seq.listIterator();
1069                while (listIter.hasNext()) {
1070                    ListElement tempElement;
1071                    tempElement = (ListElement) listIter.next();
1072                    if (tempElement.getLayoutManager() != this) {
1073                        tempElement.setPosition(notifyPos(new NonLeafPosition(this,
1074                                tempElement.getPosition())));
1075                    }
1076                    targetList.add(tempElement);
1077                }
1078                returnList.addAll(targetList);
1079            } else if (seq.isInlineSequence() && alignment == EN_JUSTIFY) {
1080                /* justified vertical alignment (not in the XSL FO recommendation):
1081                   create a multi-layout sequence whose elements will contain
1082                   a conventional Position */

1083                Position returnPosition = new LeafPosition(this, p);
1084                createElements(returnList, llPoss, returnPosition);
1085            } else {
1086                /* "normal" vertical alignment: create a sequence whose boxes
1087                   represent effective lines, and contain LineBreakPositions */

1088                Position returnPosition = new LeafPosition(this, p);
1089                int startIndex = 0;
1090                for (int i = 0;
1091                        i < llPoss.getChosenLineCount();
1092                        i++) {
1093                    if (!((BlockLevelLayoutManager) parentLM).mustKeepTogether()
1094                            && i >= fobj.getOrphans()
1095                            && i <= llPoss.getChosenLineCount() - fobj.getWidows()
1096                            && returnList.size() > 0) {
1097                        // null penalty allowing a page break between lines
1098
returnList.add(new BreakElement(
1099                                returnPosition, 0, context));
1100                        //returnList.add(new KnuthPenalty(0, 0, false, returnPosition, false));
1101
}
1102                    int endIndex
1103                      = ((LineBreakPosition) llPoss.getChosenPosition(i)).getLeafPos();
1104                    // create a list of the FootnoteBodyLM handling footnotes
1105
// whose citations are in this line
1106
LinkedList JavaDoc footnoteList = new LinkedList JavaDoc();
1107                    ListIterator JavaDoc elementIterator = seq.listIterator(startIndex);
1108                    while (elementIterator.nextIndex() <= endIndex) {
1109                        KnuthElement element = (KnuthElement) elementIterator.next();
1110                        if (element instanceof KnuthInlineBox
1111                            && ((KnuthInlineBox) element).isAnchor()) {
1112                            footnoteList.add(((KnuthInlineBox) element).getFootnoteBodyLM());
1113                        } else if (element instanceof KnuthBlockBox) {
1114                            footnoteList.addAll(((KnuthBlockBox) element).getFootnoteBodyLMs());
1115                        }
1116                    }
1117                    startIndex = endIndex + 1;
1118                    LineBreakPosition lbp
1119                      = (LineBreakPosition) llPoss.getChosenPosition(i);
1120                    returnList.add(new KnuthBlockBox
1121                                   (lbp.lineHeight + lbp.spaceBefore + lbp.spaceAfter,
1122                                    footnoteList, lbp, false));
1123                    /* // add stretch and shrink to the returnlist
1124                    if (!seq.isInlineSequence()
1125                            && lbp.availableStretch != 0 || lbp.availableShrink != 0) {
1126                        returnList.add(new KnuthPenalty(0, -KnuthElement.INFINITE,
1127                                false, new Position(this), false));
1128                        returnList.add(new KnuthGlue(0, lbp.availableStretch, lbp.availableShrink,
1129                                new Position(this), false));
1130                    }
1131                    */

1132                }
1133            }
1134        }
1135        
1136        return returnList;
1137    }
1138
1139
1140    private void createElements(List JavaDoc list, LineLayoutPossibilities llPoss,
1141                                Position elementPosition) {
1142        /* number of normal, inner lines */
1143        int nInnerLines = 0;
1144        /* number of lines that can be used in order to fill more space */
1145        int nOptionalLines = 0;
1146        /* number of lines that can be used in order to fill more space
1147           only if the paragraph is not parted */

1148        int nConditionalOptionalLines = 0;
1149        /* number of lines that can be omitted in order to fill less space */
1150        int nEliminableLines = 0;
1151        /* number of lines that can be omitted in order to fill less space
1152           only if the paragraph is not parted */

1153        int nConditionalEliminableLines = 0;
1154        /* number of the first unbreakable lines */
1155        int nFirstLines = fobj.getOrphans();
1156        /* number of the last unbreakable lines */
1157        int nLastLines = fobj.getWidows();
1158        /* sub-sequence used to separate the elements representing different lines */
1159        List JavaDoc breaker = new LinkedList JavaDoc();
1160
1161        /* comment out the next lines in order to test particular situations */
1162        if (fobj.getOrphans() + fobj.getWidows() <= llPoss.getMinLineCount()) {
1163            nInnerLines = llPoss.getMinLineCount()
1164                          - (fobj.getOrphans() + fobj.getWidows());
1165            nOptionalLines = llPoss.getMaxLineCount()
1166                             - llPoss.getOptLineCount();
1167            nEliminableLines = llPoss.getOptLineCount()
1168                               - llPoss.getMinLineCount();
1169        } else if (fobj.getOrphans() + fobj.getWidows() <= llPoss.getOptLineCount()) {
1170            nOptionalLines = llPoss.getMaxLineCount()
1171                             - llPoss.getOptLineCount();
1172            nEliminableLines = llPoss.getOptLineCount()
1173                               - (fobj.getOrphans() + fobj.getWidows());
1174            nConditionalEliminableLines = (fobj.getOrphans() + fobj.getWidows())
1175                                          - llPoss.getMinLineCount();
1176        } else if (fobj.getOrphans() + fobj.getWidows() <= llPoss.getMaxLineCount()) {
1177            nOptionalLines = llPoss.getMaxLineCount()
1178                             - (fobj.getOrphans() + fobj.getWidows());
1179            nConditionalOptionalLines = (fobj.getOrphans() + fobj.getWidows())
1180                                        - llPoss.getOptLineCount();
1181            nConditionalEliminableLines = llPoss.getOptLineCount()
1182                                          - llPoss.getMinLineCount();
1183            nFirstLines -= nConditionalOptionalLines;
1184        } else {
1185            nConditionalOptionalLines = llPoss.getMaxLineCount()
1186                                        - llPoss.getOptLineCount();
1187            nConditionalEliminableLines = llPoss.getOptLineCount()
1188                                          - llPoss.getMinLineCount();
1189            nFirstLines = llPoss.getOptLineCount();
1190            nLastLines = 0;
1191        }
1192        /* comment out the previous lines in order to test particular situations */
1193
1194        /* use these lines to test particular situations
1195        nInnerLines = 0;
1196        nOptionalLines = 1;
1197        nConditionalOptionalLines = 2;
1198        nEliminableLines = 0;
1199        nConditionalEliminableLines = 0;
1200        nFirstLines = 1;
1201        nLastLines = 3;
1202        */

1203
1204        if (nLastLines != 0
1205            && (nConditionalOptionalLines > 0 || nConditionalEliminableLines > 0)) {
1206            breaker.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
1207            breaker.add(new KnuthGlue(0, -nConditionalOptionalLines * constantLineHeight,
1208                                        -nConditionalEliminableLines * constantLineHeight,
1209                                        LINE_NUMBER_ADJUSTMENT, elementPosition, false));
1210            breaker.add(new KnuthPenalty(nConditionalOptionalLines * constantLineHeight,
1211                                           0, false, elementPosition, false));
1212            breaker.add(new KnuthGlue(0, nConditionalOptionalLines * constantLineHeight,
1213                                        nConditionalEliminableLines * constantLineHeight,
1214                                        LINE_NUMBER_ADJUSTMENT, elementPosition, false));
1215        } else if (nLastLines != 0) {
1216            breaker.add(new KnuthPenalty(0, 0, false, elementPosition, false));
1217        }
1218
1219        //log.debug("first=" + nFirstLines + " inner=" + nInnerLines
1220
// + " optional=" + nOptionalLines + " eliminable=" + nEliminableLines
1221
// + " last=" + nLastLines
1222
// + " (condOpt=" + nConditionalOptionalLines + " condEl=" + nConditionalEliminableLines + ")");
1223

1224        // creation of the elements:
1225
// first group of lines
1226
list.add(new KnuthBox(nFirstLines * constantLineHeight, elementPosition,
1227                              (nLastLines == 0
1228                               && nConditionalOptionalLines == 0
1229                               && nConditionalEliminableLines == 0 ? true : false)));
1230        if (nConditionalOptionalLines > 0
1231            || nConditionalEliminableLines > 0) {
1232            list.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
1233            list.add(new KnuthGlue(0, nConditionalOptionalLines * constantLineHeight,
1234                                   nConditionalEliminableLines * constantLineHeight,
1235                                   LINE_NUMBER_ADJUSTMENT, elementPosition, false));
1236            list.add(new KnuthBox(0, elementPosition,
1237                                  (nLastLines == 0 ? true : false)));
1238        }
1239
1240        // optional lines
1241
for (int i = 0; i < nOptionalLines; i++) {
1242            list.addAll(breaker);
1243            list.add(new KnuthBox(0, elementPosition, false));
1244            list.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
1245            list.add(new KnuthGlue(0, 1 * constantLineHeight, 0,
1246                                   LINE_NUMBER_ADJUSTMENT, elementPosition, false));
1247            list.add(new KnuthBox(0, elementPosition, false));
1248        }
1249
1250        // eliminable lines
1251
for (int i = 0; i < nEliminableLines; i++) {
1252            list.addAll(breaker);
1253            list.add(new KnuthBox(1 * constantLineHeight, elementPosition, false));
1254            list.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
1255            list.add(new KnuthGlue(0, 0, 1 * constantLineHeight,
1256                                   LINE_NUMBER_ADJUSTMENT, elementPosition, false));
1257            list.add(new KnuthBox(0, elementPosition, false));
1258        }
1259
1260        // inner lines
1261
for (int i = 0; i < nInnerLines; i++) {
1262            list.addAll(breaker);
1263            list.add(new KnuthBox(1 * constantLineHeight, elementPosition, false));
1264        }
1265
1266        // last group of lines
1267
if (nLastLines > 0) {
1268            list.addAll(breaker);
1269            list.add(new KnuthBox(nLastLines * constantLineHeight,
1270                                  elementPosition, true));
1271        }
1272    }
1273
1274    /**
1275     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether
1276     */

1277    public boolean mustKeepTogether() {
1278        return ((BlockLevelLayoutManager) getParent()).mustKeepTogether();
1279    }
1280
1281    /**
1282     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious
1283     */

1284    public boolean mustKeepWithPrevious() {
1285        return false;
1286    }
1287
1288    /**
1289     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext
1290     */

1291    public boolean mustKeepWithNext() {
1292        return false;
1293    }
1294
1295    /**
1296     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#negotiateBPDAdjustment(int, org.apache.fop.layoutmgr.KnuthElement)
1297     */

1298    public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
1299        LeafPosition pos = (LeafPosition)lastElement.getPosition();
1300        int totalAdj = adj;
1301        //if (lastElement.isPenalty()) {
1302
// totalAdj += lastElement.getW();
1303
//}
1304
//int lineNumberDifference = (int)((double) totalAdj / constantLineHeight);
1305
int lineNumberDifference = (int) Math.round((double) totalAdj / constantLineHeight
1306                                                    + (adj > 0 ? - 0.4 : 0.4));
1307        //log.debug(" LLM> variazione calcolata = " + ((double) totalAdj / constantLineHeight) + " variazione applicata = " + lineNumberDifference);
1308
LineLayoutPossibilities llPoss;
1309        llPoss = (LineLayoutPossibilities) lineLayoutsList.get(pos.getLeafPos());
1310        lineNumberDifference = llPoss.applyLineCountAdjustment(lineNumberDifference);
1311        return lineNumberDifference * constantLineHeight;
1312    }
1313
1314    /**
1315     * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#discardSpace(KnuthGlue)
1316     */

1317    public void discardSpace(KnuthGlue spaceGlue) {
1318    }
1319
1320    /**
1321     * @see org.apache.fop.layoutmgr.LayoutManager#getChangedKnuthElements(List, int)
1322     */

1323    public LinkedList JavaDoc getChangedKnuthElements(List JavaDoc oldList, int alignment) {
1324        LinkedList JavaDoc returnList = new LinkedList JavaDoc();
1325        for (int p = 0; p < knuthParagraphs.size(); p++) {
1326            LineLayoutPossibilities llPoss;
1327            llPoss = (LineLayoutPossibilities)lineLayoutsList.get(p);
1328            //log.debug("demerits of the chosen layout: " + llPoss.getChosenDemerits());
1329
for (int i = 0; i < llPoss.getChosenLineCount(); i++) {
1330                if (!((BlockLevelLayoutManager) parentLM).mustKeepTogether()
1331                    && i >= fobj.getOrphans()
1332                    && i <= llPoss.getChosenLineCount() - fobj.getWidows()) {
1333                    // null penalty allowing a page break between lines
1334
returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
1335                }
1336                LineBreakPosition lbp = (LineBreakPosition) llPoss.getChosenPosition(i);
1337                //log.debug("LLM.getChangedKnuthElements> lineWidth= " + lbp.lineWidth + " difference= " + lbp.difference);
1338
//log.debug(" shrink= " + lbp.availableShrink + " stretch= " + lbp.availableStretch);
1339

1340                //log.debug("linewidth= " + lbp.lineWidth + " difference= " + lbp.difference + " indent= " + lbp.startIndent);
1341
MinOptMax contentIPD;
1342                if (alignment == EN_JUSTIFY) {
1343                    contentIPD = new MinOptMax(
1344                        lbp.lineWidth - lbp.difference - lbp.availableShrink,
1345                        lbp.lineWidth - lbp.difference,
1346                        lbp.lineWidth - lbp.difference + lbp.availableStretch);
1347                } else if (alignment == EN_CENTER) {
1348                    contentIPD = new MinOptMax(lbp.lineWidth - 2 * lbp.startIndent);
1349                } else if (alignment == EN_END) {
1350                    contentIPD = new MinOptMax(lbp.lineWidth - lbp.startIndent);
1351                } else {
1352                    contentIPD = new MinOptMax(lbp.lineWidth - lbp.difference + lbp.startIndent);
1353                }
1354                returnList.add(new KnuthBlockBox(lbp.lineHeight,
1355                                                 contentIPD,
1356                                                 (lbp.ipdAdjust != 0
1357                                                         ? lbp.lineWidth - lbp.difference : 0),
1358                                                 lbp, false));
1359            }
1360        }
1361        return returnList;
1362    }
1363
1364    /**
1365     * find hyphenation points for every word int the current paragraph
1366     * @ param currPar the paragraph whose words will be hyphenated
1367     */

1368    private void findHyphenationPoints(Paragraph currPar) {
1369        // hyphenate every word
1370
ListIterator JavaDoc currParIterator
1371            = currPar.listIterator(currPar.ignoreAtStart);
1372        // list of TLM involved in hyphenation
1373
LinkedList JavaDoc updateList = new LinkedList JavaDoc();
1374        KnuthElement firstElement = null;
1375        KnuthElement nextElement = null;
1376        // current InlineLevelLayoutManager
1377
InlineLevelLayoutManager currLM = null;
1378        // number of KnuthBox elements containing word fragments
1379
int boxCount;
1380        // number of auxiliary KnuthElements between KnuthBoxes
1381
int auxCount;
1382        StringBuffer JavaDoc sbChars = null;
1383
1384        // find all hyphenation points
1385
while (currParIterator.hasNext()) {
1386            firstElement = (KnuthElement) currParIterator.next();
1387            //
1388
if (firstElement.getLayoutManager() != currLM) {
1389                currLM = (InlineLevelLayoutManager) firstElement.getLayoutManager();
1390                if (currLM != null) {
1391                    updateList.add(new Update(currLM, currParIterator.previousIndex()));
1392                } else {
1393                    break;
1394                }
1395            } else if (currLM == null) {
1396                break;
1397            }
1398            //TODO Something's not right here. See block_hyphenation_linefeed_preserve.xml
1399

1400            // collect word fragments, ignoring auxiliary elements;
1401
// each word fragment was created by a different TextLM
1402
if (firstElement.isBox() && !firstElement.isAuxiliary()) {
1403                boxCount = 1;
1404                auxCount = 0;
1405                sbChars = new StringBuffer JavaDoc();
1406                currLM.getWordChars(sbChars, firstElement.getPosition());
1407                // look if next elements are boxes too
1408
while (currParIterator.hasNext()) {
1409                    nextElement = (KnuthElement) currParIterator.next();
1410                    if (nextElement.isBox() && !nextElement.isAuxiliary()) {
1411                        // a non-auxiliary KnuthBox: append word chars
1412
if (currLM != nextElement.getLayoutManager()) {
1413                            currLM = (InlineLevelLayoutManager) nextElement.getLayoutManager();
1414                            updateList.add(new Update(currLM, currParIterator.previousIndex()));
1415                        }
1416                        // append text to recreate the whole word
1417
boxCount++;
1418                        currLM.getWordChars(sbChars, nextElement.getPosition());
1419                    } else if (!nextElement.isAuxiliary()) {
1420                        // a non-auxiliary non-box KnuthElement: stop
1421
// go back to the last box or auxiliary element
1422
currParIterator.previous();
1423                        break;
1424                    } else {
1425                        if (currLM != nextElement.getLayoutManager()) {
1426                            currLM = (InlineLevelLayoutManager) nextElement.getLayoutManager();
1427                            updateList.add(new Update(currLM, currParIterator.previousIndex()));
1428                        }
1429                        // an auxiliary KnuthElement: simply ignore it
1430
auxCount++;
1431                    }
1432                }
1433                log.trace(" Word to hyphenate: " + sbChars.toString());
1434                // find hyphenation points
1435
HyphContext hc = getHyphenContext(sbChars);
1436                // ask each LM to hyphenate its word fragment
1437
if (hc != null) {
1438                    KnuthElement element = null;
1439                    for (int i = 0; i < (boxCount + auxCount); i++) {
1440                        currParIterator.previous();
1441                    }
1442                    for (int i = 0; i < (boxCount + auxCount); i++) {
1443                        element = (KnuthElement) currParIterator.next();
1444                        if (element.isBox() && !element.isAuxiliary()) {
1445                            ((InlineLevelLayoutManager)
1446                             element.getLayoutManager()).hyphenate(element.getPosition(), hc);
1447                        } else {
1448                            // nothing to do, element is an auxiliary KnuthElement
1449
}
1450                    }
1451                }
1452            }
1453        }
1454
1455        // create iterator for the updateList
1456
ListIterator JavaDoc updateListIterator = updateList.listIterator();
1457        Update currUpdate = null;
1458        //int iPreservedElements = 0;
1459
int iAddedElements = 0;
1460        //int iRemovedElements = 0;
1461

1462        while (updateListIterator.hasNext()) {
1463            // ask the LMs to apply the changes and return
1464
// the new KnuthElements to replace the old ones
1465
currUpdate = (Update) updateListIterator.next();
1466            int fromIndex = currUpdate.iFirstIndex;
1467            int toIndex;
1468            if (updateListIterator.hasNext()) {
1469                Update nextUpdate = (Update) updateListIterator.next();
1470                toIndex = nextUpdate.iFirstIndex;
1471                updateListIterator.previous();
1472            } else {
1473                // maybe this is not always correct!
1474
toIndex = currPar.size() - currPar.ignoreAtEnd
1475                    - iAddedElements;
1476            }
1477
1478            // applyChanges() returns true if the LM modifies its data,
1479
// so it must return new KnuthElements to replace the old ones
1480
if (((InlineLevelLayoutManager) currUpdate.inlineLM)
1481                .applyChanges(currPar.subList(fromIndex + iAddedElements,
1482                                              toIndex + iAddedElements))) {
1483                // insert the new KnuthElements
1484
LinkedList JavaDoc newElements = null;
1485                newElements
1486                    = currUpdate.inlineLM.getChangedKnuthElements
1487                    (currPar.subList(fromIndex + iAddedElements,
1488                                     toIndex + iAddedElements),
1489                     /*flaggedPenalty,*/ effectiveAlignment);
1490                // remove the old elements
1491
currPar.subList(fromIndex + iAddedElements,
1492                                toIndex + iAddedElements).clear();
1493                // insert the new elements
1494
currPar.addAll(fromIndex + iAddedElements, newElements);
1495                iAddedElements += newElements.size() - (toIndex - fromIndex);
1496            }
1497        }
1498        updateListIterator = null;
1499        updateList.clear();
1500    }
1501
1502    /**
1503     * Line area is always considered to act as a fence.
1504     * @param isNotFirst ignored
1505     * @return always true
1506     */

1507    protected boolean hasLeadingFence(boolean isNotFirst) {
1508        return true;
1509    }
1510
1511    /**
1512     * Line area is always considered to act as a fence.
1513     * @param isNotLast ignored
1514     * @return always true
1515     */

1516    protected boolean hasTrailingFence(boolean isNotLast) {
1517        return true;
1518    }
1519
1520    private HyphContext getHyphenContext(StringBuffer JavaDoc sbChars) {
1521        // Find all hyphenation points in this word
1522
// (get in an array of offsets)
1523
// hyphenationProperties are from the block level?.
1524
// Note that according to the spec,
1525
// they also "apply to" fo:character.
1526
// I don't know what that means, since
1527
// if we change language in the middle of a "word",
1528
// the effect would seem quite strange!
1529
// Or perhaps in that case, we say that it's several words.
1530
// We probably should bring the hyphenation props up from the actual
1531
// TextLM which generate the hyphenation buffer,
1532
// since these properties inherit and could be specified
1533
// on an inline or wrapper below the block level.
1534
Hyphenation hyph
1535            = Hyphenator.hyphenate(hyphenationProperties.language,
1536                               hyphenationProperties.country,
1537                               getFObj().getUserAgent().getFactory().getHyphenationTreeResolver(),
1538                               sbChars.toString(),
1539                               hyphenationProperties.hyphenationRemainCharacterCount,
1540                               hyphenationProperties.hyphenationPushCharacterCount);
1541        // They hyph structure contains the information we need
1542
// Now start from prev: reset to that position, ask that LM to get
1543
// a Position for the first hyphenation offset. If the offset isn't in
1544
// its characters, it returns null,
1545
// but must tell how many chars it had.
1546
// Keep looking at currentBP using next hyphenation point until the
1547
// returned size is greater than the available size
1548
// or no more hyphenation points remain. Choose the best break.
1549
if (hyph != null) {
1550            return new HyphContext(hyph.getHyphenationPoints());
1551        } else {
1552            return null;
1553        }
1554    }
1555
1556    /**
1557     * Reset the positions to the given position.
1558     *
1559     * @param resetPos the position to reset to
1560     */

1561    public void resetPosition(Position resetPos) {
1562        if (resetPos == null) {
1563            setFinished(false);
1564            iReturnedLBP = 0;
1565        } else {
1566            if (isFinished()) {
1567                // if isFinished is true, iReturned LBP == breakpoints.size()
1568
// and breakpoints.get(iReturnedLBP) would generate
1569
// an IndexOutOfBoundException
1570
setFinished(false);
1571                iReturnedLBP--;
1572            }
1573            // It is not clear that the member lineLayouts has the correct value;
1574
// because the method is not called, this cannot be checked.
1575
while ((LineBreakPosition) lineLayouts.getChosenPosition(iReturnedLBP)
1576                   != (LineBreakPosition) resetPos) {
1577                iReturnedLBP--;
1578            }
1579            iReturnedLBP++;
1580        }
1581    }
1582
1583    /**
1584     * Add the areas with the break points.
1585     *
1586     * @param parentIter the iterator of break positions
1587     * @param context the context for adding areas
1588     */

1589    public void addAreas(PositionIterator parentIter,
1590                         LayoutContext context) {
1591        while (parentIter.hasNext()) {
1592            Position pos = (Position) parentIter.next();
1593            boolean isLastPosition = !parentIter.hasNext();
1594            if (pos instanceof LineBreakPosition) {
1595                addInlineArea(context, pos, isLastPosition);
1596            } else if ((pos instanceof NonLeafPosition) && pos.generatesAreas()) {
1597                addBlockArea(context, pos, isLastPosition);
1598            } else {
1599                /*
1600                 * pos was the Position inside a penalty item, nothing to do;
1601                 * or Pos does not generate an area,
1602                 * i.e. it stand for spaces, borders and padding.
1603                 */

1604            }
1605        }
1606        setCurrentArea(null); // ?? necessary
1607
}
1608
1609    /**
1610     * Add a line with inline content
1611     * @param context the context for adding areas
1612     * @param pos the position for which the line is generated
1613     * @param isLastPosition true if this is the last position of this LM
1614     */

1615    private void addInlineArea(LayoutContext context, Position pos, boolean isLastPosition) {
1616            ListIterator JavaDoc seqIterator = null;
1617            KnuthElement tempElement = null;
1618            // the TLM which created the last KnuthElement in this line
1619
LayoutManager lastLM = null;
1620            
1621            LineBreakPosition lbp = (LineBreakPosition) pos;
1622            int iCurrParIndex;
1623            iCurrParIndex = lbp.iParIndex;
1624            KnuthSequence seq = (KnuthSequence) knuthParagraphs.get(iCurrParIndex);
1625            int iStartElement = lbp.iStartIndex;
1626            int iEndElement = lbp.getLeafPos();
1627            
1628            LineArea lineArea
1629              = new LineArea((lbp.getLeafPos() < seq.size() - 1
1630                              ? textAlignment : textAlignmentLast),
1631                              lbp.difference, lbp.availableStretch, lbp.availableShrink);
1632            if (lbp.startIndent != 0) {
1633                lineArea.addTrait(Trait.START_INDENT, new Integer JavaDoc(lbp.startIndent));
1634            }
1635            lineArea.setBPD(lbp.lineHeight);
1636            lineArea.setIPD(lbp.lineWidth);
1637            lineArea.addTrait(Trait.SPACE_BEFORE, new Integer JavaDoc(lbp.spaceBefore));
1638            lineArea.addTrait(Trait.SPACE_AFTER, new Integer JavaDoc(lbp.spaceAfter));
1639            alignmentContext.resizeLine(lbp.lineHeight, lbp.baseline);
1640            
1641            if (seq instanceof Paragraph) {
1642                Paragraph currPar = (Paragraph) seq;
1643                // ignore the first elements added by the LineLayoutManager
1644
iStartElement += (iStartElement == 0) ? currPar.ignoreAtStart : 0;
1645                
1646                // if this is the last line area that for this paragraph,
1647
// ignore the last elements added by the LineLayoutManager and
1648
// subtract the last-line-end-indent from the area ipd
1649
if (iEndElement == (currPar.size() - 1)) {
1650                    iEndElement -= currPar.ignoreAtEnd;
1651                    lineArea.setIPD(lineArea.getIPD() - lastLineEndIndent.getValue(this));
1652                }
1653            }
1654            
1655            // Remove trailing spaces if allowed so
1656
if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED
1657                || whiteSpaceTreament == EN_IGNORE
1658                || whiteSpaceTreament == EN_IGNORE_IF_BEFORE_LINEFEED) {
1659                // ignore the last element in the line if it is a KnuthGlue object
1660
seqIterator = seq.listIterator(iEndElement);
1661                tempElement = (KnuthElement) seqIterator.next();
1662                if (tempElement.isGlue()) {
1663                    iEndElement--;
1664                    // this returns the same KnuthElement
1665
seqIterator.previous();
1666                    if (seqIterator.hasPrevious()) {
1667                        tempElement = (KnuthElement) seqIterator.previous();
1668                    } else {
1669                        tempElement = null;
1670                    }
1671                }
1672                if (tempElement != null) {
1673                    lastLM = tempElement.getLayoutManager();
1674                }
1675            }
1676            
1677            // Remove leading spaces if allowed so
1678
if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED
1679                || whiteSpaceTreament == EN_IGNORE
1680                || whiteSpaceTreament == EN_IGNORE_IF_AFTER_LINEFEED) {
1681                // ignore KnuthGlue and KnuthPenalty objects
1682
// at the beginning of the line
1683
seqIterator = seq.listIterator(iStartElement);
1684                tempElement = (KnuthElement) seqIterator.next();
1685                while (!tempElement.isBox() && seqIterator.hasNext()) {
1686                    tempElement = (KnuthElement) seqIterator.next();
1687                    iStartElement++;
1688                }
1689            }
1690            // Add the inline areas to lineArea
1691
PositionIterator inlinePosIter
1692              = new KnuthPossPosIter(seq, iStartElement, iEndElement + 1);
1693            
1694            iStartElement = lbp.getLeafPos() + 1;
1695            if (iStartElement == seq.size()) {
1696                // advance to next paragraph
1697
iStartElement = 0;
1698            }
1699            
1700            LayoutContext lc = new LayoutContext(0);
1701            lc.setAlignmentContext(alignmentContext);
1702            lc.setSpaceAdjust(lbp.dAdjust);
1703            lc.setIPDAdjust(lbp.ipdAdjust);
1704            lc.setLeadingSpace(new SpaceSpecifier(true));
1705            lc.setTrailingSpace(new SpaceSpecifier(false));
1706            lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
1707            
1708            /*
1709             * extension (not in the XSL FO recommendation): if the left and right margins
1710             * have been optimized, recompute indents and / or adjust ratio, according
1711             * to the paragraph horizontal alignment
1712             */

1713            if (false && textAlignment == EN_JUSTIFY) {
1714                // re-compute space adjust ratio
1715
int updatedDifference = context.getStackLimit().opt
1716                                        - lbp.lineWidth + lbp.difference;
1717                double updatedRatio = 0.0;
1718                if (updatedDifference > 0) {
1719                    updatedRatio = (float) updatedDifference / lbp.availableStretch;
1720                } else if (updatedDifference < 0) {
1721                    updatedRatio = (float) updatedDifference / lbp.availableShrink;
1722                }
1723                lc.setIPDAdjust(updatedRatio);
1724                //log.debug("LLM.addAreas> old difference = " + lbp.difference + " new difference = " + updatedDifference);
1725
//log.debug(" old ratio = " + lbp.ipdAdjust + " new ratio = " + updatedRatio);
1726
} else if (false && textAlignment == EN_CENTER) {
1727                // re-compute indent
1728
int updatedIndent = lbp.startIndent
1729                                    + (context.getStackLimit().opt - lbp.lineWidth) / 2;
1730                lineArea.addTrait(Trait.START_INDENT, new Integer JavaDoc(updatedIndent));
1731            } else if (false && textAlignment == EN_END) {
1732                // re-compute indent
1733
int updatedIndent = lbp.startIndent
1734                                    + (context.getStackLimit().opt - lbp.lineWidth);
1735                lineArea.addTrait(Trait.START_INDENT, new Integer JavaDoc(updatedIndent));
1736            }
1737            
1738            setCurrentArea(lineArea);
1739            setChildContext(lc);
1740            LayoutManager childLM;
1741            while ((childLM = inlinePosIter.getNextChildLM()) != null) {
1742                lc.setFlags(LayoutContext.LAST_AREA, (childLM == lastLM));
1743                childLM.addAreas(inlinePosIter, lc);
1744                lc.setLeadingSpace(lc.getTrailingSpace());
1745                lc.setTrailingSpace(new SpaceSpecifier(false));
1746            }
1747            
1748            // when can this be null?
1749
// if display-align is distribute, add space after
1750
if (context.getSpaceAfter() > 0
1751                    && (!context.isLastArea() || !isLastPosition)) {
1752                lineArea.setBPD(lineArea.getBPD() + context.getSpaceAfter());
1753            }
1754            lineArea.finalise();
1755            parentLM.addChildArea(lineArea);
1756    }
1757    
1758    /**
1759     * Add a line with block content
1760     * @param context the context for adding areas
1761     * @param pos the position for which the line is generated
1762     * @param isLastPosition true if this is the last position of this LM
1763     */

1764    private void addBlockArea(LayoutContext context, Position pos, boolean isLastPosition) {
1765        /* Nested block-level content;
1766         * go down the LM stack again;
1767         * "unwrap" the positions and put the child positions in a new list.
1768         * The positionList must contain one area-generating position,
1769         * which creates one line area.
1770         */

1771        List JavaDoc positionList = new ArrayList JavaDoc(1);
1772        Position innerPosition;
1773        innerPosition = ((NonLeafPosition) pos).getPosition();
1774        positionList.add(innerPosition);
1775
1776        // do we have the last LM?
1777
LayoutManager lastLM = null;
1778        if (isLastPosition) {
1779            lastLM = innerPosition.getLM();
1780        }
1781        
1782        LineArea lineArea = new LineArea();
1783        setCurrentArea(lineArea);
1784        LayoutContext lc = new LayoutContext(0);
1785        lc.setAlignmentContext(alignmentContext);
1786        setChildContext(lc);
1787        
1788        PositionIterator childPosIter = new StackingIter(positionList.listIterator());
1789        LayoutContext blocklc = new LayoutContext(0);
1790        blocklc.setLeadingSpace(new SpaceSpecifier(true));
1791        blocklc.setTrailingSpace(new SpaceSpecifier(false));
1792        blocklc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
1793        LayoutManager childLM;
1794        while ((childLM = childPosIter.getNextChildLM()) != null) {
1795            // set last area flag
1796
blocklc.setFlags(LayoutContext.LAST_AREA,
1797                             (context.isLastArea() && childLM == lastLM));
1798            blocklc.setStackLimit(context.getStackLimit());
1799            // Add the line areas to Area
1800
childLM.addAreas(childPosIter, blocklc);
1801            blocklc.setLeadingSpace(blocklc.getTrailingSpace());
1802            blocklc.setTrailingSpace(new SpaceSpecifier(false));
1803        }
1804        lineArea.updateExtentsFromChildren();
1805        parentLM.addChildArea(lineArea);
1806    }
1807
1808    /**
1809     * @see org.apache.fop.layoutmgr.LayoutManager#addChildArea(Area)
1810     */

1811    public void addChildArea(Area childArea) {
1812        // Make sure childArea is inline area
1813
if (childArea instanceof InlineArea) {
1814            Area parent = getCurrentArea();
1815            if (getContext().resolveLeadingSpace()) {
1816                addSpace(parent,
1817                         getContext().getLeadingSpace().resolve(false),
1818                         getContext().getSpaceAdjust());
1819            }
1820            parent.addChildArea(childArea);
1821        }
1822    }
1823
1824    // --------- Property Resolution related functions --------- //
1825

1826    /**
1827     * @see org.apache.fop.layoutmgr.LayoutManager#getGeneratesBlockArea
1828     */

1829    public boolean getGeneratesBlockArea() {
1830        return true;
1831    }
1832   
1833    /**
1834     * @see org.apache.fop.layoutmgr.LayoutManager#getGeneratesLineArea
1835     */

1836    public boolean getGeneratesLineArea() {
1837        return true;
1838    }
1839}
1840
1841
Popular Tags