KickJava   Java API By Example, From Geeks To Geeks.

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


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: InlineLayoutManager.java 453310 2006-10-05 18:44:15Z spepping $ */
19
20 package org.apache.fop.layoutmgr.inline;
21
22 import java.util.ListIterator JavaDoc;
23 import java.util.LinkedList JavaDoc;
24 import java.util.List 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.inline.InlineArea;
30 import org.apache.fop.area.inline.InlineBlockParent;
31 import org.apache.fop.area.inline.InlineParent;
32 import org.apache.fop.datatypes.Length;
33 import org.apache.fop.fo.flow.Inline;
34 import org.apache.fop.fo.flow.InlineLevel;
35 import org.apache.fop.fo.flow.Leader;
36 import org.apache.fop.fo.pagination.Title;
37 import org.apache.fop.fo.properties.CommonBorderPaddingBackground;
38 import org.apache.fop.fo.properties.CommonMarginInline;
39 import org.apache.fop.fo.properties.SpaceProperty;
40 import org.apache.fop.fonts.Font;
41 import org.apache.fop.layoutmgr.BlockKnuthSequence;
42 import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
43 import org.apache.fop.layoutmgr.BreakElement;
44 import org.apache.fop.layoutmgr.KnuthBox;
45 import org.apache.fop.layoutmgr.KnuthSequence;
46 import org.apache.fop.layoutmgr.LayoutContext;
47 import org.apache.fop.layoutmgr.NonLeafPosition;
48 import org.apache.fop.layoutmgr.SpaceSpecifier;
49 import org.apache.fop.layoutmgr.TraitSetter;
50 import org.apache.fop.layoutmgr.LayoutManager;
51 import org.apache.fop.layoutmgr.Position;
52 import org.apache.fop.layoutmgr.PositionIterator;
53 import org.apache.fop.traits.MinOptMax;
54 import org.apache.fop.traits.SpaceVal;
55
56 /**
57  * LayoutManager for objects which stack children in the inline direction,
58  * such as Inline or Line
59  */

