/* * Copyright 1999-2006 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 java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.awt.Point; import java.awt.geom.Rectangle2D; import org.apache.fop.area.Area; import org.apache.fop.area.BlockViewport; import org.apache.fop.area.Block; import org.apache.fop.area.Trait; import org.apache.fop.fo.FONode; import org.apache.fop.fo.flow.BlockContainer; import org.apache.fop.fo.properties.CommonAbsolutePosition; import org.apache.fop.area.CTM; import org.apache.fop.datatypes.FODimension; import org.apache.fop.datatypes.Length; import org.apache.fop.traits.MinOptMax; import org.apache.fop.traits.SpaceVal; /** * LayoutManager for a block-container FO. */ public class BlockContainerLayoutManager extends BlockStackingLayoutManager implements ConditionalElementListener { private BlockViewport viewportBlockArea; private Block referenceArea; private CommonAbsolutePosition abProps; private FODimension relDims; private CTM absoluteCTM; private Length width; private Length height; //private int vpContentIPD; private int vpContentBPD; // When viewport should grow with the content. private boolean autoHeight = true; /* holds the (one-time use) fo:block space-before and -after properties. Large fo:blocks are split into multiple Area.Blocks to accomodate the subsequent regions (pages) they are placed on. space-before is applied at the beginning of the first Block and space-after at the end of the last Block used in rendering the fo:block. */ //TODO space-before|after: handle space-resolution rules private MinOptMax foBlockSpaceBefore; private MinOptMax foBlockSpaceAfter; private boolean discardBorderBefore; private boolean discardBorderAfter; private boolean discardPaddingBefore; private boolean discardPaddingAfter; private MinOptMax effSpaceBefore; private MinOptMax effSpaceAfter; /** * Create a new block container layout manager. * @param node block-container node to create the layout manager for. */ public BlockContainerLayoutManager(BlockContainer node) { super(node); } /** @see org.apache.fop.layoutmgr.LayoutManager#initialize() */ public void initialize() { abProps = getBlockContainerFO().getCommonAbsolutePosition(); foBlockSpaceBefore = new SpaceVal(getBlockContainerFO().getCommonMarginBlock() .spaceBefore, this).getSpace(); foBlockSpaceAfter = new SpaceVal(getBlockContainerFO().getCommonMarginBlock() .spaceAfter, this).getSpace(); startIndent = getBlockContainerFO().getCommonMarginBlock().startIndent.getValue(this); endIndent = getBlockContainerFO().getCommonMarginBlock().endIndent.getValue(this); boolean rotated = (getBlockContainerFO().getReferenceOrientation() % 180 != 0); if (rotated) { height = getBlockContainerFO().getInlineProgressionDimension() .getOptimum(this).getLength(); width = getBlockContainerFO().getBlockProgressionDimension() .getOptimum(this).getLength(); } else { height = getBlockContainerFO().getBlockProgressionDimension() .getOptimum(this).getLength(); width = getBlockContainerFO().getInlineProgressionDimension() .getOptimum(this).getLength(); } bpUnit = 0; //layoutProps.blockProgressionUnit; if (bpUnit == 0) { // use optimum space values adjustedSpaceBefore = getBlockContainerFO().getCommonMarginBlock() .spaceBefore.getSpace().getOptimum(this).getLength().getValue(this); adjustedSpaceAfter = getBlockContainerFO().getCommonMarginBlock() .spaceAfter.getSpace().getOptimum(this).getLength().getValue(this); } else { // use minimum space values adjustedSpaceBefore = getBlockContainerFO().getCommonMarginBlock() .spaceBefore.getSpace().getMinimum(this).getLength().getValue(this); adjustedSpaceAfter = getBlockContainerFO().getCommonMarginBlock() .spaceAfter.getSpace().getMinimum(this).getLength().getValue(this); } } private void resetSpaces() { this.discardBorderBefore = false; this.discardBorderAfter = false; this.discardPaddingBefore = false; this.discardPaddingAfter = false; this.effSpaceBefore = null; this.effSpaceAfter = null; } /** @return the content IPD */ protected int getRotatedIPD() { return getBlockContainerFO().getInlineProgressionDimension() .getOptimum(this).getLength().getValue(this); } private boolean needClip() { int overflow = getBlockContainerFO().getOverflow(); return (overflow == EN_HIDDEN || overflow == EN_ERROR_IF_OVERFLOW); } private int getSpaceBefore() { return foBlockSpaceBefore.opt; } private int getBPIndents() { int indents = 0; /* TODO This is wrong isn't it? indents += getBlockContainerFO().getCommonMarginBlock() .spaceBefore.getOptimum(this).getLength().getValue(this); indents += getBlockContainerFO().getCommonMarginBlock() .spaceAfter.getOptimum(this).getLength().getValue(this); */ indents += getBlockContainerFO().getCommonBorderPaddingBackground() .getBPPaddingAndBorder(false, this); return indents; } private boolean isAbsoluteOrFixed() { return (abProps.absolutePosition == EN_ABSOLUTE) || (abProps.absolutePosition == EN_FIXED); } private boolean isFixed() { return (abProps.absolutePosition == EN_FIXED); } /** @see org.apache.fop.layoutmgr.LayoutManager#getContentAreaBPD() */ public int getContentAreaBPD() { if (autoHeight) { return -1; } else { return this.vpContentBPD; } } /** @see org.apache.fop.layoutmgr.LayoutManager */ public LinkedList getNextKnuthElements(LayoutContext context, int alignment) { resetSpaces(); if (isAbsoluteOrFixed()) { return getNextKnuthElementsAbsolute(context, alignment); } autoHeight = false; //boolean rotated = (getBlockContainerFO().getReferenceOrientation() % 180 != 0); int maxbpd = context.getStackLimit().opt; int allocBPD; if (height.getEnum() == EN_AUTO || (!height.isAbsolute() && getAncestorBlockAreaBPD() <= 0)) { //auto height when height="auto" or "if that dimension is not specified explicitly //(i.e., it depends on content's blockprogression-dimension)" (XSL 1.0, 7.14.1) allocBPD = maxbpd; autoHeight = true; } else { allocBPD = height.getValue(this); //this is the content-height allocBPD += getBPIndents(); } vpContentBPD = allocBPD - getBPIndents(); referenceIPD = context.getRefIPD(); if (width.getEnum() == EN_AUTO) { updateContentAreaIPDwithOverconstrainedAdjust(); } else { int contentWidth = width.getValue(this); updateContentAreaIPDwithOverconstrainedAdjust(contentWidth); } double contentRectOffsetX = 0; contentRectOffsetX += getBlockContainerFO() .getCommonMarginBlock().startIndent.getValue(this); double contentRectOffsetY = 0; contentRectOffsetY += getBlockContainerFO() .getCommonBorderPaddingBackground().getBorderBeforeWidth(false); contentRectOffsetY += getBlockContainerFO() .getCommonBorderPaddingBackground().getPaddingBefore(false, this); Rectangle2D rect = new Rectangle2D.Double( contentRectOffsetX, contentRectOffsetY, getContentAreaIPD(), getContentAreaBPD()); relDims = new FODimension(0, 0); absoluteCTM = CTM.getCTMandRelDims(getBlockContainerFO().getReferenceOrientation(), getBlockContainerFO().getWritingMode(), rect, relDims); int availableIPD = referenceIPD - getIPIndents(); if (rect.getWidth() > availableIPD) { log.warn(FONode.decorateWithContextInfo( "The extent in inline-progression-direction (width) of a block-container is" + " bigger than the available space (" + rect.getWidth() + "mpt > " + context.getRefIPD() + "mpt)", getBlockContainerFO())); } MinOptMax stackLimit = new MinOptMax(relDims.bpd); LinkedList returnedList = null; LinkedList contentList = new LinkedList(); LinkedList returnList = new LinkedList(); if (!breakBeforeServed) { try { if (addKnuthElementsForBreakBefore(returnList, context)) { return returnList; } } finally { breakBeforeServed = true; } } if (!firstVisibleMarkServed) { addKnuthElementsForSpaceBefore(returnList, alignment); } addKnuthElementsForBorderPaddingBefore(returnList, !firstVisibleMarkServed); firstVisibleMarkServed = true; if (autoHeight) { //Spaces, border and padding to be repeated at each break addPendingMarks(context); BlockLevelLayoutManager curLM; // currently active LM BlockLevelLayoutManager prevLM = null; // previously active LM while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) { LayoutContext childLC = new LayoutContext(0); childLC.copyPendingMarksFrom(context); // curLM is a ? childLC.setStackLimit(MinOptMax.subtract(context .getStackLimit(), stackLimit)); childLC.setRefIPD(relDims.ipd); childLC.setWritingMode(getBlockContainerFO().getWritingMode()); // get elements from curLM returnedList = curLM.getNextKnuthElements(childLC, alignment); if (returnedList.size() == 1 && ((KnuthElement)returnedList.getFirst()).isForcedBreak()) { // a descendant of this block has break-before /* if (returnList.size() == 0) { // the first child (or its first child ...) has // break-before; // all this block, including space before, will be put in // the // following page bSpaceBeforeServed = false; }*/ contentList.addAll(returnedList); // "wrap" the Position inside each element // moving the elements from contentList to returnList returnedList = new LinkedList(); wrapPositionElements(contentList, returnList); return returnList; } else { if (prevLM != null) { // there is a block handled by prevLM // before the one handled by curLM if (mustKeepTogether() || prevLM.mustKeepWithNext() || curLM.mustKeepWithPrevious()) { // add an infinite penalty to forbid a break between // blocks contentList.add(new BreakElement( new Position(this), KnuthElement.INFINITE, context)); //contentList.add(new KnuthPenalty(0, // KnuthElement.INFINITE, false, // new Position(this), false)); } else if (!((KnuthElement) contentList.getLast()).isGlue()) { // add a null penalty to allow a break between blocks contentList.add(new BreakElement( new Position(this), 0, context)); //contentList.add(new KnuthPenalty(0, 0, false, // new Position(this), false)); } else { // the last element in contentList is a glue; // it is a feasible breakpoint, there is no need to add // a penalty } } contentList.addAll(returnedList); if (returnedList.size() == 0) { //Avoid NoSuchElementException below (happens with empty blocks) continue; } if (((ListElement)returnedList.getLast()).isForcedBreak()) { // a descendant of this block has break-after if (curLM.isFinished()) { // there is no other content in this block; // it's useless to add space after before a page break setFinished(true); } returnedList = new LinkedList(); wrapPositionElements(contentList, returnList); return returnList; } } prevLM = curLM; } returnedList = new LinkedList(); wrapPositionElements(contentList, returnList); } else { MinOptMax range = new MinOptMax(relDims.ipd); BlockContainerBreaker breaker = new BlockContainerBreaker(this, range); breaker.doLayout(relDims.bpd); boolean contentOverflows = false; if (!breaker.isEmpty()) { contentOverflows = (breaker.deferredAlg.getPageBreaks().size() > 1); } Position bcPosition = new BlockContainerPosition(this, breaker); returnList.add(new KnuthBox(vpContentBPD, notifyPos(bcPosition), false)); //TODO Handle min/opt/max for block-progression-dimension /* These two elements will be used to add stretchability to the above box returnList.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, returnPosition, false)); returnList.add(new KnuthGlue(0, 1 * constantLineHeight, 0, LINE_NUMBER_ADJUSTMENT, returnPosition, false)); */ if (contentOverflows) { log.warn("Contents overflow block-container viewport: clipping"); if (getBlockContainerFO().getOverflow() == EN_ERROR_IF_OVERFLOW) { //TODO Throw layout exception } } } addKnuthElementsForBorderPaddingAfter(returnList, true); addKnuthElementsForSpaceAfter(returnList, alignment); addKnuthElementsForBreakAfter(returnList, context); setFinished(true); return returnList; } private LinkedList getNextKnuthElementsAbsolute(LayoutContext context, int alignment) { autoHeight = false; Point offset = getAbsOffset(); int allocBPD, allocIPD; if (height.getEnum() == EN_AUTO || (!height.isAbsolute() && getAncestorBlockAreaBPD() <= 0)) { //auto height when height="auto" or "if that dimension is not specified explicitly //(i.e., it depends on content's blockprogression-dimension)" (XSL 1.0, 7.14.1) allocBPD = 0; if (abProps.bottom.getEnum() != EN_AUTO) { int availHeight; if (isFixed()) { availHeight = (int)getCurrentPV().getViewArea().getHeight(); } else { availHeight = context.getStackLimit().opt; } allocBPD = availHeight; allocBPD -= offset.y; if (abProps.bottom.getEnum() != EN_AUTO) { allocBPD -= abProps.bottom.getValue(this); if (allocBPD < 0) { //TODO Fix absolute b-c layout, layout may need to be defferred until //after page breaking when the size of the containing box is known. /* Warning disabled due to a interpretation mistake. * See: http://marc.theaimsgroup.com/?l=fop-dev&m=113189981926163&w=2 log.error("The current combination of top and bottom properties results" + " in a negative extent for the block-container. 'bottom' may be" + " at most " + (allocBPD + abProps.bottom.getValue(this)) + " mpt," + " but was actually " + abProps.bottom.getValue(this) + " mpt." + " The nominal available height is " + availHeight + " mpt."); */ allocBPD = 0; } } else { if (allocBPD < 0) { /* Warning disabled due to a interpretation mistake. * See: http://marc.theaimsgroup.com/?l=fop-dev&m=113189981926163&w=2 log.error("The current combination of top and bottom properties results" + " in a negative extent for the block-container. 'top' may be" + " at most " + availHeight + " mpt," + " but was actually " + offset.y + " mpt." + " The nominal available height is " + availHeight + " mpt."); */ allocBPD = 0; } } } else { autoHeight = true; } } else { allocBPD = height.getValue(this); //this is the content-height allocBPD += getBPIndents(); } if (width.getEnum() == EN_AUTO) { int availWidth; if (isFixed()) { availWidth = (int)getCurrentPV().getViewArea().getWidth(); } else { availWidth = context.getRefIPD(); } allocIPD = availWidth; if (abProps.left.getEnum() != EN_AUTO) { allocIPD -= abProps.left.getValue(this); } if (abProps.right.getEnum() != EN_AUTO) { allocIPD -= abProps.right.getValue(this); if (allocIPD < 0) { /* Warning disabled due to a interpretation mistake. * See: http://marc.theaimsgroup.com/?l=fop-dev&m=113189981926163&w=2 log.error("The current combination of left and right properties results" + " in a negative extent for the block-container. 'right' may be" + " at most " + (allocIPD + abProps.right.getValue(this)) + " mpt," + " but was actually " + abProps.right.getValue(this) + " mpt." + " The nominal available width is " + availWidth + " mpt."); */ allocIPD = 0; } } else { if (allocIPD < 0) { /* Warning disabled due to a interpretation mistake. * See: http://marc.theaimsgroup.com/?l=fop-dev&m=113189981926163&w=2 log.error("The current combination of left and right properties results" + " in a negative extent for the block-container. 'left' may be" + " at most " + allocIPD + " mpt," + " but was actually " + abProps.left.getValue(this) + " mpt." + " The nominal available width is " + availWidth + " mpt."); */ allocIPD = 0; } } } else { allocIPD = width.getValue(this); //this is the content-width allocIPD += getIPIndents(); } vpContentBPD = allocBPD - getBPIndents(); setContentAreaIPD(allocIPD - getIPIndents()); double contentRectOffsetX = offset.getX(); contentRectOffsetX += getBlockContainerFO() .getCommonMarginBlock().startIndent.getValue(this); double contentRectOffsetY = offset.getY(); contentRectOffsetY += getSpaceBefore(); //TODO Uhm, is that necessary? contentRectOffsetY += getBlockContainerFO() .getCommonBorderPaddingBackground().getBorderBeforeWidth(false); contentRectOffsetY += getBlockContainerFO() .getCommonBorderPaddingBackground().getPaddingBefore(false, this); Rectangle2D rect = new Rectangle2D.Double( contentRectOffsetX, contentRectOffsetY, getContentAreaIPD(), vpContentBPD); relDims = new FODimension(0, 0); absoluteCTM = CTM.getCTMandRelDims( getBlockContainerFO().getReferenceOrientation(), getBlockContainerFO().getWritingMode(), rect, relDims); MinOptMax range = new MinOptMax(relDims.ipd); BlockContainerBreaker breaker = new BlockContainerBreaker(this, range); breaker.doLayout(relDims.bpd); boolean contentOverflows = breaker.isOverflow(); LinkedList returnList = new LinkedList(); if (!breaker.isEmpty()) { Position bcPosition = new BlockContainerPosition(this, breaker); returnList.add(new KnuthBox(0, notifyPos(bcPosition), false)); //TODO Maybe check for page overflow when autoHeight=true if (!autoHeight & (contentOverflows/*usedBPD > relDims.bpd*/)) { log.warn("Contents overflow block-container viewport: clipping"); if (getBlockContainerFO().getOverflow() == EN_ERROR_IF_OVERFLOW) { //TODO Throw layout exception } } } setFinished(true); return returnList; } private class BlockContainerPosition extends NonLeafPosition { private BlockContainerBreaker breaker; public BlockContainerPosition(LayoutManager lm, BlockContainerBreaker breaker) { super(lm, null); this.breaker = breaker; } public BlockContainerBreaker getBreaker() { return this.breaker; } } private class BlockContainerBreaker extends AbstractBreaker { private BlockContainerLayoutManager bclm; private MinOptMax ipd; //Info for deferred adding of areas private PageBreakingAlgorithm deferredAlg; private BlockSequence deferredOriginalList; private BlockSequence deferredEffectiveList; public BlockContainerBreaker(BlockContainerLayoutManager bclm, MinOptMax ipd) { this.bclm = bclm; this.ipd = ipd; } /** @see org.apache.fop.layoutmgr.AbstractBreaker#observeElementList(java.util.List) */ protected void observeElementList(List elementList) { ElementListObserver.observe(elementList, "block-container", bclm.getBlockContainerFO().getId()); } /** @see org.apache.fop.layoutmgr.AbstractBreaker#isPartOverflowRecoveryActivated() */ protected boolean isPartOverflowRecoveryActivated() { //For block-containers, this must be disabled because of wanted overflow. return false; } public int getDifferenceOfFirstPart() { PageBreakPosition pbp = (PageBreakPosition)this.deferredAlg.getPageBreaks().getFirst(); return pbp.difference; } public boolean isOverflow() { if (isEmpty()) { return false; } else { return (deferredAlg.getPageBreaks().size() > 1); } } protected LayoutManager getTopLevelLM() { return bclm; } protected LayoutContext createLayoutContext() { LayoutContext lc = super.createLayoutContext(); lc.setRefIPD(ipd.opt); lc.setWritingMode(getBlockContainerFO().getWritingMode()); return lc; } protected LinkedList getNextKnuthElements(LayoutContext context, int alignment) { LayoutManager curLM; // currently active LM LinkedList returnList = new LinkedList(); while ((curLM = getChildLM()) != null) { LayoutContext childLC = new LayoutContext(0); childLC.setStackLimit(context.getStackLimit()); childLC.setRefIPD(context.getRefIPD()); childLC.setWritingMode(getBlockContainerFO().getWritingMode()); LinkedList returnedList = null; if (!curLM.isFinished()) { returnedList = curLM.getNextKnuthElements(childLC, alignment); } if (returnedList != null) { bclm.wrapPositionElements(returnedList, returnList); } } SpaceResolver.resolveElementList(returnList); setFinished(true); return returnList; } protected int getCurrentDisplayAlign() { return getBlockContainerFO().getDisplayAlign(); } protected boolean hasMoreContent() { return !isFinished(); } protected void addAreas(PositionIterator posIter, LayoutContext context) { AreaAdditionUtil.addAreas(bclm, posIter, context); } protected void doPhase3(PageBreakingAlgorithm alg, int partCount, BlockSequence originalList, BlockSequence effectiveList) { //Defer adding of areas until addAreas is called by the parent LM this.deferredAlg = alg; this.deferredOriginalList = originalList; this.deferredEffectiveList = effectiveList; } protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) { //nop for bclm } protected LayoutManager getCurrentChildLM() { return curChildLM; } public void addContainedAreas() { if (isEmpty()) { return; } //Rendering all parts (not just the first) at once for the case where the parts that //overflow should be visible. //TODO Check if this has any unwanted side-effects. Feels a bit like a hack. this.addAreas(this.deferredAlg, /*1*/ this.deferredAlg.getPageBreaks().size(), this.deferredOriginalList, this.deferredEffectiveList); } } private Point getAbsOffset() { int x = 0; int y = 0; if (abProps.left.getEnum() != EN_AUTO) { x = abProps.left.getValue(this); } if (abProps.top.getEnum() != EN_AUTO) { y = abProps.top.getValue(this); } return new Point(x, y); } /** @see org.apache.fop.layoutmgr.LayoutManager */ public void addAreas(PositionIterator parentIter, LayoutContext layoutContext) { getParentArea(null); // if this will create the first block area in a page // and display-align is bottom or center, add space before if (layoutContext.getSpaceBefore() > 0) { addBlockSpacing(0.0, new MinOptMax(layoutContext.getSpaceBefore())); } LayoutManager childLM = null; LayoutManager lastLM = null; LayoutContext lc = new LayoutContext(0); lc.setSpaceAdjust(layoutContext.getSpaceAdjust()); // set space after in the LayoutContext for children if (layoutContext.getSpaceAfter() > 0) { lc.setSpaceAfter(layoutContext.getSpaceAfter()); } BlockContainerPosition bcpos = null; PositionIterator childPosIter; // "unwrap" the NonLeafPositions stored in parentIter // and put them in a new list; LinkedList positionList = new LinkedList(); Position pos; boolean bSpaceBefore = false; boolean bSpaceAfter = false; Position firstPos = null; Position lastPos = null; while (parentIter.hasNext()) { pos = (Position) parentIter.next(); if (pos.getIndex() >= 0) { if (firstPos == null) { firstPos = pos; } lastPos = pos; } Position innerPosition = null; if (pos instanceof NonLeafPosition) { innerPosition = ((NonLeafPosition)pos).getPosition(); } if (pos instanceof BlockContainerPosition) { if (bcpos != null) { throw new IllegalStateException("Only one BlockContainerPosition allowed"); } bcpos = (BlockContainerPosition)pos; //Add child areas inside the reference area //bcpos.getBreaker().addContainedAreas(); } else if (innerPosition == null) { if (pos instanceof NonLeafPosition) { // pos was created by this BCLM and was inside an element // representing space before or after // this means the space was not discarded if (positionList.size() == 0 && bcpos == null) { // pos was in the element representing space-before bSpaceBefore = true; } else { // pos was in the element representing space-after bSpaceAfter = true; } } else { //ignore (probably a Position for a simple penalty between blocks) } } else if (innerPosition.getLM() == this && !(innerPosition instanceof MappingPosition)) { // pos was created by this BlockLM and was inside a penalty // allowing or forbidding a page break // nothing to do } else { // innerPosition was created by another LM positionList.add(innerPosition); lastLM = innerPosition.getLM(); } } getPSLM().addIDToPage(getBlockContainerFO().getId()); if (markers != null) { getCurrentPV().addMarkers(markers, true, isFirst(firstPos), isLast(lastPos)); } if (bcpos == null) { if (bpUnit == 0) { // the Positions in positionList were inside the elements // created by the LineLM childPosIter = new StackingIter(positionList.listIterator()); } else { // the Positions in positionList were inside the elements // created by the BCLM in the createUnitElements() method //if (((Position) positionList.getLast()) instanceof // LeafPosition) { // // the last item inside positionList is a LeafPosition // // (a LineBreakPosition, more precisely); this means that // // the whole paragraph is on the same page // childPosIter = new KnuthPossPosIter(storedList, 0, // storedList.size()); //} else { // // the last item inside positionList is a Position; // // this means that the paragraph has been split // // between consecutive pages LinkedList splitList = new LinkedList(); int splitLength = 0; int iFirst = ((MappingPosition) positionList.getFirst()).getFirstIndex(); int iLast = ((MappingPosition) positionList.getLast()).getLastIndex(); // copy from storedList to splitList all the elements from // iFirst to iLast ListIterator storedListIterator = storedList.listIterator(iFirst); while (storedListIterator.nextIndex() <= iLast) { KnuthElement element = (KnuthElement) storedListIterator .next(); // some elements in storedList (i.e. penalty items) were created // by this BlockLM, and must be ignored if (element.getLayoutManager() != this) { splitList.add(element); splitLength += element.getW(); lastLM = element.getLayoutManager(); } } //log.debug("Adding areas from " + iFirst + " to " + iLast); //log.debug("splitLength= " + splitLength // + " (" + neededUnits(splitLength) + " units') " // + (neededUnits(splitLength) * bpUnit - splitLength) // + " spacing"); // add space before and / or after the paragraph // to reach a multiple of bpUnit if (bSpaceBefore && bSpaceAfter) { foBlockSpaceBefore = new SpaceVal(getBlockContainerFO() .getCommonMarginBlock().spaceBefore, this).getSpace(); foBlockSpaceAfter = new SpaceVal(getBlockContainerFO() .getCommonMarginBlock().spaceAfter, this).getSpace(); adjustedSpaceBefore = (neededUnits(splitLength + foBlockSpaceBefore.min + foBlockSpaceAfter.min) * bpUnit - splitLength) / 2; adjustedSpaceAfter = neededUnits(splitLength + foBlockSpaceBefore.min + foBlockSpaceAfter.min) * bpUnit - splitLength - adjustedSpaceBefore; } else if (bSpaceBefore) { adjustedSpaceBefore = neededUnits(splitLength + foBlockSpaceBefore.min) * bpUnit - splitLength; } else { adjustedSpaceAfter = neededUnits(splitLength + foBlockSpaceAfter.min) * bpUnit - splitLength; } //log.debug("space before = " + adjustedSpaceBefore // + " space after = " + adjustedSpaceAfter + " total = " + // (adjustedSpaceBefore + adjustedSpaceAfter + splitLength)); childPosIter = new KnuthPossPosIter(splitList, 0, splitList .size()); //} } while ((childLM = childPosIter.getNextChildLM()) != null) { // set last area flag lc.setFlags(LayoutContext.LAST_AREA, (layoutContext.isLastArea() && childLM == lastLM)); /*LF*/lc.setStackLimit(layoutContext.getStackLimit()); // Add the line areas to Area childLM.addAreas(childPosIter, lc); } } else { //Add child areas inside the reference area bcpos.getBreaker().addContainedAreas(); } if (markers != null) { getCurrentPV().addMarkers(markers, false, isFirst(firstPos), isLast(lastPos)); } TraitSetter.addSpaceBeforeAfter(viewportBlockArea, layoutContext.getSpaceAdjust(), effSpaceBefore, effSpaceAfter); flush(); viewportBlockArea = null; referenceArea = null; resetSpaces(); } /** * Get the parent area for children of this block container. * This returns the current block container area * and creates it if required. * * @see org.apache.fop.layoutmgr.LayoutManager#getParentArea(Area) */ public Area getParentArea(Area childArea) { if (referenceArea == null) { viewportBlockArea = new BlockViewport(); viewportBlockArea.addTrait(Trait.IS_VIEWPORT_AREA, Boolean.TRUE); viewportBlockArea.setIPD(getContentAreaIPD()); if (autoHeight) { viewportBlockArea.setBPD(0); } else { viewportBlockArea.setBPD(getContentAreaBPD()); } TraitSetter.setProducerID(viewportBlockArea, getBlockContainerFO().getId()); TraitSetter.addBorders(viewportBlockArea, getBlockContainerFO().getCommonBorderPaddingBackground(), discardBorderBefore, discardBorderAfter, false, false, this); TraitSetter.addPadding(viewportBlockArea, getBlockContainerFO().getCommonBorderPaddingBackground(), discardPaddingBefore, discardPaddingAfter, false, false, this); // TraitSetter.addBackground(viewportBlockArea, // getBlockContainerFO().getCommonBorderPaddingBackground(), // this); TraitSetter.addMargins(viewportBlockArea, getBlockContainerFO().getCommonBorderPaddingBackground(), startIndent, endIndent, this); viewportBlockArea.setCTM(absoluteCTM); viewportBlockArea.setClip(needClip()); /* if (getSpaceBefore() != 0) { viewportBlockArea.addTrait(Trait.SPACE_BEFORE, new Integer(getSpaceBefore())); } if (foBlockSpaceAfter.opt != 0) { viewportBlockArea.addTrait(Trait.SPACE_AFTER, new Integer(foBlockSpaceAfter.opt)); }*/ if (abProps.absolutePosition == EN_ABSOLUTE || abProps.absolutePosition == EN_FIXED) { Point offset = getAbsOffset(); viewportBlockArea.setXOffset(offset.x); viewportBlockArea.setYOffset(offset.y); } else { //nop } referenceArea = new Block(); referenceArea.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE); TraitSetter.setProducerID(referenceArea, getBlockContainerFO().getId()); if (abProps.absolutePosition == EN_ABSOLUTE) { viewportBlockArea.setPositioning(Block.ABSOLUTE); } else if (abProps.absolutePosition == EN_FIXED) { viewportBlockArea.setPositioning(Block.FIXED); } // Set up dimensions // Must get dimensions from parent area /*Area parentArea =*/ parentLM.getParentArea(referenceArea); //int referenceIPD = parentArea.getIPD(); referenceArea.setIPD(relDims.ipd); // Get reference IPD from parentArea setCurrentArea(viewportBlockArea); // ??? for generic operations } return referenceArea; } /** * Add the child to the block container. * * @see org.apache.fop.layoutmgr.LayoutManager#addChildArea(Area) */ public void addChildArea(Area childArea) { if (referenceArea != null) { referenceArea.addBlock((Block) childArea); } } /** * @see org.apache.fop.layoutmgr.LayoutManager#resetPosition(org.apache.fop.layoutmgr.Position) */ public void resetPosition(Position resetPos) { if (resetPos == null) { reset(null); } } /** * Force current area to be added to parent area. * @see org.apache.fop.layoutmgr.BlockStackingLayoutManager#flush() */ protected void flush() { viewportBlockArea.addBlock(referenceArea, autoHeight); TraitSetter.addBackground(viewportBlockArea, getBlockContainerFO().getCommonBorderPaddingBackground(), this); // Fake a 0 height for absolute positioned blocks. int saveBPD = viewportBlockArea.getBPD(); if (viewportBlockArea.getPositioning() == Block.ABSOLUTE) { viewportBlockArea.setBPD(0); } super.flush(); // Restore the right height. if (viewportBlockArea.getPositioning() == Block.ABSOLUTE) { viewportBlockArea.setBPD(saveBPD); } } /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager */ public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) { // TODO Auto-generated method stub return 0; } /** @see org.apache.fop.layoutmgr.BlockLevelLayoutManager */ public void discardSpace(KnuthGlue spaceGlue) { // TODO Auto-generated method stub } /** * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepTogether() */ public boolean mustKeepTogether() { //TODO Keeps will have to be more sophisticated sooner or later return ((BlockLevelLayoutManager)getParent()).mustKeepTogether() || !getBlockContainerFO().getKeepTogether().getWithinPage().isAuto() || !getBlockContainerFO().getKeepTogether().getWithinColumn().isAuto(); } /** * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithPrevious() */ public boolean mustKeepWithPrevious() { return !getBlockContainerFO().getKeepWithPrevious().getWithinPage().isAuto() || !getBlockContainerFO().getKeepWithPrevious().getWithinColumn().isAuto(); } /** * @see org.apache.fop.layoutmgr.BlockLevelLayoutManager#mustKeepWithNext() */ public boolean mustKeepWithNext() { return !getBlockContainerFO().getKeepWithNext().getWithinPage().isAuto() || !getBlockContainerFO().getKeepWithNext().getWithinColumn().isAuto(); } /** * @return the BlockContainer node */ protected BlockContainer getBlockContainerFO() { return (BlockContainer) fobj; } // --------- Property Resolution related functions --------- // /** * @see org.apache.fop.layoutmgr.LayoutManager#getGeneratesReferenceArea */ public boolean getGeneratesReferenceArea() { return true; } /** * @see org.apache.fop.layoutmgr.LayoutManager#getGeneratesBlockArea */ public boolean getGeneratesBlockArea() { return true; } /** @see org.apache.fop.layoutmgr.ConditionalElementListener */ public void notifySpace(RelSide side, MinOptMax effectiveLength) { if (RelSide.BEFORE == side) { if (log.isDebugEnabled()) { log.debug(this + ": Space " + side + ", " + this.effSpaceBefore + "-> " + effectiveLength); } this.effSpaceBefore = effectiveLength; } else { if (log.isDebugEnabled()) { log.debug(this + ": Space " + side + ", " + this.effSpaceAfter + "-> " + effectiveLength); } this.effSpaceAfter = effectiveLength; } } /** @see org.apache.fop.layoutmgr.ConditionalElementListener */ public void notifyBorder(RelSide side, MinOptMax effectiveLength) { if (effectiveLength == null) { if (RelSide.BEFORE == side) { this.discardBorderBefore = true; } else { this.discardBorderAfter = true; } } if (log.isDebugEnabled()) { log.debug(this + ": Border " + side + " -> " + effectiveLength); } } /** @see org.apache.fop.layoutmgr.ConditionalElementListener */ public void notifyPadding(RelSide side, MinOptMax effectiveLength) { if (effectiveLength == null) { if (RelSide.BEFORE == side) { this.discardPaddingBefore = true; } else { this.discardPaddingAfter = true; } } if (log.isDebugEnabled()) { log.debug(this + ": Padding " + side + " -> " + effectiveLength); } } }