You are viewing a plain text version of this content. The canonical link for it is here.
Posted to fop-commits@xmlgraphics.apache.org by kl...@apache.org on 2001/11/09 22:57:47 UTC
cvs commit: xml-fop/src/org/apache/fop/layoutmgr AbstractLayoutManager.java BlockStackingLayoutManager.java BreakCost.java FlowLayoutManager.java LayoutManager.java PageLayoutManager.java SpaceSpecifier.java SplitContext.java
klease 01/11/09 13:57:47
Added: src/org/apache/fop/layoutmgr AbstractLayoutManager.java
BlockStackingLayoutManager.java BreakCost.java
FlowLayoutManager.java LayoutManager.java
PageLayoutManager.java SpaceSpecifier.java
SplitContext.java
Log:
First versions of LayoutManager classes
Revision Changes Path
1.1 xml-fop/src/org/apache/fop/layoutmgr/AbstractLayoutManager.java
Index: AbstractLayoutManager.java
===================================================================
/*
* $Id: AbstractLayoutManager.java,v 1.1 2001/11/09 21:57:47 klease Exp $
* Copyright (C) 2001 The Apache Software Foundation. All rights reserved.
* For details on use and redistribution please refer to the
* LICENSE file included with these sources.
*/
package org.apache.fop.layoutmgr;
import org.apache.fop.fo.FObj;
import org.apache.fop.area.Area;
import java.util.Iterator;
/**
* The base class for all LayoutManagers.
*/
public abstract class AbstractLayoutManager implements LayoutManager {
protected LayoutManager parentLM;
protected FObj fobj;
public AbstractLayoutManager(FObj fobj) {
this.fobj = fobj;
this.parentLM = null;
}
public void setParentLM(LayoutManager lm) {
this.parentLM = lm;
}
/**
* Propagates to lower level layout managers. It iterates over the
* children of its FO, asks each for its LayoutManager and calls
* its generateAreas method.
*/
public void generateAreas() {
Iterator children = fobj.getChildren();
while (children.hasNext()) {
LayoutManager lm = ((FObj)children.next()).getLayoutManager();
lm.setParentLM(this);
if (lm != null) {
lm.generateAreas();
}
}
flush(); // Add last area to parent
}
// /**
// * Ask the parent LayoutManager to add the current (full) area to the
// * appropriate parent area.
// * @param bFinished If true, this area is finished, either because it's
// * completely full or because there is no more content to put in it.
// * If false, we are in the middle of this area. This can happen,
// * for example, if we find floats in a line. We stop the current area,
// * and add it (temporarily) to its parent so that we can see if there
// * is enough space to place the float(s) anchored in the line.
// */
// protected void flush(Area area, boolean bFinished) {
// if (area != null) {
// // area.setFinished(true);
// parentLM.addChild(area, bFinished); // ????
// if (bFinished) {
// setCurrentArea(null);
// }
// }
// }
/**
* Force current area to be added to parent area.
*/
abstract protected void flush();
/**
* Return an Area which can contain the passed childArea. The childArea
* may not yet have any content, but it has essential traits set.
* In general, if the LayoutManager already has an Area it simply returns
* it. Otherwise, it makes a new Area of the appropriate class.
* It gets a parent area for its area by calling its parent LM.
* Finally, based on the dimensions of the parent area, it initializes
* its own area. This includes setting the content IPD and the maximum
* BPD.
*/
abstract public Area getParentArea(Area childArea);
public boolean generatesInlineAreas() {
return false;
}
public boolean generatesLineAreas() {
return false;
}
/**
* Add a child area to the current area. If this causes the maximum
* dimension of the current area to be exceeded, the parent LM is called
* to add it.
*/
abstract public void addChild(Area childArea) ;
/** Do nothing */
public boolean splitArea(Area areaToSplit, SplitContext context) {
context.nextArea = areaToSplit;
return false;
}
}
1.1 xml-fop/src/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java
Index: BlockStackingLayoutManager.java
===================================================================
/*
* $Id: BlockStackingLayoutManager.java,v 1.1 2001/11/09 21:57:47 klease Exp $
* Copyright (C) 2001 The Apache Software Foundation. All rights reserved.
* For details on use and redistribution please refer to the
* LICENSE file included with these sources.
*/
package org.apache.fop.layoutmgr;
import org.apache.fop.fo.FObj;
import org.apache.fop.area.Area;
import org.apache.fop.area.BlockParent;
import org.apache.fop.area.Block;
import org.apache.fop.area.MinOptMax;
import java.util.Iterator;
/**
* Base LayoutManager class for all areas which stack their child
* areas in the block-progression direction, such as Flow, Block, ListBlock.
*/
public abstract class BlockStackingLayoutManager
extends AbstractLayoutManager {
/** Reference to FO whose areas it's managing or to the traits
* of the FO.
*/
LayoutManager curChildLM = null;
BlockParent parentArea = null;
public BlockStackingLayoutManager(FObj fobj) {
super(fobj);
}
public boolean splitArea(Area area, SplitContext splitContext) {
// Divide area so that it will be within targetLength if possible
// If not, it can be shorter, but not longer.
/* Iterate over contents of the area. */
// Need to figure out if we can do this generically
// Logically a BlockStacking LM only handles Block-type areas
if (!(area instanceof BlockParent)) {
return false;
}
Iterator areaIter = ((BlockParent)area).getChildAreas().iterator();
BreakCost minBreakCost = null;
MinOptMax remainBPD = splitContext.targetBPD;
splitContext.nextArea = area;
while (areaIter.hasNext()) {
Area childArea = (Area)areaIter.next();
if (remainBPD.max < childArea.getAllocationBPD().min) {
// Past the end point: try to break it
// TODO: get a LayoutManager to do the split of the child
// area, either Area => LM or Area => gen FO => LM
LayoutManager childLM = childArea.getGeneratingFObj().
getLayoutManager();
splitContext.targetBPD = remainBPD;
if (childLM.splitArea(childArea, splitContext) == false) {
// Can't split, so must split this area before childArea
// Can we pass the iter?
// If already saw several a potential break, use it
if (minBreakCost != null) {
/* Split 'area', placing all children after
* minBreakCost.getArea() into a new area,
* which we store in the splitContext.
*/
// splitContext.nextArea = area.splitAfter(minBreakCost.getArea());
}
else {
/* This area will be shorter than the desired minimum.
* Split before the current childArea (which will be
* the first area in the newly created Area.
*/
//splitContext.nextArea = area.splitBefore(childArea);
}
}
else return true; // childLM has done the work for us!
// Set cost, dimension ???
break;
}
else {
remainBPD.subtract(childArea.getAllocationBPD());
if (remainBPD.min < 0) {
// Potential breakpoint: remember break Position and
// break "cost" (constraint violation)
BreakCost breakCost = evaluateBreakCost(area, childArea);
minBreakCost = breakCost.chooseLowest(minBreakCost);
}
}
//Note: size of area when split can depend on conditional
// space, border and padding of the split area!!!
}
// True if some part of area can be placed, false if none is placed
return (splitContext.nextArea != area);
}
private BreakCost evaluateBreakCost(Area parent, Area child) {
return new BreakCost(child,0);
}
/** return current area being filled
*/
protected BlockParent getCurrentArea() {
return this.parentArea;
}
/**
* Set the current area being filled.
*/
protected void setCurrentArea(BlockParent parentArea) {
this.parentArea = parentArea;
}
protected MinOptMax resolveSpaceSpecifier(Area nextArea) {
SpaceSpecifier spaceSpec = new SpaceSpecifier();
// Area prevArea = getCurrentArea().getLast();
// if (prevArea != null) {
// spaceSpec.addSpace(prevArea.getSpaceAfter());
// }
// spaceSpec.addSpace(nextArea.getSpaceBefore());
return spaceSpec.resolve();
}
/**
* Add the childArea to the passed area.
* Called by child LayoutManager when it has filled one of its areas.
* The LM should already have an Area in which to put the child.
* See if the area will fit in the current area.
* If so, add it. Otherwise initiate breaking.
* @param childArea the area to add: will be some block-stacked Area.
* @param parentArea the area in which to add the childArea
*/
protected void addChildToArea(Area childArea, BlockParent parentArea) {
// This should be a block-level Area (Block in the generic sense)
if (!(childArea instanceof Block)) {
System.err.println("Child not a Block in BlockStackingLM!");
return;
}
// See if the whole thing fits, including space before
// Calculate space between last child in curFlow and childArea
MinOptMax targetDim = parentArea.getAvailBPD();
MinOptMax spaceBefore = resolveSpaceSpecifier(childArea) ;
targetDim.subtract(spaceBefore);
if (targetDim.max >= childArea.getAllocationBPD().min) {
//parentArea.addBlock(new InterBlockSpace(spaceBefore));
parentArea.addBlock((Block)childArea);
return;
}
else {
// Probably need something like max BPD so we don't get into
// infinite loops with large unbreakable chunks
SplitContext splitContext = new SplitContext(targetDim);
LayoutManager childLM = childArea.getGeneratingFObj().
getLayoutManager();
if (childLM.splitArea(childArea, splitContext)) {
//parentArea.addBlock(new InterBlockSpace(spaceBefore));
parentArea.addBlock((Block)childArea);
}
flush(); // hand off current area to parent
getParentArea(splitContext.nextArea);
// Check that reference IPD hasn't changed!!!
// If it has, we must "reflow" the content
addChild(splitContext.nextArea);
}
}
/**
* Add the childArea to the current area.
* Called by child LayoutManager when it has filled one of its areas.
* The LM should already have an Area in which to put the child.
* See if the area will fit in the current area.
* If so, add it. Otherwise initiate breaking.
* @param childArea the area to add: will be some block-stacked Area.
*/
public void addChild(Area childArea) {
addChildToArea((Block)childArea, getCurrentArea());
}
/**
* Force current area to be added to parent area.
*/
protected void flush() {
parentLM.addChild(getCurrentArea());
}
}
1.1 xml-fop/src/org/apache/fop/layoutmgr/BreakCost.java
Index: BreakCost.java
===================================================================
/*
* $Id: BreakCost.java,v 1.1 2001/11/09 21:57:47 klease Exp $
* Copyright (C) 2001 The Apache Software Foundation. All rights reserved.
* For details on use and redistribution please refer to the
* LICENSE file included with these sources.
*/
package org.apache.fop.layoutmgr;
import org.apache.fop.area.Area;
/**
* Evaluate and store the cost of breaking an Area at a given point.
*/
public class BreakCost {
private Area breakArea;
private int cost; // Will be more complicated than this!
public BreakCost(Area breakArea, int cost) {
this.breakArea = breakArea;
this.cost = cost;
}
Area getArea() {
return breakArea;
}
public BreakCost chooseLowest(BreakCost otherCost) {
return this;
}
}
1.1 xml-fop/src/org/apache/fop/layoutmgr/FlowLayoutManager.java
Index: FlowLayoutManager.java
===================================================================
/*
* $Id: FlowLayoutManager.java,v 1.1 2001/11/09 21:57:47 klease Exp $
* Copyright (C) 2001 The Apache Software Foundation. All rights reserved.
* For details on use and redistribution please refer to the
* LICENSE file included with these sources.
*/
package org.apache.fop.layoutmgr;
import org.apache.fop.apps.FOPException;
import org.apache.fop.fo.FObj;
import org.apache.fop.fo.properties.Constants;
import org.apache.fop.area.*;
/**
* LayoutManager for an fo:flow object.
* Its parent LM is the PageLayoutManager.
* 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 {
/** Array of areas currently being filled stored by area class */
private BlockParent[] currentAreas = new BlockParent[Area.CLASS_MAX];
/**
* This is the top level layout manager.
* It is created by the PageSequence FO.
*/
public FlowLayoutManager(FObj fobj) {
super(fobj);
}
/**
* 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.
*/
public void addChild(Area childArea) {
addChildToArea(childArea,
this.currentAreas[childArea.getAreaClass()]);
}
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;
}
}
1.1 xml-fop/src/org/apache/fop/layoutmgr/LayoutManager.java
Index: LayoutManager.java
===================================================================
/*
* $Id: LayoutManager.java,v 1.1 2001/11/09 21:57:47 klease Exp $
* Copyright (C) 2001 The Apache Software Foundation. All rights reserved.
* For details on use and redistribution please refer to the
* LICENSE file included with these sources.
*/
package org.apache.fop.layoutmgr;
import org.apache.fop.area.Area;
/**
* The interface for all LayoutManagers.
*/
public interface LayoutManager {
public void generateAreas();
public Area getParentArea (Area childArea);
public void addChild (Area childArea);
public boolean splitArea(Area areaToSplit, SplitContext context);
public void setParentLM(LayoutManager lm);
}
1.1 xml-fop/src/org/apache/fop/layoutmgr/PageLayoutManager.java
Index: PageLayoutManager.java
===================================================================
/*
* $Id: PageLayoutManager.java,v 1.1 2001/11/09 21:57:47 klease Exp $
* Copyright (C) 2001 The Apache Software Foundation. All rights reserved.
* For details on use and redistribution please refer to the
* LICENSE file included with these sources.
*/
package org.apache.fop.layoutmgr;
import org.apache.fop.apps.FOPException;
import org.apache.fop.area.*;
import org.apache.fop.fo.pagination.PageSequence;
import org.apache.fop.fo.properties.Constants;
/**
* LayoutManager for a PageSequence and its flow.
* It manages all page-related layout.
*/
public class PageLayoutManager extends AbstractLayoutManager
implements Runnable{
private PageSequence pageseq;
/** True if haven't yet laid out any pages.*/
private boolean bFirstPage;
/** Current page being worked on. */
private PageViewport curPage;
/** Body region of the current page */
private BodyRegion curBody;
/** Current span being filled */
private Span curSpan;
/** Number of columns in current span area. */
private int curSpanColumns;
/** Current flow-reference-area (column) being filled. */
private Flow curFlow;
/** Manager which handles a queue of all pages which are completely
* laid out and ready for rendering, except for resolution of ID
* references?
*/
private AreaTree areaTree;
/**
* This is the top level layout manager.
* It is created by the PageSequence FO.
*/
public PageLayoutManager(AreaTree areaTree, PageSequence pageseq) {
super( pageseq);
this.areaTree = areaTree;
}
/**
* The layout process is designed to be able to be run in a thread.
* In theory it can run at the same
* time as FO tree generation, once the layout-master-set has been read.
* We can arrange it so that the iterator over the fobj children waits
* until the next child is available.
* As it produces pages, it adds them to the AreaTree, where the
* rendering process can also run in a parallel thread.
*/
public void run() {
generateAreas();
}
/**
* For now, only handle normal flow areas.
*/
public void addChild(Area childArea) {
if (childArea.getAreaClass() == Area.CLASS_NORMAL) {
placeFlowRefArea(childArea);
}
else ; // TODO: all the others!
}
/**
* Place a FlowReferenceArea into the current span. The FlowLM is
* responsible for making sure that it will actually fit in the
* current span area. In fact the area has already been added to the
* current span, so we are just checking to see if the span is "full",
* possibly moving to the next column or to the next page.
*/
protected void placeFlowRefArea(Area area) {
// assert (curSpan != null);
// assert (area == curFlow);
// assert (curFlow == curSpan.getFlow(curSpan.getColumnCount()-1));
// assert (area.getBPD().min < curSpan.getHeight());
// Last column on this page is filled
// See if the flow is full. The Flow LM can add an area before
// it's full in the case of a break or a span.
// Also in the case of a float to be placed. In that case, there
// may be further material added later.
// The Flow LM sets the "finished" flag on the Flow Area if it has
// completely filled it. In this case, if on the last column
// end the page.
// Alternatively the child LM indicates to parent that it's full?
if (area.getAllocationBPD().max >= curSpan.getMaxBPD().min) {
// Consider it filled
if (curSpan.getColumnCount() == curSpanColumns) {
finishPage();
}
else curFlow = null; // Create new flow on next getParentArea()
}
}
protected void placeAbsoluteArea(Area area) {
}
protected void placeBeforeFloat(Area area) {
}
protected void placeSideFloat(Area area) {
}
protected void placeFootnote(Area area) {
// After doing this, reduce available space on the curSpan.
// This has to be propagated to the curFlow (FlowLM) so that
// it can adjust its limit for composition (or it just asks
// curSpan for BPD before doing the break?)
// If multi-column, we may have to balance to find more space
// for a float. When?
}
private PageViewport makeNewPage(boolean bIsBlank, boolean bIsLast) {
finishPage();
try {
curPage = pageseq.createPage(bIsBlank, bIsLast);
} catch (FOPException fopex) { /* ???? */ }
curBody = (BodyRegion) curPage.getPage().
getRegion(RegionReference.BODY).getRegion();
return curPage;
}
private void finishPage() {
if (curPage != null) {
// Layout static content into the regions
// Need help from pageseq for this
// Queue for ID resolution and rendering
areaTree.addPage(curPage);
curPage = null;
curBody = null;
curSpan=null;
curFlow=null;
}
}
/**
* This is called from FlowLayoutManager when it needs to start
* a new flow container (while generating areas).
* @param area The area for which a container is needed. It must be
* some kind of block-level area. It must have area-class, break-before
* and span properties set.
*/
public Area getParentArea(Area childArea) {
int aclass = childArea.getAreaClass() ;
if (aclass == Area.CLASS_NORMAL) {
// TODO: how to get properties from the Area???
// Need span, break
int breakVal = Constants.AUTO; // childArea.getBreakBefore();
if (breakVal != Constants.AUTO) {
// We may be forced to make new page
handleBreak(breakVal);
}
else if (curPage == null) {
makeNewPage(false, false);
}
// Now we should be on the right kind of page
boolean bNeedSpan = false;
int span = Constants.NONE; // childArea.getSpan()
int numCols=1;
if (span == Constants.ALL) {
// Assume the number of columns is stored on the curBody object.
//numCols = curBody.getProperty(NUMBER_OF_COLUMNS);
}
if (curSpan == null) {
createBodyMainReferenceArea();
bNeedSpan = true;
}
else if (numCols != curSpanColumns) {
// TODO: BALANCE EXISTING COLUMNS
if (curSpanColumns > 1) {
// balanceColumns();
}
bNeedSpan = true;
}
if (bNeedSpan) {
// Make a new span and the first flow
createSpan(numCols);
}
else if (curFlow == null) {
createFlow();
}
return curFlow;
}
else {
if (curPage == null) {
makeNewPage(false, false);
}
// Now handle different kinds of areas
if (aclass == Area.CLASS_BEFORE_FLOAT) {
BeforeFloat bf = curBody.getBeforeFloat();
if (bf == null) {
bf = new BeforeFloat();
curBody.setBeforeFloat(bf);
}
return bf;
}
else if (aclass == Area.CLASS_FOOTNOTE) {
Footnote fn = curBody.getFootnote();
if (fn == null) {
fn = new Footnote();
curBody.setFootnote(fn);
}
return fn;
}
// TODO!!! other area classes (side-float, absolute, fixed)
return null;
}
}
/**
* Depending on the kind of break condition, make new column
* or page. May need to make an empty page if next page would
* not have the desired "handedness".
*/
protected void handleBreak(int breakVal) {
if (breakVal == Constants.COLUMN) {
if (curSpan != null &&
curSpan.getColumnCount() != curSpanColumns) {
// Move to next column
createFlow();
return;
}
// else need new page
breakVal = Constants.PAGE;
}
if (needEmptyPage(breakVal)) {
curPage = makeNewPage(true, false);
}
if (needNewPage(breakVal)) {
curPage = makeNewPage(false, false);
}
}
/**
* If we have already started to layout content on a page,
* and there is a forced break, see if we need to generate
* an empty page.
* Note that if not all content is placed, we aren't sure whether
* it will flow onto another page or not, so we'd probably better
* block until the queue of layoutable stuff is empty!
*/
private boolean needEmptyPage(int breakValue) {
return false;
// if (breakValue == Constants.PAGE || curPage.isEmpty()) {
// // any page is OK or we already have an empty page
// return false;
// }
// else {
// /* IF we are on the kind of page we need, we'll need a new page. */
// if (curPage.getPageNumber()%2 != 0) {
// // Current page is odd
// return (breakValue == Constants.ODD_PAGE);
// }
// else {
// return (breakValue == Constants.EVEN_PAGE);
// }
// }
}
/**
* See if need to generate a new page for a forced break condition.
* TODO: methods to see if the current page is empty and to get
* its number.
*/
private boolean needNewPage(int breakValue) {
return false;
// if (curPage.isEmpty()) {
// if (breakValue == Constants.PAGE) {
// return false;
// }
// else if (curPage.getPageNumber()%2 != 0) {
// // Current page is odd
// return (breakValue == Constants.EVEN_PAGE);
// }
// else {
// return (breakValue == Constants.ODD_PAGE);
// }
// }
// else {
// return true;
// }
}
private void createBodyMainReferenceArea() {
curBody.setMainReference(new MainReference());
}
private Flow createFlow() {
curFlow = new Flow();
// Set IPD and max BPD on the curFlow from curBody
curSpan.addFlow(curFlow);
return curFlow;
}
private void createSpan(int numCols) {
// check number of columns (= all in Body or 1)
// If already have a span, get its size and position (as MinMaxOpt)
// This determines the position of the new span area
// Attention: space calculation between the span areas.
// MinOptMax newpos ;
// if (curSpan != null) {
// newpos = curSpan.getPosition(BPD);
// newpos.add(curSpan.getDimension(BPD));
// }
// else newpos = new MinOptMax();
curSpan = new Span(numCols);
//curSpan.setPosition(BPD, newpos);
curBody.getMainReference().addSpan(curSpan);
createFlow();
}
// See finishPage...
protected void flush() {}
}
1.1 xml-fop/src/org/apache/fop/layoutmgr/SpaceSpecifier.java
Index: SpaceSpecifier.java
===================================================================
/*
* $Id: SpaceSpecifier.java,v 1.1 2001/11/09 21:57:47 klease Exp $
* Copyright (C) 2001 The Apache Software Foundation. All rights reserved.
* For details on use and redistribution please refer to the
* LICENSE file included with these sources.
*/
package org.apache.fop.layoutmgr;
import org.apache.fop.area.Area;
import org.apache.fop.area.MinOptMax;
import org.apache.fop.datatypes.Space;
/**
* Accumulate a sequence of space-specifiers (XSL space type) on
* areas with a stacking constraint. Provide a way to resolve these into
* a single MinOptMax value.
*/
public class SpaceSpecifier {
/**
* Combine passed space property value with any existing space.
*/
public void addSpace(Space moreSpace) {
}
public MinOptMax resolve() {
return new MinOptMax();
}
}
1.1 xml-fop/src/org/apache/fop/layoutmgr/SplitContext.java
Index: SplitContext.java
===================================================================
/*
* $Id: SplitContext.java,v 1.1 2001/11/09 21:57:47 klease Exp $
* Copyright (C) 2001 The Apache Software Foundation. All rights reserved.
* For details on use and redistribution please refer to the
* LICENSE file included with these sources.
*/
package org.apache.fop.layoutmgr;
import org.apache.fop.area.Area;
import org.apache.fop.area.MinOptMax;
public class SplitContext {
Area nextArea;
MinOptMax targetBPD;
public SplitContext(MinOptMax targetBPD) {
this.targetBPD = targetBPD;
nextArea = null;
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: fop-cvs-unsubscribe@xml.apache.org
For additional commands, e-mail: fop-cvs-help@xml.apache.org