60 public class InlineLayoutManager extends InlineStackingLayoutManager {
61
62     /**
63      * logging instance
64      */

65     private static Log log = LogFactory.getLog(InlineLayoutManager.class);
66
67     private InlineLevel fobj;
68
69     private CommonMarginInline inlineProps = null;
70     private CommonBorderPaddingBackground borderProps = null;
71
72     private boolean areaCreated = false;
73     private LayoutManager lastChildLM = null; // Set when return last breakposs;
74

75     private Position auxiliaryPosition;
76
77     private Font font;
78
79     /** The alignment adjust property */
80     protected Length alignmentAdjust;
81     /** The alignment baseline property */
82     protected int alignmentBaseline = EN_BASELINE;
83     /** The baseline shift property */
84     protected Length baselineShift;
85     /** The dominant baseline property */
86     protected int dominantBaseline;
87     /** The line height property */
88     protected SpaceProperty lineHeight;
89     
90     private AlignmentContext alignmentContext = null;
91
92     /**
93      * Create an inline layout manager.
94      * This is used for fo's that create areas that
95      * contain inline areas.
96      *
97      * @param node the formatting object that creates the area
98      */

99     // The node should be FObjMixed
100
public InlineLayoutManager(InlineLevel node) {
101         super(node);
102         fobj = node;
103     }
104     
105     private Inline getInlineFO() {
106         return (Inline)fobj;
107     }
108     
109     /** @see LayoutManager#initialize */
110     public void initialize() {
111         int padding = 0;
112         font = fobj.getCommonFont().getFontState(fobj.getFOEventHandler().getFontInfo(), this);
113         lineHeight = fobj.getLineHeight();
114         borderProps = fobj.getCommonBorderPaddingBackground();
115         inlineProps = fobj.getCommonMarginInline();
116         
117         if (fobj instanceof Inline) {
118             alignmentAdjust = ((Inline)fobj).getAlignmentAdjust();
119             alignmentBaseline = ((Inline)fobj).getAlignmentBaseline();
120             baselineShift = ((Inline)fobj).getBaselineShift();
121             dominantBaseline = ((Inline)fobj).getDominantBaseline();
122         } else if (fobj instanceof Leader) {
123             alignmentAdjust = ((Leader)fobj).getAlignmentAdjust();
124             alignmentBaseline = ((Leader)fobj).getAlignmentBaseline();
125             baselineShift = ((Leader)fobj).getBaselineShift();
126             dominantBaseline = ((Leader)fobj).getDominantBaseline();
127         }
128         if (borderProps != null) {
129             padding = borderProps.getPadding(CommonBorderPaddingBackground.BEFORE, false, this);
130             padding += borderProps.getBorderWidth(CommonBorderPaddingBackground.BEFORE,
131                                                  false);
132             padding += borderProps.getPadding(CommonBorderPaddingBackground.AFTER, false, this);
133             padding += borderProps.getBorderWidth(CommonBorderPaddingBackground.AFTER, false);
134         }
135         extraBPD = new MinOptMax(padding);
136
137     }
138
139     /** @see InlineStackingLayoutManager#getExtraIPD(boolean, boolean) */
140     protected MinOptMax getExtraIPD(boolean isNotFirst, boolean isNotLast) {
141         int borderAndPadding = 0;
142         if (borderProps != null) {
143             borderAndPadding
144                 = borderProps.getPadding(CommonBorderPaddingBackground.START, isNotFirst, this);
145             borderAndPadding
146                 += borderProps.getBorderWidth(CommonBorderPaddingBackground.START, isNotFirst);
147             borderAndPadding
148                 += borderProps.getPadding(CommonBorderPaddingBackground.END, isNotLast, this);
149             borderAndPadding
150                 += borderProps.getBorderWidth(CommonBorderPaddingBackground.END, isNotLast);
151         }
152         return new MinOptMax(borderAndPadding);
153     }
154
155
156     /** @see InlineStackingLayoutManager#hasLeadingFence(boolean) */
157     protected boolean hasLeadingFence(boolean isNotFirst) {
158         return borderProps != null
159             && (borderProps.getPadding(CommonBorderPaddingBackground.START, isNotFirst, this) > 0
160                 || borderProps.getBorderWidth(CommonBorderPaddingBackground.START, isNotFirst) > 0
161                );
162     }
163
164     /** @see InlineStackingLayoutManager#hasTrailingFence(boolean) */
165     protected boolean hasTrailingFence(boolean isNotLast) {
166         return borderProps != null
167             && (borderProps.getPadding(CommonBorderPaddingBackground.END, isNotLast, this) > 0
168                 || borderProps.getBorderWidth(CommonBorderPaddingBackground.END, isNotLast) > 0
169                );
170     }
171
172     /** @see InlineStackingLayoutManager#getSpaceStart */
173     protected SpaceProperty getSpaceStart() {
174         return inlineProps != null ? inlineProps.spaceStart : null;
175     }
176     /** @see InlineStackingLayoutManager#getSpaceEnd */
177     protected SpaceProperty getSpaceEnd() {
178         return inlineProps != null ? inlineProps.spaceEnd : null;
179     }
180     
181     /** @see org.apache.fop.layoutmgr.inline.InlineLayoutManager#createArea(boolean) */
182     protected InlineArea createArea(boolean hasInlineParent) {
183         InlineArea area;
184         if (hasInlineParent) {
185             area = new InlineParent();
186             area.setOffset(0);
187         } else {
188             area = new InlineBlockParent();
189         }
190         if (fobj instanceof Inline) {
191             TraitSetter.setProducerID(area, getInlineFO().getId());
192         }
193         return area;
194     }
195     
196     /**
197      * @see org.apache.fop.layoutmgr.inline.InlineStackingLayoutManager#setTraits(boolean, boolean)
198      */

199     protected void setTraits(boolean isNotFirst, boolean isNotLast) {
200         if (borderProps != null) {
201             // Add border and padding to current area and set flags (FIRST, LAST ...)
202
TraitSetter.setBorderPaddingTraits(getCurrentArea(),
203                                                borderProps, isNotFirst, isNotLast, this);
204             TraitSetter.addBackground(getCurrentArea(), borderProps, this);
205         }
206     }
207
208     /**
209      * @return true if this element must be kept together
210      */

211     // TODO Use the keep-together property on Inline as well
212
public boolean mustKeepTogether() {
213         return mustKeepTogether(this.getParent());
214     }
215
216     private boolean mustKeepTogether(LayoutManager lm) {
217         if (lm instanceof BlockLevelLayoutManager) {
218             return ((BlockLevelLayoutManager) lm).mustKeepTogether();
219         } else if (lm instanceof InlineLayoutManager) {
220             return ((InlineLayoutManager) lm).mustKeepTogether();
221         } else {
222             return mustKeepTogether(lm.getParent());
223         }
224     }
225
226     /** @see org.apache.fop.layoutmgr.LayoutManager */
227     public LinkedList JavaDoc getNextKnuthElements(LayoutContext context, int alignment) {
228         LayoutManager curLM;
229
230         // the list returned by child LM
231
LinkedList JavaDoc returnedList;
232
233         // the list which will be returned to the parent LM
234
LinkedList JavaDoc returnList = new LinkedList JavaDoc();
235         KnuthSequence lastSequence = null;
236
237         SpaceSpecifier leadingSpace = context.getLeadingSpace();
238         
239         if (fobj instanceof Title) {
240             alignmentContext = new AlignmentContext(font,
241                                     lineHeight.getOptimum(this).getLength().getValue(this),
242                                     context.getWritingMode());
243                                                     
244         } else {
245             alignmentContext = new AlignmentContext(font
246                                     , lineHeight.getOptimum(this).getLength().getValue(this)
247                                     , alignmentAdjust
248                                     , alignmentBaseline
249                                     , baselineShift
250                                     , dominantBaseline
251                                     , context.getAlignmentContext());
252         }
253         
254         childLC = new LayoutContext(context);
255         childLC.setAlignmentContext(alignmentContext);
256
257         if (context.startsNewArea()) {
258             // First call to this LM in new parent "area", but this may
259
// not be the first area created by this inline
260
if (getSpaceStart() != null) {
261                 context.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart(), this));
262             }
263
264             // Check for "fence"
265
if (hasLeadingFence(!context.isFirstArea())) {
266                 // Reset leading space sequence for child areas
267
leadingSpace = new SpaceSpecifier(false);
268             }
269             // Reset state variables
270
clearPrevIPD(); // Clear stored prev content dimensions
271
}
272
273         StringBuffer JavaDoc trace = new StringBuffer JavaDoc("InlineLM:");
274
275         // We'll add the border to the first inline sequence created.
276
// This flag makes sure we do it only once.
277
boolean borderAdded = false;
278
279         if (borderProps != null) {
280             childLC.setLineStartBorderAndPaddingWidth(context.getLineStartBorderAndPaddingWidth()
281                 + borderProps.getPaddingStart(true, this)
282                 + borderProps.getBorderStartWidth(true)
283              );
284             childLC.setLineEndBorderAndPaddingWidth(context.getLineEndBorderAndPaddingWidth()
285                 + borderProps.getPaddingEnd(true, this)
286                 + borderProps.getBorderEndWidth(true)
287              );
288         }
289         
290         while ((curLM = (LayoutManager) getChildLM()) != null) {
291             if (!(curLM instanceof InlineLevelLayoutManager)) {
292                 // A block LM
293
// Leave room for start/end border and padding
294
if (borderProps != null) {
295                     childLC.setRefIPD(childLC.getRefIPD()
296                             - borderProps.getPaddingStart(lastChildLM != null, this)
297                             - borderProps.getBorderStartWidth(lastChildLM != null)
298                             - borderProps.getPaddingEnd(hasNextChildLM(), this)
299                             - borderProps.getBorderEndWidth(hasNextChildLM()));
300                 }
301             }
302             // get KnuthElements from curLM
303
returnedList = curLM.getNextKnuthElements(childLC, alignment);
304             if (returnList.size() == 0 && childLC.isKeepWithPreviousPending()) {
305                 childLC.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING, false);
306             }
307             if (returnedList == null) {
308                 // curLM returned null because it finished;
309
// just iterate once more to see if there is another child
310
continue;
311             }
312             if (returnedList.size() == 0) {
313                 continue;
314             }
315             if (curLM instanceof InlineLevelLayoutManager) {
316                 context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING, false);
317                 // "wrap" the Position stored in each element of returnedList
318
ListIterator JavaDoc seqIter = returnedList.listIterator();
319                 while (seqIter.hasNext()) {
320                     KnuthSequence sequence = (KnuthSequence) seqIter.next();
321                     sequence.wrapPositions(this);
322                 }
323                 if (lastSequence != null && lastSequence.appendSequenceOrClose
324                         ((KnuthSequence) returnedList.get(0))) {
325                     returnedList.remove(0);
326                 }
327                 // add border and padding to the first complete sequence of this LM
328
if (!borderAdded && returnedList.size() != 0) {
329                     addKnuthElementsForBorderPaddingStart((KnuthSequence) returnedList.get(0));
330                     borderAdded = true;
331                 }
332                 returnList.addAll(returnedList);
333             } else { // A block LM
334
BlockKnuthSequence sequence = new BlockKnuthSequence(returnedList);
335                 sequence.wrapPositions(this);
336                 boolean appended = false;
337                 if (lastSequence != null) {
338                     if (lastSequence.canAppendSequence(sequence)) {
339                         BreakElement bk = new BreakElement(new Position(this), 0, context);
340                         boolean keepTogether = (mustKeepTogether()
341                                                 || context.isKeepWithNextPending()
342                                                 || childLC.isKeepWithPreviousPending());
343                         appended = lastSequence.appendSequenceOrClose(sequence, keepTogether, bk);
344                     } else {
345                         lastSequence.endSequence();
346                     }
347                 }
348                 if (!appended) {
349                     // add border and padding to the first complete sequence of this LM
350
if (!borderAdded) {
351                         addKnuthElementsForBorderPaddingStart(sequence);
352                         borderAdded = true;
353                     }
354                     returnList.add(sequence);
355                 }
356                 // propagate and clear
357
context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING,
358                                  childLC.isKeepWithNextPending());
359                 childLC.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING, false);
360                 childLC.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING, false);
361             }
362             lastSequence = (KnuthSequence) returnList.getLast();
363             lastChildLM = curLM;
364         }
365         
366         if (lastSequence != null) {
367             addKnuthElementsForBorderPaddingEnd(lastSequence);
368         }
369
370         setFinished(true);
371         log.trace(trace);
372         return returnList.size() == 0 ? null : returnList;
373     }
374
375     /**
376      * Generate and add areas to parent area.
377      * Set size of each area. This should only create and return one
378      * inline area for any inline parent area.
379      *
380      * @param parentIter Iterator over Position information returned
381      * by this LayoutManager.
382      * @param context layout context.
383      */

