KickJava   Java API By Example, From Geeks To Geeks.

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


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: BlockLayoutManager.java 453310 2006-10-05 18:44:15Z spepping $ */
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.area.Area;
29 import org.apache.fop.area.Block;
30 import org.apache.fop.area.LineArea;
31 import org.apache.fop.datatypes.Length;
32 import org.apache.fop.fonts.Font;
33 import org.apache.fop.layoutmgr.inline.InlineLayoutManager;
34 import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager;
35 import org.apache.fop.layoutmgr.inline.LineLayoutManager;
36 import org.apache.fop.traits.MinOptMax;
37 import org.apache.fop.traits.SpaceVal;
38
39 /**
40  * LayoutManager for a block FO.
41  */

42 public class BlockLayoutManager extends BlockStackingLayoutManager
43             implements ConditionalElementListener {
44
45     /**
46      * logging instance
47      */

48     private static Log log = LogFactory.getLog(BlockLayoutManager.class);
49     
50     private Block curBlockArea;
51
52     /** Iterator over the child layout managers. */
53     protected ListIterator JavaDoc proxyLMiter;
54
55     private int lead = 12000;
56     private Length lineHeight;
57     private int follow = 2000;
58     private int middleShift = 0;
59     
60     private boolean discardBorderBefore;
61     private boolean discardBorderAfter;
62     private boolean discardPaddingBefore;
63     private boolean discardPaddingAfter;
64     private MinOptMax effSpaceBefore;
65     private MinOptMax effSpaceAfter;
66
67     /** The list of child BreakPoss instances. */
68     protected List JavaDoc childBreaks = new java.util.ArrayList JavaDoc();
69
70     /**
71      * Creates a new BlockLayoutManager.
72      * @param inBlock the block FO object to create the layout manager for.
73      */

74     public BlockLayoutManager(org.apache.fop.fo.flow.Block inBlock) {
75         super(inBlock);
76         proxyLMiter = new ProxyLMiter();
77     }
78
79     public void initialize() {
80         super.initialize();
81         Font fs = getBlockFO().getCommonFont().getFontState(
82                   getBlockFO().getFOEventHandler().getFontInfo(), this);
83         
84         lead = fs.getAscender();
85         follow = -fs.getDescender();
86         middleShift = -fs.getXHeight() / 2;
87         lineHeight = getBlockFO().getLineHeight().getOptimum(this).getLength();
88         startIndent = getBlockFO().getCommonMarginBlock().startIndent.getValue(this);
89         endIndent = getBlockFO().getCommonMarginBlock().endIndent.getValue(this);
90         foSpaceBefore = new SpaceVal(getBlockFO().getCommonMarginBlock().spaceBefore, this)
91                             .getSpace();
92         foSpaceAfter = new SpaceVal(getBlockFO().getCommonMarginBlock().spaceAfter, this)
93                             .getSpace();
94         bpUnit = 0; // non-standard extension
95
if (bpUnit == 0) {
96             // use optimum space values
97
adjustedSpaceBefore = getBlockFO().getCommonMarginBlock().spaceBefore.getSpace()
98                                         .getOptimum(this).getLength().getValue(this);
99             adjustedSpaceAfter = getBlockFO().getCommonMarginBlock().spaceAfter.getSpace()
100                                         .getOptimum(this).getLength().getValue(this);
101         } else {
102             // use minimum space values
103
adjustedSpaceBefore = getBlockFO().getCommonMarginBlock().spaceBefore.getSpace()
104                                         .getMinimum(this).getLength().getValue(this);
105             adjustedSpaceAfter = getBlockFO().getCommonMarginBlock().spaceAfter.getSpace()
106                                         .getMinimum(this).getLength().getValue(this);
107         }
108     }
109
110     /** @see org.apache.fop.layoutmgr.BlockStackingLayoutManager */
111     public LinkedList JavaDoc getNextKnuthElements(LayoutContext context, int alignment) {
112         resetSpaces();
113         return super.getNextKnuthElements(context, alignment);
114     }
115    
116     private void resetSpaces() {
117         this.discardBorderBefore = false;
118         this.discardBorderAfter = false;
119         this.discardPaddingBefore = false;
120         this.discardPaddingAfter = false;
121         this.effSpaceBefore = null;
122         this.effSpaceAfter = null;
123     }
124     
125     /**
126      * Proxy iterator for Block LM.
127      * This iterator creates and holds the complete list
128      * of child LMs.
129      * It uses fobjIter as its base iterator.
130      * Block LM's createNextChildLMs uses this iterator
131      * as its base iterator.
132      */

133     protected class ProxyLMiter extends LMiter {
134
135         /*
136          * Constructs a proxy iterator for Block LM.
137          */

138         public ProxyLMiter() {
139             super(BlockLayoutManager.this);
140             listLMs = new java.util.ArrayList JavaDoc(10);
141         }
142
143         /**
144          * @return true if there are more child lms
145          */

146         public boolean hasNext() {
147             return (curPos < listLMs.size()) ? true : createNextChildLMs(curPos);
148         }
149
150         /**
151          * @return true if new child lms were added
152          */

153         protected boolean createNextChildLMs(int pos) {
154             List JavaDoc newLMs = createChildLMs(pos + 1 - listLMs.size());
155             if (newLMs != null) {
156                 listLMs.addAll(newLMs);
157             }
158             return pos < listLMs.size();
159         }
160     }
161
162     /**
163      * @see org.apache.fop.layoutmgr.LayoutManager#createNextChildLMs
164      */

165     public boolean createNextChildLMs(int pos) {
166
167         while (proxyLMiter.hasNext()) {
168             LayoutManager lm = (LayoutManager) proxyLMiter.next();
169             if (lm instanceof InlineLevelLayoutManager) {
170                 LineLayoutManager lineLM = createLineManager(lm);
171                 addChildLM(lineLM);
172             } else {
173                 addChildLM(lm);
174             }
175             if (pos < childLMs.size()) {
176                 return true;
177             }
178         }
179         return false;
180     }
181
182     /**
183      * Create a new LineLM, and collect all consecutive
184      * inline generating LMs as its child LMs.
185      * @param firstlm First LM in new LineLM
186      * @return the newly created LineLM
187      */

188     private LineLayoutManager createLineManager(LayoutManager firstlm) {
189         LineLayoutManager llm;
190         llm = new LineLayoutManager(getBlockFO(), lineHeight, lead, follow);
191         List JavaDoc inlines = new java.util.ArrayList JavaDoc();
192         inlines.add(firstlm);
193         while (proxyLMiter.hasNext()) {
194             LayoutManager lm = (LayoutManager) proxyLMiter.next();
195             if (lm instanceof InlineLevelLayoutManager) {
196                 inlines.add(lm);
197             } else {
198                 proxyLMiter.previous();
199                 break;
200             }
201         }
202         llm.addChildLMs(inlines);
203         return llm;
204     }
205
206     /**
207      * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether()
208      */

209     public boolean mustKeepTogether() {
210         // TODO Keeps will have to be more sophisticated sooner or later
211
// TODO This is a quick fix for the fact that the parent is not always a BlockLevelLM;
212
// eventually mustKeepTogether() must be moved up to the LM interface
213
return (!getBlockFO().getKeepTogether().getWithinPage().isAuto()
214                 || !getBlockFO().getKeepTogether().getWithinColumn().isAuto()
215                 || (getParent() instanceof BlockLevelLayoutManager
216                     && ((BlockLevelLayoutManager) getParent()).mustKeepTogether())
217                 || (getParent() instanceof InlineLayoutManager
218                     && ((InlineLayoutManager) getParent()).mustKeepTogether()));
219     }
220
221     /**
222      * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious()
223      */

224     public boolean mustKeepWithPrevious() {
225         return !getBlockFO().getKeepWithPrevious().getWithinPage().isAuto()
226                 || !getBlockFO().getKeepWithPrevious().getWithinColumn().isAuto();
227     }
228
229     /**
230      * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext()
231      */

232     public boolean mustKeepWithNext() {
233         return !getBlockFO().getKeepWithNext().getWithinPage().isAuto()
234                 || !getBlockFO().getKeepWithNext().getWithinColumn().isAuto();
235     }
236
237     /**
238      * @see org.apache.fop.layoutmgr.LayoutManager#addAreas(org.apache.fop.layoutmgr.PositionIterator, org.apache.fop.layoutmgr.LayoutContext)
239      */

240     public void addAreas(PositionIterator parentIter,
241             LayoutContext layoutContext) {
242         getParentArea(null);
243
244         // if this will create the first block area in a page
245
// and display-align is after or center, add space before
246
if (layoutContext.getSpaceBefore() > 0) {
247             addBlockSpacing(0.0, new MinOptMax(layoutContext.getSpaceBefore()));
248         }
249
250         LayoutManager childLM = null;
251         LayoutManager lastLM = null;
252         LayoutContext lc = new LayoutContext(0);
253         lc.setSpaceAdjust(layoutContext.getSpaceAdjust());
254         // set space after in the LayoutContext for children
255
if (layoutContext.getSpaceAfter() > 0) {
256             lc.setSpaceAfter(layoutContext.getSpaceAfter());
257         }
258         PositionIterator childPosIter;
259
260         // "unwrap" the NonLeafPositions stored in parentIter
261
// and put them in a new list;
262
LinkedList JavaDoc positionList = new LinkedList JavaDoc();
263         Position pos;
264         boolean bSpaceBefore = false;
265         boolean bSpaceAfter = false;
266         Position firstPos = null;
267         Position lastPos = null;
268         while (parentIter.hasNext()) {
269             pos = (Position) parentIter.next();
270             //log.trace("pos = " + pos.getClass().getName() + "; " + pos);
271
if (pos.getIndex() >= 0) {
272                 if (firstPos == null) {
273                     firstPos = pos;
274                 }
275                 lastPos = pos;
276             }
277             Position innerPosition = pos;
278             if (pos instanceof NonLeafPosition) {
279                 //Not all elements are wrapped
280
innerPosition = ((NonLeafPosition) pos).getPosition();
281             }
282             if (innerPosition == null) {
283                 // pos was created by this BlockLM and was inside an element
284
// representing space before or after
285
// this means the space was not discarded
286
if (positionList.size() == 0) {
287                     // pos was in the element representing space-before
288
bSpaceBefore = true;
289                     //log.trace(" space before");
290
} else {
291                     // pos was in the element representing space-after
292
bSpaceAfter = true;
293                     //log.trace(" space-after");
294
}
295             } else if (innerPosition.getLM() == this
296                     && !(innerPosition instanceof MappingPosition)) {
297                 // pos was created by this BlockLM and was inside a penalty
298
// allowing or forbidding a page break
299
// nothing to do
300
//log.trace(" penalty");
301
} else {
302                 // innerPosition was created by another LM
303
positionList.add(innerPosition);
304                 lastLM = innerPosition.getLM();
305                 //log.trace(" " + innerPosition.getClass().getName());
306
}
307         }
308
309         getPSLM().addIDToPage(getBlockFO().getId());
310         if (markers != null) {
311             getCurrentPV().addMarkers(markers, true, isFirst(firstPos), isLast(lastPos));
312         }
313
314         if (bpUnit == 0) {
315             // the Positions in positionList were inside the elements
316
// created by the LineLM
317
childPosIter = new StackingIter(positionList.listIterator());
318             } else {
319             // the Positions in positionList were inside the elements
320
// created by the BlockLM in the createUnitElements() method
321
//if (((Position) positionList.getLast()) instanceof
322
// LeafPosition) {
323
// // the last item inside positionList is a LeafPosition
324
// // (a LineBreakPosition, more precisely); this means that
325
// // the whole paragraph is on the same page
326
// childPosIter = new KnuthPossPosIter(storedList, 0,
327
// storedList.size());
328
//} else {
329
// // the last item inside positionList is a Position;
330
// // this means that the paragraph has been split
331
// // between consecutive pages
332
LinkedList JavaDoc splitList = new LinkedList JavaDoc();
333             int splitLength = 0;
334             int iFirst = ((MappingPosition) positionList.getFirst()).getFirstIndex();
335             int iLast = ((MappingPosition) positionList.getLast()).getLastIndex();
336             // copy from storedList to splitList all the elements from
337
// iFirst to iLast
338
ListIterator JavaDoc storedListIterator = storedList.listIterator(iFirst);
339             while (storedListIterator.nextIndex() <= iLast) {
340                 KnuthElement element = (KnuthElement) storedListIterator
341                         .next();
342                 // some elements in storedList (i.e. penalty items) were created
343
// by this BlockLM, and must be ignored
344
if (element.getLayoutManager() != this) {
345                     splitList.add(element);
346                     splitLength += element.getW();
347                     lastLM = element.getLayoutManager();
348                 }
349             }
350             //log.debug("Adding areas from " + iFirst + " to " + iLast);
351
//log.debug("splitLength= " + splitLength
352
// + " (" + neededUnits(splitLength) + " units') "
353
// + (neededUnits(splitLength) * bpUnit - splitLength)
354
// + " spacing");
355
// add space before and / or after the paragraph
356
// to reach a multiple of bpUnit
357
if (bSpaceBefore && bSpaceAfter) {
358                 foSpaceBefore = new SpaceVal(getBlockFO()
359                                     .getCommonMarginBlock().spaceBefore, this).getSpace();
360                 foSpaceAfter = new SpaceVal(getBlockFO()
361                                     .getCommonMarginBlock().spaceAfter, this).getSpace();
362                 adjustedSpaceBefore = (neededUnits(splitLength
363                         + foSpaceBefore.min
364                         + foSpaceAfter.min)
365                         * bpUnit - splitLength) / 2;
366                 adjustedSpaceAfter = neededUnits(splitLength
367                         + foSpaceBefore.min
368                         + foSpaceAfter.min)
369                         * bpUnit - splitLength - adjustedSpaceBefore;
370                 } else if (bSpaceBefore) {
371                 adjustedSpaceBefore = neededUnits(splitLength
372                         + foSpaceBefore.min)
373                         * bpUnit - splitLength;
374                 } else {
375                 adjustedSpaceAfter = neededUnits(splitLength
376                         + foSpaceAfter.min)
377                         * bpUnit - splitLength;
378                 }
379             //log.debug("spazio prima = " + adjustedSpaceBefore
380
// + " spazio dopo = " + adjustedSpaceAfter + " totale = " +
381
// (adjustedSpaceBefore + adjustedSpaceAfter + splitLength));
382
childPosIter = new KnuthPossPosIter(splitList, 0, splitList
383                     .size());
384             //}
385
}
386
387         while ((childLM = childPosIter.getNextChildLM()) != null) {
388             // set last area flag
389
lc.setFlags(LayoutContext.LAST_AREA,
390                     (layoutContext.isLastArea() && childLM == lastLM));
391             lc.setStackLimit(layoutContext.getStackLimit());
392             // Add the line areas to Area
393
childLM.addAreas(childPosIter, lc);
394         }
395
396         if (markers != null) {
397             getCurrentPV().addMarkers(markers, false, isFirst(firstPos), isLast(lastPos));
398         }
399
400         TraitSetter.addSpaceBeforeAfter(curBlockArea, layoutContext.getSpaceAdjust(),
401                 effSpaceBefore, effSpaceAfter);
402         flush();
403
404         curBlockArea = null;
405         resetSpaces();
406         
407         // Notify end of block layout manager to the PSLM
408
getPSLM().notifyEndOfLayout(getBlockFO().getId());
409     }
410
411     /**
412      * Return an Area which can contain the passed childArea. The childArea
413      * may not yet have any content, but it has essential traits set.
414      * In general, if the LayoutManager already has an Area it simply returns
415      * it. Otherwise, it makes a new Area of the appropriate class.
416      * It gets a parent area for its area by calling its parent LM.
417      * Finally, based on the dimensions of the parent area, it initializes
418      * its own area. This includes setting the content IPD and the maximum
419      * BPD.
420      * @param childArea area to get the parent area for
421      * @return the parent area
422      */

423     public Area getParentArea(Area childArea) {
424         if (curBlockArea == null) {
425             curBlockArea = new Block();
426
427             curBlockArea.setIPD(super.getContentAreaIPD());
428
429             TraitSetter.addBreaks(curBlockArea,
430                     getBlockFO().getBreakBefore(), getBlockFO().getBreakAfter());
431
432             // Must get dimensions from parent area
433
//Don't optimize this line away. It can have ugly side-effects.
434
/*Area parentArea =*/ parentLM.getParentArea(curBlockArea);
435
436             // set traits
437
TraitSetter.setProducerID(curBlockArea, getBlockFO().getId());
438             TraitSetter.addBorders(curBlockArea,
439                     getBlockFO().getCommonBorderPaddingBackground(),
440                     discardBorderBefore, discardBorderAfter, false, false, this);
441             TraitSetter.addPadding(curBlockArea,
442                     getBlockFO().getCommonBorderPaddingBackground(),
443                     discardPaddingBefore, discardPaddingAfter, false, false, this);
444             TraitSetter.addMargins(curBlockArea,
445                     getBlockFO().getCommonBorderPaddingBackground(),
446                     startIndent, endIndent,
447                     this);
448
449             setCurrentArea(curBlockArea); // ??? for generic operations
450
}
451         return curBlockArea;
452     }
453
454     /**
455      * @see org.apache.fop.layoutmgr.LayoutManager#addChildArea(Area)
456      */

457     public void addChildArea(Area childArea) {
458         if (curBlockArea != null) {
459             if (childArea instanceof LineArea) {
460                 curBlockArea.addLineArea((LineArea) childArea);
461             } else {
462                 curBlockArea.addBlock((Block) childArea);
463             }
464         }
465     }
466
467     /**
468      * Force current area to be added to parent area.
469      * @see org.apache.fop.layoutmgr.BlockStackingLayoutManager#flush()
470      */

471     protected void flush() {
472         if (curBlockArea != null) {
473             TraitSetter.addBackground(curBlockArea,
474                     getBlockFO().getCommonBorderPaddingBackground(),
475                     this);
476             super.flush();
477         }
478     }
479
480     /**
481      * @see org.apache.fop.layoutmgr.LayoutManager#resetPosition(org.apache.fop.layoutmgr.Position)
482      */

483     public void resetPosition(Position resetPos) {
484         if (resetPos == null) {
485             reset(null);
486             childBreaks.clear();
487         } else {
488             //reset(resetPos);
489
LayoutManager lm = resetPos.getLM();
490         }
491     }
492
493     /**
494      * convenience method that returns the Block node
495      * @return the block node
496      */

497     protected org.apache.fop.fo.flow.Block getBlockFO() {
498         return (org.apache.fop.fo.flow.Block) fobj;
499     }
500     
501     // --------- Property Resolution related functions --------- //
502

503     /**
504      * Returns the IPD of the content area
505      * @return the IPD of the content area
506      */

507     public int getContentAreaIPD() {
508         if (curBlockArea != null) {
509             return curBlockArea.getIPD();
510         }
511         return super.getContentAreaIPD();
512     }
513    
514
515     /**
516      * Returns the BPD of the content area
517      * @return the BPD of the content area
518      */

519     public int getContentAreaBPD() {
520         if (curBlockArea != null) {
521             return curBlockArea.getBPD();
522         }
523         return -1;
524     }
525    
526     /**
527      * @see org.apache.fop.layoutmgr.LayoutManager#getGeneratesBlockArea
528      */

529     public boolean getGeneratesBlockArea() {
530         return true;
531     }
532
533     /** @see org.apache.fop.layoutmgr.ConditionalElementListener */
534     public void notifySpace(RelSide side, MinOptMax effectiveLength) {
535         if (RelSide.BEFORE == side) {
536             if (log.isDebugEnabled()) {
537                 log.debug(this + ": Space " + side + ", "
538                         + this.effSpaceBefore + "-> " + effectiveLength);
539             }
540             this.effSpaceBefore = effectiveLength;
541         } else {
542             if (log.isDebugEnabled()) {
543                 log.debug(this + ": Space " + side + ", "
544                         + this.effSpaceAfter + "-> " + effectiveLength);
545             }
546             this.effSpaceAfter = effectiveLength;
547         }
548     }
549
550     /** @see org.apache.fop.layoutmgr.ConditionalElementListener */
551     public void notifyBorder(RelSide side, MinOptMax effectiveLength) {
552         if (effectiveLength == null) {
553             if (RelSide.BEFORE == side) {
554                 this.discardBorderBefore = true;
555             } else {
556                 this.discardBorderAfter = true;
557             }
558         }
559         if (log.isDebugEnabled()) {
560             log.debug(this + ": Border " + side + " -> " + effectiveLength);
561         }
562     }
563
564     /** @see org.apache.fop.layoutmgr.ConditionalElementListener */
565     public void notifyPadding(RelSide side, MinOptMax effectiveLength) {
566         if (effectiveLength == null) {
567             if (RelSide.BEFORE == side) {
568                 this.discardPaddingBefore = true;
569             } else {
570                 this.discardPaddingAfter = true;
571             }
572         }
573         if (log.isDebugEnabled()) {
574             log.debug(this + ": Padding " + side + " -> " + effectiveLength);
575         }
576     }
577
578 }
579
580
Popular Tags