blob: 7a8386dd2800412dcdc513cfaea5aa399ac52faa [file] [log] [blame]
/*
* 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.Iterator;
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.
*/
public class FlowLayoutManager extends BlockStackingLayoutManager
implements BlockLevelLayoutManager {
private Flow fobj;
/** List of break possibilities */
protected List blockBreaks = new java.util.ArrayList();
/** Array of areas currently being filled stored by area class */
private BlockParent[] currentAreas = new BlockParent[Area.CLASS_MAX];
private int iStartPos = 0;
/**
* Used to count the number of subsequent times to layout child areas on
* multiple pages.
*/
private int numSubsequentOverflows = 0;
/*LF*/
private static class StackingIter extends PositionIterator {
StackingIter(Iterator parentIter) {
super(parentIter);
}
protected LayoutManager getLM(Object nextObj) {
return ((Position) nextObj).getLM();
}
protected Position getPos(Object nextObj) {
return ((Position) nextObj);
}
}
/*LF*/
/**
* This is the top level layout manager.
* It is created by the PageSequence FO.
* @param node Flow object
*/
public FlowLayoutManager(Flow node) {
super(node);
fobj = node;
}
/*
public BreakPoss getNextBreakPoss(LayoutContext context) {
// currently active LM
LayoutManager curLM;
MinOptMax stackSize = new MinOptMax();
fobj.setLayoutDimension(PercentBase.BLOCK_IPD, context.getRefIPD());
fobj.setLayoutDimension(PercentBase.BLOCK_BPD, context.getStackLimit().opt);
while ((curLM = getChildLM()) != null) {
if (curLM.generatesInlineAreas()) {
log.error("inline area not allowed under flow - ignoring");
curLM.setFinished(true);
continue;
}
// Make break positions and return page break
// Set up a LayoutContext
MinOptMax bpd = context.getStackLimit();
BreakPoss bp;
LayoutContext childLC = new LayoutContext(0);
boolean breakPage = false;
childLC.setStackLimit(MinOptMax.subtract(bpd, stackSize));
childLC.setRefIPD(context.getRefIPD());
if (!curLM.isFinished()) {
if ((bp = curLM.getNextBreakPoss(childLC)) != null) {
stackSize.add(bp.getStackingSize());
blockBreaks.add(bp);
// set stackLimit for remaining space
childLC.setStackLimit(MinOptMax.subtract(bpd, stackSize));
if (bp.isForcedBreak() || bp.nextBreakOverflows()) {
if (log.isDebugEnabled()) {
log.debug("BreakPoss signals " + (bp.isForcedBreak()
? "forced break" : "next break overflows"));
}
breakPage = true;
}
}
}
// check the stack bpd and if greater than available
// height then go to the last best break and return
// break position
if (stackSize.opt > context.getStackLimit().opt) {
breakPage = true;
}
if (breakPage) {
numSubsequentOverflows++;
if (numSubsequentOverflows > 50) {
log.error("Content overflows available area. Giving up after 50 attempts.");
setFinished(true);
return null;
}
return new BreakPoss(
new LeafPosition(this, blockBreaks.size() - 1));
}
numSubsequentOverflows = 0; //Reset emergency counter
}
setFinished(true);
if (blockBreaks.size() > 0) {
return new BreakPoss(
new LeafPosition(this, blockBreaks.size() - 1));
}
return null;
}*/
/**
* "wrap" the Position inside each element moving the elements from
* SourceList to targetList
* @param sourceList source list
* @param targetList target list receiving the wrapped position elements
*/
protected void wrapPositionElements(List sourceList, List targetList) {
ListIterator listIter = sourceList.listIterator();
while (listIter.hasNext()) {
KnuthElement tempElement;
tempElement = (KnuthElement) listIter.next();
//if (tempElement.getLayoutManager() != this) {
tempElement.setPosition(new NonLeafPosition(this,
tempElement.getPosition()));
//}
targetList.add(tempElement);
}
}
//TODO Reintroduce emergency counter (generate error to avoid endless loop)
public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
// set layout dimensions
fobj.setLayoutDimension(PercentBase.BLOCK_IPD, context.getRefIPD());
fobj.setLayoutDimension(PercentBase.BLOCK_BPD, context.getStackLimit().opt);
// currently active LM
BlockLevelLayoutManager curLM;
BlockLevelLayoutManager prevLM = null;
//MinOptMax stackSize = new MinOptMax();
LinkedList returnedList;
LinkedList returnList = new LinkedList();
while ((curLM = ((BlockLevelLayoutManager) getChildLM())) != null) {
if (curLM.generatesInlineAreas()) {
log.error("inline area not allowed under flow - ignoring");
curLM.setFinished(true);
continue;
}
// 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());
// "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 (prevLM.mustKeepWithNext()
|| curLM.mustKeepWithPrevious()) {
// 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));
}
}
/*LF*/ 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
/*LF*/ //System.out.println("FLM - break after!!");
return returnList;
}
/*LF*/ }
}
prevLM = curLM;
}
setFinished(true);
if (returnList.size() > 0) {
return returnList;
} else {
return null;
}
}
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;
}
}
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);
}
}
public boolean mustKeepTogether() {
return false;
}
public boolean mustKeepWithPrevious() {
return false;
}
public boolean mustKeepWithNext() {
return false;
}
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;
/*LF*/ //System.out.println("");
/*LF*/ //System.out.println("FLM.getChangedKnuthElements> prima dell'unwrap, oldList.size() = " + oldList.size() + " da 0 a " + (oldList.size() - 1));
// "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();
/*LF*/ //System.out.println("FLM.getChangedKnuthElements> dopo l'unwrap, oldList.size() = " + oldList.size() + " da 0 a " + (oldList.size() - 1));
while (oldListIterator.hasNext()) {
currElement = (KnuthElement) oldListIterator.next();
/*LF*/ //System.out.println("elemento n. " + oldListIterator.previousIndex() + " nella oldList");
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();
/*LF*/ //System.out.println("FLM.getChangedKnuthElements> chiamata da " + fromIndex + " a " + oldListIterator.previousIndex());
returnedList.addAll(prevLM.getChangedKnuthElements(oldList.subList(fromIndex, oldListIterator.previousIndex()),
/*flaggedPenalty,*/ 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();
/*LF*/ //System.out.println("FLM.getChangedKnuthElements> chiamata da " + fromIndex + " a " + oldList.size());
returnedList.addAll(currLM.getChangedKnuthElements(oldList.subList(fromIndex, oldList.size()),
/*flaggedPenalty,*/ 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(parentIter, layoutContext);
/*
LayoutManager childLM = null;
LayoutContext lc = new LayoutContext(0);
LayoutManager firstLM = null;
LayoutManager lastLM = null;
// "unwrap" the NonLeafPositions stored in parentIter
// and put them in a new list;
LinkedList positionList = new LinkedList();
Position pos;
while (parentIter.hasNext()) {
pos = (Position)parentIter.next();
if (pos instanceof NonLeafPosition) {
// pos was created by a child of this FlowLM
positionList.add(((NonLeafPosition) pos).getPosition());
lastLM = ((NonLeafPosition) pos).getPosition().getLM();
if (firstLM == null) {
firstLM = lastLM;
}
} else {
// pos was created by this FlowLM, so it must be ignored
}
}
StackingIter childPosIter = new StackingIter(positionList.listIterator());
while ((childLM = childPosIter.getNextChildLM()) != null) {
// Add the block areas to Area
lc.setFlags(LayoutContext.FIRST_AREA, childLM == firstLM);
lc.setFlags(LayoutContext.LAST_AREA, childLM == lastLM);
// set space before for the first LM, in order to implement
// display-align = center or after
lc.setSpaceBefore((childLM == firstLM ? layoutContext.getSpaceBefore() : 0));
// set space after for each LM, in order to implement
// display-align = distribute
lc.setSpaceAfter(layoutContext.getSpaceAfter());
lc.setStackLimit(layoutContext.getStackLimit());
childLM.addAreas(childPosIter, lc);
}*/
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) {
addChildToArea(childArea,
this.currentAreas[childArea.getAreaClass()]);
}
/**
* @see org.apache.fop.layoutmgr.LayoutManager#getParentArea(Area)
*/
public Area getParentArea(Area childArea) {
// Get an area from the Page
BlockParent parentArea = (BlockParent)parentLM.getParentArea(childArea);
this.currentAreas[parentArea.getAreaClass()] = parentArea;
setCurrentArea(parentArea);
return parentArea;
}
/**
* @see org.apache.fop.layoutmgr.LayoutManager#resetPosition(Position)
*/
public void resetPosition(Position resetPos) {
if (resetPos == null) {
reset(null);
}
}
}