384     public void addAreas(PositionIterator parentIter,
385                          LayoutContext context) {
386         
387         Position lastPos = null;
388         
389         addId();
390
391         setChildContext(new LayoutContext(context)); // Store current value
392

393         // If this LM has fence, make a new leading space specifier.
394
if (hasLeadingFence(areaCreated)) {
395             getContext().setLeadingSpace(new SpaceSpecifier(false));
396             getContext().setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
397         } else {
398             getContext().setFlags(LayoutContext.RESOLVE_LEADING_SPACE, false);
399         }
400
401         if (getSpaceStart() != null) {
402             context.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart(), this));
403         }
404
405         // "Unwrap" the NonLeafPositions stored in parentIter and put
406
// them in a new list. Set lastLM to be the LayoutManager
407
// which created the last Position: if the LAST_AREA flag is
408
// set in the layout context, it must be also set in the
409
// layout context given to lastLM, but must be cleared in the
410
// layout context given to the other LMs.
411
LinkedList JavaDoc positionList = new LinkedList JavaDoc();
412         NonLeafPosition pos = null;
413         LayoutManager lastLM = null; // last child LM in this iterator
414
while (parentIter.hasNext()) {
415             pos = (NonLeafPosition) parentIter.next();
416             if (pos != null && pos.getPosition() != null) {
417                 positionList.add(pos.getPosition());
418                 lastLM = pos.getPosition().getLM();
419                 lastPos = pos;
420             }
421         }
422         /*if (pos != null) {
423             lastLM = pos.getPosition().getLM();
424         }*/

