/* * Copyright 1999-2005 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* $Id$ */ package org.apache.fop.layoutmgr; import org.apache.fop.datatypes.PercentBase; import org.apache.fop.fo.pagination.Flow; import org.apache.fop.area.Area; import org.apache.fop.area.BlockParent; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; /** * LayoutManager for an fo:flow object. * Its parent LM is the PageSequenceLayoutManager. * This LM is responsible for getting columns of the appropriate size * and filling them with block-level areas generated by its children. * @todo Reintroduce emergency counter (generate error to avoid endless loop) */ public class FlowLayoutManager extends BlockStackingLayoutManager implements BlockLevelLayoutManager { private Flow fobj; /** Array of areas currently being filled stored by area class */ private BlockParent[] currentAreas = new BlockParent[Area.CLASS_MAX]; private int currentSpan = EN_NONE; /** * This is the top level layout manager. * It is created by the PageSequence FO. * @param pslm parent PageSequenceLayoutManager object * @param node Flow object */ public FlowLayoutManager(PageSequenceLayoutManager pslm, Flow node) { super(node); fobj = node; setParent(pslm); } /** @see org.apache.fop.layoutmgr.LayoutManager */ public LinkedList getNextKnuthElements(LayoutContext context, int alignment) { // set layout dimensions int flowIPD = getCurrentPV().getCurrentSpan().getColumnWidth(); int flowBPD = (int) getCurrentPV().getBodyRegion().getBPD(); fobj.setLayoutDimension(PercentBase.REFERENCE_AREA_IPD, flowIPD); fobj.setLayoutDimension(PercentBase.REFERENCE_AREA_BPD, flowBPD); fobj.setLayoutDimension(PercentBase.BLOCK_IPD, context.getRefIPD()); fobj.setLayoutDimension(PercentBase.BLOCK_BPD, context.getStackLimit().opt); // currently active LM BlockLevelLayoutManager curLM; LinkedList returnedList; LinkedList returnList = new LinkedList(); while ((curLM = ((BlockLevelLayoutManager) getChildLM())) != null) { if (curLM instanceof InlineLevelLayoutManager) { log.error("inline area not allowed under flow - ignoring"); curLM.setFinished(true); continue; } int span = EN_NONE; if (curLM instanceof BlockLayoutManager) { span = ((BlockLayoutManager)curLM).getBlockFO().getSpan(); } else if (curLM instanceof BlockContainerLayoutManager) { span = ((BlockContainerLayoutManager)curLM).getBlockContainerFO().getSpan(); } if (currentSpan != span) { log.debug("span change from " + currentSpan + " to " + span); context.signalSpanChange(span); currentSpan = span; return returnList; } // Set up a LayoutContext //MinOptMax bpd = context.getStackLimit(); LayoutContext childLC = new LayoutContext(0); childLC.setStackLimit(context.getStackLimit()); childLC.setRefIPD(context.getRefIPD()); // get elements from curLM returnedList = curLM.getNextKnuthElements(childLC, alignment); //log.debug("FLM.getNextKnuthElements> returnedList.size() = " + returnedList.size()); if (returnList.size() == 0 && childLC.isKeepWithPreviousPending()) { context.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING); childLC.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING, false); } // "wrap" the Position inside each element LinkedList tempList = returnedList; returnedList = new LinkedList(); wrapPositionElements(tempList, returnedList); if (returnedList.size() == 1 && ((KnuthElement)returnedList.getFirst()).isPenalty() && ((KnuthPenalty)returnedList.getFirst()).getP() == -KnuthElement.INFINITE) { // a descendant of this flow has break-before returnList.addAll(returnedList); return returnList; } else { if (returnList.size() > 0) { // there is a block before this one if (context.isKeepWithNextPending() || childLC.isKeepWithPreviousPending()) { //Clear pending keep flag context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING, false); childLC.setFlags(LayoutContext.KEEP_WITH_PREVIOUS_PENDING, false); // add an infinite penalty to forbid a break between blocks returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, new Position(this), false)); } else if (!((KnuthElement) returnList.getLast()).isGlue()) { // add a null penalty to allow a break between blocks returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false)); } } if (returnedList.size() > 0) { // controllare! returnList.addAll(returnedList); if (((KnuthElement)returnedList.getLast()).isPenalty() && ((KnuthPenalty)returnedList.getLast()).getP() == -KnuthElement.INFINITE) { // a descendant of this flow has break-after return returnList; } } } if (childLC.isKeepWithNextPending()) { //Clear and propagate childLC.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING, false); context.setFlags(LayoutContext.KEEP_WITH_NEXT_PENDING); } } setFinished(true); if (returnList.size() > 0) { return returnList; } else { return null; } } /** * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager */ public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) { log.debug(" FLM.negotiateBPDAdjustment> " + adj); if (lastElement.getPosition() instanceof NonLeafPosition) { // this element was not created by this FlowLM NonLeafPosition savedPos = (NonLeafPosition)lastElement.getPosition(); lastElement.setPosition(savedPos.getPosition()); int returnValue = ((BlockLevelLayoutManager)lastElement.getLayoutManager()) .negotiateBPDAdjustment(adj, lastElement); lastElement.setPosition(savedPos); log.debug(" FLM.negotiateBPDAdjustment> result " + returnValue); return returnValue; } else { return 0; } } /** * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager */ public void discardSpace(KnuthGlue spaceGlue) { log.debug(" FLM.discardSpace> "); if (spaceGlue.getPosition() instanceof NonLeafPosition) { // this element was not created by this FlowLM NonLeafPosition savedPos = (NonLeafPosition)spaceGlue.getPosition(); spaceGlue.setPosition(savedPos.getPosition()); ((BlockLevelLayoutManager) spaceGlue.getLayoutManager()).discardSpace(spaceGlue); spaceGlue.setPosition(savedPos); } } /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether() */ public boolean mustKeepTogether() { return false; } /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious() */ public boolean mustKeepWithPrevious() { return false; } /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext() */ public boolean mustKeepWithNext() { return false; } /** * @see org.apache.fop.layoutmgr.LayoutManager#getChangedKnuthElements(java.util.List, int) */ public LinkedList getChangedKnuthElements(List oldList, /*int flaggedPenalty,*/ int alignment) { ListIterator oldListIterator = oldList.listIterator(); KnuthElement returnedElement; LinkedList returnedList = new LinkedList(); LinkedList returnList = new LinkedList(); KnuthElement prevElement = null; KnuthElement currElement = null; int fromIndex = 0; // "unwrap" the Positions stored in the elements KnuthElement oldElement; while (oldListIterator.hasNext()) { oldElement = (KnuthElement)oldListIterator.next(); if (oldElement.getPosition() instanceof NonLeafPosition) { // oldElement was created by a descendant of this FlowLM oldElement.setPosition(((NonLeafPosition)oldElement.getPosition()).getPosition()); } else { // thisElement was created by this FlowLM, remove it oldListIterator.remove(); } } // reset the iterator oldListIterator = oldList.listIterator(); while (oldListIterator.hasNext()) { currElement = (KnuthElement) oldListIterator.next(); if (prevElement != null && prevElement.getLayoutManager() != currElement.getLayoutManager()) { // prevElement is the last element generated by the same LM BlockLevelLayoutManager prevLM = (BlockLevelLayoutManager) prevElement.getLayoutManager(); BlockLevelLayoutManager currLM = (BlockLevelLayoutManager) currElement.getLayoutManager(); returnedList.addAll(prevLM.getChangedKnuthElements( oldList.subList(fromIndex, oldListIterator.previousIndex()), alignment)); fromIndex = oldListIterator.previousIndex(); // there is another block after this one if (prevLM.mustKeepWithNext() || currLM.mustKeepWithPrevious()) { // add an infinite penalty to forbid a break between blocks returnedList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, new Position(this), false)); } else if (!((KnuthElement) returnedList.getLast()).isGlue()) { // add a null penalty to allow a break between blocks returnedList.add(new KnuthPenalty(0, 0, false, new Position(this), false)); } } prevElement = currElement; } if (currElement != null) { BlockLevelLayoutManager currLM = (BlockLevelLayoutManager) currElement.getLayoutManager(); returnedList.addAll(currLM.getChangedKnuthElements( oldList.subList(fromIndex, oldList.size()), alignment)); } // "wrap" the Position stored in each element of returnedList // and add elements to returnList ListIterator listIter = returnedList.listIterator(); while (listIter.hasNext()) { returnedElement = (KnuthElement)listIter.next(); if (returnedElement.getLayoutManager() != this) { returnedElement.setPosition( new NonLeafPosition(this, returnedElement.getPosition())); } returnList.add(returnedElement); } return returnList; } /** * @see org.apache.fop.layoutmgr.LayoutManager#addAreas(PositionIterator, LayoutContext) */ public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) { AreaAdditionUtil.addAreas(this, parentIter, layoutContext); flush(); } /** * Add child area to a the correct container, depending on its * area class. A Flow can fill at most one area container of any class * at any one time. The actual work is done by BlockStackingLM. * @see org.apache.fop.layoutmgr.LayoutManager#addChildArea(Area) */ public void addChildArea(Area childArea) { getParentArea(childArea); addChildToArea(childArea, this.currentAreas[childArea.getAreaClass()]); } /** * @see org.apache.fop.layoutmgr.LayoutManager#getParentArea(Area) */ public Area getParentArea(Area childArea) { BlockParent parentArea = null; int aclass = childArea.getAreaClass(); if (aclass == Area.CLASS_NORMAL) { parentArea = getCurrentPV().getCurrentFlow(); } else if (aclass == Area.CLASS_BEFORE_FLOAT) { parentArea = getCurrentPV().getBodyRegion().getBeforeFloat(); } else if (aclass == Area.CLASS_FOOTNOTE) { parentArea = getCurrentPV().getBodyRegion().getFootnote(); } else { throw new IllegalStateException("(internal error) Invalid " + "area class (" + aclass + ") requested."); } this.currentAreas[aclass] = parentArea; setCurrentArea(parentArea); return parentArea; } /** * @see org.apache.fop.layoutmgr.LayoutManager#resetPosition(Position) */ public void resetPosition(Position resetPos) { if (resetPos == null) { reset(null); } } }