001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /* $Id: FlowLayoutManager.java 498835 2007-01-22 22:33:42Z jeremias $ */
019:
020: package org.apache.fop.layoutmgr;
021:
022: import org.apache.commons.logging.Log;
023: import org.apache.commons.logging.LogFactory;
024: import org.apache.fop.fo.pagination.Flow;
025: import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager;
026: import org.apache.fop.area.Area;
027: import org.apache.fop.area.BlockParent;
028:
029: import java.util.LinkedList;
030: import java.util.List;
031: import java.util.ListIterator;
032:
033: /**
034: * LayoutManager for an fo:flow object.
035: * Its parent LM is the PageSequenceLayoutManager.
036: * This LM is responsible for getting columns of the appropriate size
037: * and filling them with block-level areas generated by its children.
038: * @todo Reintroduce emergency counter (generate error to avoid endless loop)
039: */
040: public class FlowLayoutManager extends BlockStackingLayoutManager
041: implements BlockLevelLayoutManager {
042:
043: /**
044: * logging instance
045: */
046: private static Log log = LogFactory.getLog(FlowLayoutManager.class);
047:
048: /** Array of areas currently being filled stored by area class */
049: private BlockParent[] currentAreas = new BlockParent[Area.CLASS_MAX];
050:
051: private int currentSpan = EN_NONE;
052:
053: /**
054: * This is the top level layout manager.
055: * It is created by the PageSequence FO.
056: * @param pslm parent PageSequenceLayoutManager object
057: * @param node Flow object
058: */
059: public FlowLayoutManager(PageSequenceLayoutManager pslm, Flow node) {
060: super (node);
061: setParent(pslm);
062: }
063:
064: /** @see org.apache.fop.layoutmgr.LayoutManager */
065: public LinkedList getNextKnuthElements(LayoutContext context,
066: int alignment) {
067:
068: // set layout dimensions
069: int flowIPD = getCurrentPV().getCurrentSpan().getColumnWidth();
070: int flowBPD = (int) getCurrentPV().getBodyRegion().getBPD();
071:
072: // currently active LM
073: LayoutManager curLM;
074: LinkedList returnedList;
075: LinkedList returnList = new LinkedList();
076:
077: while ((curLM = getChildLM()) != null) {
078: if (curLM instanceof InlineLevelLayoutManager) {
079: log
080: .error("inline area not allowed under flow - ignoring");
081: curLM.setFinished(true);
082: continue;
083: }
084:
085: int span = EN_NONE;
086: if (curLM instanceof BlockLayoutManager) {
087: span = ((BlockLayoutManager) curLM).getBlockFO()
088: .getSpan();
089: } else if (curLM instanceof BlockContainerLayoutManager) {
090: span = ((BlockContainerLayoutManager) curLM)
091: .getBlockContainerFO().getSpan();
092: }
093: if (currentSpan != span) {
094: log.debug("span change from " + currentSpan + " to "
095: + span);
096: context.signalSpanChange(span);
097: currentSpan = span;
098: SpaceResolver.resolveElementList(returnList);
099: return returnList;
100: }
101:
102: // Set up a LayoutContext
103: //MinOptMax bpd = context.getStackLimit();
104:
105: LayoutContext childLC = new LayoutContext(0);
106: childLC.setStackLimit(context.getStackLimit());
107: childLC.setRefIPD(context.getRefIPD());
108: childLC.setWritingMode(getCurrentPage()
109: .getSimplePageMaster().getWritingMode());
110:
111: // get elements from curLM
112: returnedList = curLM.getNextKnuthElements(childLC,
113: alignment);
114: //log.debug("FLM.getNextKnuthElements> returnedList.size() = " + returnedList.size());
115: if (returnList.size() == 0
116: && childLC.isKeepWithPreviousPending()) {
117: context
118: .setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING);
119: childLC
120: .setFlags(
121: LayoutContext.KEEP_WITH_PREVIOUS_PENDING,
122: false);
123: }
124:
125: // "wrap" the Position inside each element
126: LinkedList tempList = returnedList;
127: returnedList = new LinkedList();
128: wrapPositionElements(tempList, returnedList);
129:
130: if (returnedList.size() == 1
131: && ElementListUtils
132: .endsWithForcedBreak(returnedList)) {
133: // a descendant of this flow has break-before
134: returnList.addAll(returnedList);
135: SpaceResolver.resolveElementList(returnList);
136: return returnList;
137: } else {
138: if (returnList.size() > 0) {
139: // there is a block before this one
140: if (context.isKeepWithNextPending()
141: || childLC.isKeepWithPreviousPending()) {
142: //Clear pending keep flag
143: context.setFlags(
144: LayoutContext.KEEP_WITH_NEXT_PENDING,
145: false);
146: childLC
147: .setFlags(
148: LayoutContext.KEEP_WITH_PREVIOUS_PENDING,
149: false);
150: // add an infinite penalty to forbid a break between blocks
151: returnList.add(new BreakElement(new Position(
152: this ), KnuthElement.INFINITE, context));
153: } else if (!((ListElement) returnList.getLast())
154: .isGlue()) {
155: // add a null penalty to allow a break between blocks
156: returnList.add(new BreakElement(new Position(
157: this ), 0, context));
158: }
159: }
160: if (returnedList.size() > 0) {
161: returnList.addAll(returnedList);
162: if (ElementListUtils
163: .endsWithForcedBreak(returnList)) {
164: // a descendant of this flow has break-after
165: SpaceResolver.resolveElementList(returnList);
166: return returnList;
167: }
168: }
169: }
170: if (childLC.isKeepWithNextPending()) {
171: //Clear and propagate
172: childLC.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING,
173: false);
174: context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING);
175: }
176: }
177:
178: SpaceResolver.resolveElementList(returnList);
179: setFinished(true);
180:
181: if (returnList.size() > 0) {
182: return returnList;
183: } else {
184: return null;
185: }
186: }
187:
188: /**
189: * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager
190: */
191: public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
192: log.debug(" FLM.negotiateBPDAdjustment> " + adj);
193:
194: if (lastElement.getPosition() instanceof NonLeafPosition) {
195: // this element was not created by this FlowLM
196: NonLeafPosition savedPos = (NonLeafPosition) lastElement
197: .getPosition();
198: lastElement.setPosition(savedPos.getPosition());
199: int returnValue = ((BlockLevelLayoutManager) lastElement
200: .getLayoutManager()).negotiateBPDAdjustment(adj,
201: lastElement);
202: lastElement.setPosition(savedPos);
203: log.debug(" FLM.negotiateBPDAdjustment> result "
204: + returnValue);
205: return returnValue;
206: } else {
207: return 0;
208: }
209: }
210:
211: /**
212: * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager
213: */
214: public void discardSpace(KnuthGlue spaceGlue) {
215: log.debug(" FLM.discardSpace> ");
216:
217: if (spaceGlue.getPosition() instanceof NonLeafPosition) {
218: // this element was not created by this FlowLM
219: NonLeafPosition savedPos = (NonLeafPosition) spaceGlue
220: .getPosition();
221: spaceGlue.setPosition(savedPos.getPosition());
222: ((BlockLevelLayoutManager) spaceGlue.getLayoutManager())
223: .discardSpace(spaceGlue);
224: spaceGlue.setPosition(savedPos);
225: }
226: }
227:
228: /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether() */
229: public boolean mustKeepTogether() {
230: return false;
231: }
232:
233: /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious() */
234: public boolean mustKeepWithPrevious() {
235: return false;
236: }
237:
238: /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext() */
239: public boolean mustKeepWithNext() {
240: return false;
241: }
242:
243: /**
244: * @see org.apache.fop.layoutmgr.LayoutManager#getChangedKnuthElements(java.util.List, int)
245: */
246: public LinkedList getChangedKnuthElements(List oldList, /*int flaggedPenalty,*/
247: int alignment) {
248: ListIterator oldListIterator = oldList.listIterator();
249: KnuthElement returnedElement;
250: LinkedList returnedList = new LinkedList();
251: LinkedList returnList = new LinkedList();
252: KnuthElement prevElement = null;
253: KnuthElement currElement = null;
254: int fromIndex = 0;
255:
256: // "unwrap" the Positions stored in the elements
257: KnuthElement oldElement;
258: while (oldListIterator.hasNext()) {
259: oldElement = (KnuthElement) oldListIterator.next();
260: if (oldElement.getPosition() instanceof NonLeafPosition) {
261: // oldElement was created by a descendant of this FlowLM
262: oldElement.setPosition(((NonLeafPosition) oldElement
263: .getPosition()).getPosition());
264: } else {
265: // thisElement was created by this FlowLM, remove it
266: oldListIterator.remove();
267: }
268: }
269: // reset the iterator
270: oldListIterator = oldList.listIterator();
271:
272: while (oldListIterator.hasNext()) {
273: currElement = (KnuthElement) oldListIterator.next();
274: if (prevElement != null
275: && prevElement.getLayoutManager() != currElement
276: .getLayoutManager()) {
277: // prevElement is the last element generated by the same LM
278: BlockLevelLayoutManager prevLM = (BlockLevelLayoutManager) prevElement
279: .getLayoutManager();
280: BlockLevelLayoutManager currLM = (BlockLevelLayoutManager) currElement
281: .getLayoutManager();
282: returnedList.addAll(prevLM.getChangedKnuthElements(
283: oldList.subList(fromIndex, oldListIterator
284: .previousIndex()), alignment));
285: fromIndex = oldListIterator.previousIndex();
286:
287: // there is another block after this one
288: if (prevLM.mustKeepWithNext()
289: || currLM.mustKeepWithPrevious()) {
290: // add an infinite penalty to forbid a break between blocks
291: returnedList.add(new KnuthPenalty(0,
292: KnuthElement.INFINITE, false, new Position(
293: this ), false));
294: } else if (!((KnuthElement) returnedList.getLast())
295: .isGlue()) {
296: // add a null penalty to allow a break between blocks
297: returnedList.add(new KnuthPenalty(0, 0, false,
298: new Position(this ), false));
299: }
300: }
301: prevElement = currElement;
302: }
303: if (currElement != null) {
304: BlockLevelLayoutManager currLM = (BlockLevelLayoutManager) currElement
305: .getLayoutManager();
306: returnedList.addAll(currLM.getChangedKnuthElements(oldList
307: .subList(fromIndex, oldList.size()), alignment));
308: }
309:
310: // "wrap" the Position stored in each element of returnedList
311: // and add elements to returnList
312: ListIterator listIter = returnedList.listIterator();
313: while (listIter.hasNext()) {
314: returnedElement = (KnuthElement) listIter.next();
315: if (returnedElement.getLayoutManager() != this ) {
316: returnedElement.setPosition(new NonLeafPosition(this ,
317: returnedElement.getPosition()));
318: }
319: returnList.add(returnedElement);
320: }
321:
322: return returnList;
323: }
324:
325: /**
326: * @see org.apache.fop.layoutmgr.LayoutManager#addAreas(PositionIterator, LayoutContext)
327: */
328: public void addAreas(PositionIterator parentIter,
329: LayoutContext layoutContext) {
330: AreaAdditionUtil.addAreas(this , parentIter, layoutContext);
331: flush();
332: }
333:
334: /**
335: * Add child area to a the correct container, depending on its
336: * area class. A Flow can fill at most one area container of any class
337: * at any one time. The actual work is done by BlockStackingLM.
338: * @see org.apache.fop.layoutmgr.LayoutManager#addChildArea(Area)
339: */
340: public void addChildArea(Area childArea) {
341: getParentArea(childArea);
342: addChildToArea(childArea, this .currentAreas[childArea
343: .getAreaClass()]);
344: }
345:
346: /**
347: * @see org.apache.fop.layoutmgr.LayoutManager#getParentArea(Area)
348: */
349: public Area getParentArea(Area childArea) {
350: BlockParent parentArea = null;
351: int aclass = childArea.getAreaClass();
352:
353: if (aclass == Area.CLASS_NORMAL) {
354: parentArea = getCurrentPV().getCurrentFlow();
355: } else if (aclass == Area.CLASS_BEFORE_FLOAT) {
356: parentArea = getCurrentPV().getBodyRegion()
357: .getBeforeFloat();
358: } else if (aclass == Area.CLASS_FOOTNOTE) {
359: parentArea = getCurrentPV().getBodyRegion().getFootnote();
360: } else {
361: throw new IllegalStateException("(internal error) Invalid "
362: + "area class (" + aclass + ") requested.");
363: }
364:
365: this .currentAreas[aclass] = parentArea;
366: setCurrentArea(parentArea);
367: return parentArea;
368: }
369:
370: /**
371: * @see org.apache.fop.layoutmgr.LayoutManager#resetPosition(Position)
372: */
373: public void resetPosition(Position resetPos) {
374: if (resetPos == null) {
375: reset(null);
376: }
377: }
378:
379: /**
380: * Returns the IPD of the content area
381: * @return the IPD of the content area
382: */
383: public int getContentAreaIPD() {
384: return getCurrentPV().getCurrentSpan().getColumnWidth();
385: }
386:
387: /**
388: * Returns the BPD of the content area
389: * @return the BPD of the content area
390: */
391: public int getContentAreaBPD() {
392: return (int) getCurrentPV().getBodyRegion().getBPD();
393: }
394:
395: }
|