425
426         InlineArea parent = createArea(lastLM == null
427                                         || lastLM instanceof InlineLevelLayoutManager);
428         parent.setBPD(alignmentContext.getHeight());
429         if (parent instanceof InlineParent) {
430             parent.setOffset(alignmentContext.getOffset());
431         } else if (parent instanceof InlineBlockParent) {
432             // All inline elements are positioned by the renderers relative to
433
// the before edge of their content rectangle
434
if (borderProps != null) {
435                 parent.setOffset(borderProps.getPaddingBefore(false, this)
436                                 + borderProps.getBorderBeforeWidth(false));
437             }
438         }
439         setCurrentArea(parent);
440         
441         StackingIter childPosIter
442             = new StackingIter(positionList.listIterator());
443
444         LayoutManager prevLM = null;
445         LayoutManager childLM;
446         while ((childLM = childPosIter.getNextChildLM()) != null) {
447             getContext().setFlags(LayoutContext.LAST_AREA,
448                                   context.isLastArea() && childLM == lastLM);
449             childLM.addAreas(childPosIter, getContext());
450             getContext().setLeadingSpace(getContext().getTrailingSpace());
451             getContext().setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
452             prevLM = childLM;
453         }
454
455         /* If this LM has a trailing fence, resolve trailing space
456          * specs from descendants. Otherwise, propagate any trailing
457          * space specs to the parent LM via the layout context. If
458          * the last child LM called returns LAST_AREA in the layout
459          * context and it is the last child LM for this LM, then this
460          * must be the last area for the current LM too.
461          */

