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: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]