462         boolean isLast = (getContext().isLastArea() && prevLM == lastChildLM);
463         if (hasTrailingFence(isLast)) {
464             addSpace(getCurrentArea(),
465                      getContext().getTrailingSpace().resolve(false),
466                      getContext().getSpaceAdjust());
467             context.setTrailingSpace(new SpaceSpecifier(false));
468         } else {
469             // Propagate trailing space-spec sequence to parent LM in context.
470
context.setTrailingSpace(getContext().getTrailingSpace());
471         }
472         // Add own trailing space to parent context (or set on area?)
473
if (context.getTrailingSpace() != null && getSpaceEnd() != null) {
474             context.getTrailingSpace().addSpace(new SpaceVal(getSpaceEnd(), this));
475         }
476         
477         // Not sure if lastPos can legally be null or if that masks a different problem.
478
// But it seems to fix bug 38053.
479
setTraits(areaCreated, lastPos == null || !isLast(lastPos));
480         parentLM.addChildArea(getCurrentArea());
481
482         context.setFlags(LayoutContext.LAST_AREA, isLast);
483         areaCreated = true;
484     }
485
486     /** @see LayoutManager#addChildArea(Area) */
487     public void addChildArea(Area childArea) {
488         Area parent = getCurrentArea();
489         if (getContext().resolveLeadingSpace()) {
490             addSpace(parent,
491                     getContext().getLeadingSpace().resolve(false),
492                     getContext().getSpaceAdjust());
493         }
494         parent.addChildArea(childArea);
495     }
496
497     /** @see LayoutManager#getChangedKnuthElements(List, int) */
498     public LinkedList JavaDoc getChangedKnuthElements(List JavaDoc oldList, int alignment) {
499         LinkedList JavaDoc returnedList = new LinkedList JavaDoc();
500         addKnuthElementsForBorderPaddingStart(returnedList);
501         returnedList.addAll(super.getChangedKnuthElements(oldList, alignment));
502         addKnuthElementsForBorderPaddingEnd(returnedList);
503         return returnedList;
504     }
505     
506     /**
507      * Creates Knuth elements for start border padding and adds them to the return list.
508      * @param returnList return list to add the additional elements to
509      */

510     protected void addKnuthElementsForBorderPaddingStart(List JavaDoc returnList) {
511         //Border and Padding (start)
512
CommonBorderPaddingBackground borderAndPadding = fobj.getCommonBorderPaddingBackground();
513         if (borderAndPadding != null) {
514             int ipStart = borderAndPadding.getBorderStartWidth(false)
515                          + borderAndPadding.getPaddingStart(false, this);
516             if (ipStart > 0) {
517                 returnList.add(0,new KnuthBox(ipStart, getAuxiliaryPosition(), true));
518             }
519         }
520     }
521
522     /**
523      * Creates Knuth elements for end border padding and adds them to the return list.
524      * @param returnList return list to add the additional elements to
525      */

526     protected void addKnuthElementsForBorderPaddingEnd(List JavaDoc returnList) {
527         //Border and Padding (after)
528
CommonBorderPaddingBackground borderAndPadding = fobj.getCommonBorderPaddingBackground();
529         if (borderAndPadding != null) {
530             int ipEnd = borderAndPadding.getBorderEndWidth(false)
531                         + borderAndPadding.getPaddingEnd(false, this);
532             if (ipEnd > 0) {
533                 returnList.add(new KnuthBox(ipEnd, getAuxiliaryPosition(), true));
534             }
535         }
536     }
537
538     /** @return a cached auxiliary Position instance used for things like spaces. */
539     protected Position getAuxiliaryPosition() {
540         //if (this.auxiliaryPosition == null) {
541
//this.auxiliaryPosition = new NonLeafPosition(this, new LeafPosition(this, -1));
542
this.auxiliaryPosition = new NonLeafPosition(this, null);
543         //}
544
return this.auxiliaryPosition;
545     }
546     
547     /** @see org.apache.fop.layoutmgr.inline.LeafNodeLayoutManager#addId() */
548     protected void addId() {
549         if (fobj instanceof Inline) {
550             getPSLM().addIDToPage(getInlineFO().getId());
551         }
552     }
553     
554 }
555
556
Popular Tags