| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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.awt.Point; |
| import java.awt.geom.Rectangle2D; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Stack; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| |
| import org.apache.fop.area.Area; |
| import org.apache.fop.area.Block; |
| import org.apache.fop.area.BlockViewport; |
| import org.apache.fop.area.CTM; |
| import org.apache.fop.area.Trait; |
| import org.apache.fop.datatypes.FODimension; |
| import org.apache.fop.datatypes.Length; |
| import org.apache.fop.fo.flow.BlockContainer; |
| import org.apache.fop.fo.properties.CommonAbsolutePosition; |
| import org.apache.fop.fo.properties.CommonBorderPaddingBackground; |
| import org.apache.fop.fo.properties.KeepProperty; |
| import org.apache.fop.traits.MinOptMax; |
| import org.apache.fop.traits.SpaceVal; |
| |
| /** |
| * LayoutManager for a block-container FO. |
| */ |
| public class BlockContainerLayoutManager extends SpacedBorderedPaddedBlockLayoutManager |
| implements BreakOpportunity { |
| |
| /** |
| * logging instance |
| */ |
| private static Log log = LogFactory.getLog(BlockContainerLayoutManager.class); |
| |
| 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; |
| private boolean inlineElementList; |
| |
| /* 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 int horizontalOverflow; |
| private double contentRectOffsetX; |
| private double contentRectOffsetY; |
| |
| /** |
| * Create a new block container layout manager. |
| * @param node block-container node to create the layout manager for. |
| */ |
| public BlockContainerLayoutManager(BlockContainer node) { |
| super(node); |
| setGeneratesBlockArea(true); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| 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); |
| |
| if (blockProgressionDirectionChanges()) { |
| 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(); |
| } |
| |
| // use optimum space values |
| adjustedSpaceBefore = getBlockContainerFO().getCommonMarginBlock() |
| .spaceBefore.getSpace().getOptimum(this).getLength().getValue(this); |
| adjustedSpaceAfter = getBlockContainerFO().getCommonMarginBlock() |
| .spaceAfter.getSpace().getOptimum(this).getLength().getValue(this); |
| } |
| |
| @Override |
| protected CommonBorderPaddingBackground getCommonBorderPaddingBackground() { |
| return getBlockContainerFO().getCommonBorderPaddingBackground(); |
| } |
| |
| 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 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; |
| } |
| |
| protected boolean isAbsoluteOrFixed() { |
| return (abProps.absolutePosition == EN_ABSOLUTE |
| || abProps.absolutePosition == EN_FIXED); |
| } |
| |
| private boolean isFixed() { |
| return (abProps.absolutePosition == EN_FIXED); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public int getContentAreaBPD() { |
| if (autoHeight) { |
| return -1; |
| } else { |
| return this.vpContentBPD; |
| } |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public List getNextKnuthElements(LayoutContext context, int alignment) { |
| return getNextKnuthElements(context, alignment, null, null, null); |
| } |
| |
| /** |
| * Overridden to handle writing-mode, and different stack limit |
| * setup. |
| * {@inheritDoc} |
| */ |
| @Override |
| protected LayoutContext makeChildLayoutContext(LayoutContext context) { |
| LayoutContext childLC = LayoutContext.newInstance(); |
| childLC.setStackLimitBP( |
| context.getStackLimitBP().minus(MinOptMax.getInstance(relDims.bpd))); |
| childLC.setRefIPD(relDims.ipd); |
| childLC.setWritingMode(getBlockContainerFO().getWritingMode()); |
| return childLC; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack, |
| Position restartPosition, LayoutManager restartAtLM) { |
| |
| resetSpaces(); |
| // special treatment for position="absolute|fixed" |
| if (isAbsoluteOrFixed()) { |
| return getNextKnuthElementsAbsolute(context); |
| } |
| |
| boolean isRestart = (lmStack != null); |
| boolean emptyStack = (!isRestart || lmStack.isEmpty()); |
| |
| setupAreaDimensions(context); |
| |
| List<ListElement> returnedList; |
| List<ListElement> contentList = new LinkedList<ListElement>(); |
| List<ListElement> returnList = new LinkedList<ListElement>(); |
| |
| if (!breakBeforeServed(context, returnList)) { |
| return returnList; |
| } |
| |
| addFirstVisibleMarks(returnList, context, alignment); |
| |
| if (autoHeight && inlineElementList) { |
| |
| LayoutManager curLM; // currently active LM |
| LayoutManager prevLM = null; // previously active LM |
| |
| LayoutContext childLC; |
| if (isRestart) { |
| if (emptyStack) { |
| assert restartAtLM != null && restartAtLM.getParent() == this; |
| curLM = restartAtLM; |
| } else { |
| curLM = (LayoutManager) lmStack.pop(); |
| } |
| setCurrentChildLM(curLM); |
| } else { |
| curLM = getChildLM(); |
| } |
| |
| while (curLM != null) { |
| childLC = makeChildLayoutContext(context); |
| |
| // get elements from curLM |
| if (!isRestart || emptyStack) { |
| if (isRestart) { |
| curLM.reset(); |
| } |
| returnedList = getNextChildElements(curLM, context, childLC, alignment, |
| null, null, null); |
| } else { |
| returnedList = getNextChildElements(curLM, context, childLC, alignment, |
| lmStack, restartPosition, restartAtLM); |
| // once encountered, irrelevant for following child LMs |
| emptyStack = true; |
| } |
| if (contentList.isEmpty() && childLC.isKeepWithPreviousPending()) { |
| //Propagate keep-with-previous up from the first child |
| context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending()); |
| childLC.clearKeepWithPreviousPending(); |
| } |
| if (returnedList.size() == 1 |
| && ElementListUtils.startsWithForcedBreak(returnedList)) { |
| // a descendant of this block has break-before |
| contentList.addAll(returnedList); |
| |
| // "wrap" the Position inside each element |
| // moving the elements from contentList to returnList |
| wrapPositionElements(contentList, returnList); |
| |
| return returnList; |
| } else { |
| if (prevLM != null) { |
| // there is a block handled by prevLM |
| // before the one handled by curLM |
| addInBetweenBreak(contentList, context, childLC); |
| } |
| contentList.addAll(returnedList); |
| if (returnedList.isEmpty()) { |
| //Avoid NoSuchElementException below (happens with empty blocks) |
| continue; |
| } |
| if (ElementListUtils.endsWithForcedBreak(returnedList)) { |
| // a descendant of this block has break-after |
| if (curLM.isFinished() && !hasNextChildLM()) { |
| // there is no other content in this block; |
| // it's useless to add space after before a page break |
| setFinished(true); |
| } |
| |
| wrapPositionElements(contentList, returnList); |
| return returnList; |
| } |
| } |
| // propagate and clear |
| context.updateKeepWithNextPending(childLC.getKeepWithNextPending()); |
| childLC.clearKeepsPending(); |
| prevLM = curLM; |
| curLM = getChildLM(); |
| } |
| wrapPositionElements(contentList, returnList); |
| } else { |
| returnList.add(generateNonInlinedBox()); |
| } |
| |
| addLastVisibleMarks(returnList, context, alignment); |
| |
| addKnuthElementsForBreakAfter(returnList, context); |
| |
| context.updateKeepWithNextPending(getKeepWithNext()); |
| |
| setFinished(true); |
| return returnList; |
| } |
| |
| private void setupAreaDimensions(LayoutContext context) { |
| autoHeight = false; |
| int maxbpd = context.getStackLimitBP().getOpt(); |
| int allocBPD; |
| BlockContainer fo = getBlockContainerFO(); |
| 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 block-progression-dimension)" (XSL 1.0, 7.14.1) |
| allocBPD = maxbpd; |
| autoHeight = true; |
| //Cannot easily inline element list when ref-or<>"0" |
| inlineElementList = (fo.getReferenceOrientation() == 0); |
| } 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); |
| } |
| |
| contentRectOffsetX = 0; |
| contentRectOffsetY = 0; |
| |
| int level = fo.getBidiLevel(); |
| if ((level < 0) || ((level & 1) == 0)) { |
| contentRectOffsetX += fo.getCommonMarginBlock().startIndent.getValue(this); |
| } else { |
| contentRectOffsetX += fo.getCommonMarginBlock().endIndent.getValue(this); |
| } |
| contentRectOffsetY += fo.getCommonBorderPaddingBackground().getBorderBeforeWidth(false); |
| contentRectOffsetY += fo.getCommonBorderPaddingBackground().getPaddingBefore(false, this); |
| |
| updateRelDims(); |
| |
| int availableIPD = referenceIPD - getIPIndents(); |
| if (getContentAreaIPD() > availableIPD) { |
| BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get( |
| fo.getUserAgent().getEventBroadcaster()); |
| eventProducer.objectTooWide(this, fo.getName(), |
| getContentAreaIPD(), context.getRefIPD(), |
| fo.getLocator()); |
| } |
| } |
| |
| private KnuthBox generateNonInlinedBox() { |
| |
| MinOptMax range = MinOptMax.getInstance(relDims.ipd); |
| BlockContainerBreaker breaker = new BlockContainerBreaker(this, range); |
| breaker.doLayout(relDims.bpd, autoHeight); |
| boolean contentOverflows = breaker.isOverflow(); |
| if (autoHeight) { |
| //Update content BPD now that it is known |
| int newHeight = breaker.deferredAlg.totalWidth; |
| if (blockProgressionDirectionChanges()) { |
| setContentAreaIPD(newHeight); |
| } else { |
| vpContentBPD = newHeight; |
| } |
| updateRelDims(); |
| } |
| |
| Position bcPosition = new BlockContainerPosition(this, breaker); |
| KnuthBox knuthBox = 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) { |
| BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get( |
| getBlockContainerFO().getUserAgent().getEventBroadcaster()); |
| boolean canRecover = (getBlockContainerFO().getOverflow() != EN_ERROR_IF_OVERFLOW); |
| eventProducer.viewportBPDOverflow(this, getBlockContainerFO().getName(), |
| breaker.getOverflowAmount(), needClip(), canRecover, |
| getBlockContainerFO().getLocator()); |
| } |
| return knuthBox; |
| } |
| |
| private boolean blockProgressionDirectionChanges() { |
| return getBlockContainerFO().getReferenceOrientation() % 180 != 0; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public boolean isRestartable() { |
| return true; |
| } |
| |
| private List<ListElement> getNextKnuthElementsAbsolute(LayoutContext context) { |
| autoHeight = false; |
| |
| boolean bpDirectionChanges = blockProgressionDirectionChanges(); |
| Point offset = getAbsOffset(); |
| int allocBPD; |
| int 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.getStackLimitBP().getOpt(); |
| } |
| 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 { |
| allocBPD = context.getStackLimitBP().getOpt(); |
| if (!bpDirectionChanges) { |
| 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; |
| } |
| if (bpDirectionChanges) { |
| autoHeight = true; |
| } |
| } |
| } else { |
| allocIPD = width.getValue(this); //this is the content-width |
| allocIPD += getIPIndents(); |
| } |
| |
| vpContentBPD = allocBPD - getBPIndents(); |
| setContentAreaIPD(allocIPD - getIPIndents()); |
| |
| contentRectOffsetX = 0; |
| contentRectOffsetY = 0; |
| updateRelDims(); |
| |
| MinOptMax range = MinOptMax.getInstance(relDims.ipd); |
| BlockContainerBreaker breaker = new BlockContainerBreaker(this, range); |
| breaker.doLayout((autoHeight ? 0 : relDims.bpd), autoHeight); |
| boolean contentOverflows = breaker.isOverflow(); |
| if (autoHeight) { |
| //Update content BPD now that it is known |
| int newHeight = breaker.deferredAlg.totalWidth; |
| if (bpDirectionChanges) { |
| setContentAreaIPD(newHeight); |
| } else { |
| vpContentBPD = newHeight; |
| } |
| updateRelDims(); |
| } |
| List<ListElement> returnList = new LinkedList<ListElement>(); |
| 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)) { |
| BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get( |
| getBlockContainerFO().getUserAgent().getEventBroadcaster()); |
| boolean canRecover = (getBlockContainerFO().getOverflow() != EN_ERROR_IF_OVERFLOW); |
| eventProducer.viewportBPDOverflow(this, getBlockContainerFO().getName(), |
| breaker.getOverflowAmount(), needClip(), canRecover, |
| getBlockContainerFO().getLocator()); |
| } |
| // this handles the IPD (horizontal) overflow |
| if (this.horizontalOverflow > 0) { |
| BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider |
| .get(getBlockContainerFO().getUserAgent().getEventBroadcaster()); |
| boolean canRecover = (getBlockContainerFO().getOverflow() != EN_ERROR_IF_OVERFLOW); |
| eventProducer.viewportIPDOverflow(this, getBlockContainerFO().getName(), |
| this.horizontalOverflow, needClip(), canRecover, getBlockContainerFO().getLocator()); |
| } |
| } |
| |
| setFinished(true); |
| return returnList; |
| } |
| |
| private void updateRelDims() { |
| Rectangle2D rect = new Rectangle2D.Double( |
| contentRectOffsetX, contentRectOffsetY, |
| getContentAreaIPD(), |
| this.vpContentBPD); |
| relDims = new FODimension(0, 0); |
| absoluteCTM = CTM.getCTMandRelDims( |
| getBlockContainerFO().getReferenceOrientation(), |
| getBlockContainerFO().getWritingMode(), |
| rect, relDims); |
| } |
| |
| 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; |
| } |
| |
| /** {@inheritDoc} */ |
| protected void observeElementList(List elementList) { |
| ElementListObserver.observe(elementList, "block-container", |
| bclm.getBlockContainerFO().getId()); |
| } |
| |
| /** {@inheritDoc} */ |
| protected boolean isPartOverflowRecoveryActivated() { |
| //For block-containers, this must be disabled because of wanted overflow. |
| return false; |
| } |
| |
| /** {@inheritDoc} */ |
| protected boolean isSinglePartFavored() { |
| return true; |
| } |
| |
| public int getDifferenceOfFirstPart() { |
| PageBreakPosition pbp = this.deferredAlg.getPageBreaks().getFirst(); |
| return pbp.difference; |
| } |
| |
| public boolean isOverflow() { |
| return !isEmpty() |
| && ((deferredAlg.getPageBreaks().size() > 1) |
| || (deferredAlg.totalWidth - deferredAlg.totalShrink) |
| > deferredAlg.getLineWidth()); |
| } |
| |
| public int getOverflowAmount() { |
| return (deferredAlg.totalWidth - deferredAlg.totalShrink) |
| - deferredAlg.getLineWidth(); |
| } |
| |
| protected LayoutManager getTopLevelLM() { |
| return bclm; |
| } |
| |
| protected LayoutContext createLayoutContext() { |
| LayoutContext lc = super.createLayoutContext(); |
| lc.setRefIPD(ipd.getOpt()); |
| lc.setWritingMode(getBlockContainerFO().getWritingMode()); |
| return lc; |
| } |
| |
| protected List getNextKnuthElements(LayoutContext context, int alignment) { |
| LayoutManager curLM; // currently active LM |
| List<ListElement> returnList = new LinkedList<ListElement>(); |
| |
| while ((curLM = getChildLM()) != null) { |
| LayoutContext childLC = makeChildLayoutContext(context); |
| |
| List 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(LayoutContext layoutContext) { |
| if (isEmpty()) { |
| return; |
| } |
| //Rendering all parts (not just the first) at once for the case where the parts that |
| //overflow should be visible. |
| this.deferredAlg.removeAllPageBreaks(); |
| this.addAreas(this.deferredAlg, |
| 0, |
| this.deferredAlg.getPageBreaks().size(), |
| this.deferredOriginalList, this.deferredEffectiveList, |
| LayoutContext.offspringOf(layoutContext)); |
| } |
| |
| } |
| |
| private Point getAbsOffset() { |
| int x = 0; |
| int y = 0; |
| if (abProps.left.getEnum() != EN_AUTO) { |
| x = abProps.left.getValue(this); |
| } else if (abProps.right.getEnum() != EN_AUTO |
| && width.getEnum() != EN_AUTO) { |
| x = getReferenceAreaIPD() |
| - abProps.right.getValue(this) - width.getValue(this); |
| } |
| if (abProps.top.getEnum() != EN_AUTO) { |
| y = abProps.top.getValue(this); |
| } else if (abProps.bottom.getEnum() != EN_AUTO |
| && height.getEnum() != EN_AUTO) { |
| y = getReferenceAreaBPD() |
| - abProps.bottom.getValue(this) - height.getValue(this); |
| } |
| return new Point(x, y); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| 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, MinOptMax.getInstance(layoutContext.getSpaceBefore())); |
| } |
| |
| LayoutManager childLM; |
| LayoutManager lastLM = null; |
| LayoutContext lc = LayoutContext.offspringOf(layoutContext); |
| 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; |
| List<Position> positionList = new LinkedList<Position>(); |
| Position pos; |
| Position firstPos = null; |
| Position lastPos = null; |
| while (parentIter.hasNext()) { |
| pos = parentIter.next(); |
| if (pos.getIndex() >= 0) { |
| if (firstPos == null) { |
| firstPos = pos; |
| } |
| lastPos = pos; |
| } |
| Position innerPosition = pos; |
| if (pos instanceof NonLeafPosition) { |
| innerPosition = 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) { |
| //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(); |
| } |
| } |
| |
| addId(); |
| |
| registerMarkers(true, isFirst(firstPos), isLast(lastPos)); |
| |
| if (bcpos == null) { |
| // the Positions in positionList were inside the elements |
| // created by the LineLM |
| childPosIter = new PositionIterator(positionList.listIterator()); |
| |
| while ((childLM = childPosIter.getNextChildLM()) != null) { |
| // set last area flag |
| lc.setFlags(LayoutContext.LAST_AREA, |
| (layoutContext.isLastArea() && childLM == lastLM)); |
| lc.setStackLimitBP(layoutContext.getStackLimitBP()); |
| // Add the line areas to Area |
| childLM.addAreas(childPosIter, lc); |
| } |
| } else { |
| //Add child areas inside the reference area |
| bcpos.getBreaker().addContainedAreas(layoutContext); |
| } |
| |
| registerMarkers(false, isFirst(firstPos), isLast(lastPos)); |
| |
| TraitSetter.addSpaceBeforeAfter(viewportBlockArea, layoutContext.getSpaceAdjust(), |
| effSpaceBefore, effSpaceAfter); |
| flush(); |
| |
| viewportBlockArea = null; |
| referenceArea = null; |
| resetSpaces(); |
| |
| notifyEndOfLayout(); |
| } |
| |
| /** |
| * Get the parent area for children of this block container. |
| * This returns the current block container area |
| * and creates it if required. |
| * |
| * {@inheritDoc} |
| */ |
| @Override |
| public Area getParentArea(Area childArea) { |
| if (referenceArea == null) { |
| boolean switchedProgressionDirection = blockProgressionDirectionChanges(); |
| boolean allowBPDUpdate = autoHeight && !switchedProgressionDirection; |
| int level = getBlockContainerFO().getBidiLevel(); |
| |
| viewportBlockArea = new BlockViewport(allowBPDUpdate); |
| viewportBlockArea.setChangeBarList(getChangeBarList()); |
| viewportBlockArea.addTrait(Trait.IS_VIEWPORT_AREA, Boolean.TRUE); |
| if (level >= 0) { |
| viewportBlockArea.setBidiLevel(level); |
| } |
| viewportBlockArea.setIPD(getContentAreaIPD()); |
| if (allowBPDUpdate) { |
| viewportBlockArea.setBPD(0); |
| } else { |
| viewportBlockArea.setBPD(this.vpContentBPD); |
| } |
| transferForeignAttributes(viewportBlockArea); |
| |
| TraitSetter.setProducerID(viewportBlockArea, getBlockContainerFO().getId()); |
| TraitSetter.setLayer(viewportBlockArea, getBlockContainerFO().getLayer()); |
| TraitSetter.addBorders(viewportBlockArea, |
| getBlockContainerFO().getCommonBorderPaddingBackground(), |
| discardBorderBefore, discardBorderAfter, false, false, this); |
| TraitSetter.addPadding(viewportBlockArea, |
| getBlockContainerFO().getCommonBorderPaddingBackground(), |
| discardPaddingBefore, discardPaddingAfter, false, false, this); |
| TraitSetter.addMargins(viewportBlockArea, |
| getBlockContainerFO().getCommonBorderPaddingBackground(), |
| startIndent, endIndent, |
| this); |
| |
| viewportBlockArea.setCTM(absoluteCTM); |
| viewportBlockArea.setClip(needClip()); |
| |
| 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.setChangeBarList(getChangeBarList()); |
| referenceArea.addTrait(Trait.IS_REFERENCE_AREA, Boolean.TRUE); |
| if (level >= 0) { |
| referenceArea.setBidiLevel(level); |
| } |
| 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 =*/ parentLayoutManager.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. |
| * |
| * {@inheritDoc} |
| */ |
| @Override |
| public void addChildArea(Area childArea) { |
| if (referenceArea != null) { |
| referenceArea.addBlock((Block) childArea); |
| } |
| } |
| |
| /** |
| * Force current area to be added to parent area. |
| * {@inheritDoc} |
| */ |
| @Override |
| protected void flush() { |
| viewportBlockArea.addBlock(referenceArea, autoHeight); |
| |
| TraitSetter.addBackground(viewportBlockArea, |
| getBlockContainerFO().getCommonBorderPaddingBackground(), |
| this); |
| |
| super.flush(); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) { |
| // TODO Auto-generated method stub |
| return 0; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public void discardSpace(KnuthGlue spaceGlue) { |
| // TODO Auto-generated method stub |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public KeepProperty getKeepTogetherProperty() { |
| return getBlockContainerFO().getKeepTogether(); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public KeepProperty getKeepWithPreviousProperty() { |
| return getBlockContainerFO().getKeepWithPrevious(); |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public KeepProperty getKeepWithNextProperty() { |
| return getBlockContainerFO().getKeepWithNext(); |
| } |
| |
| /** |
| * @return the BlockContainer node |
| */ |
| protected BlockContainer getBlockContainerFO() { |
| return (BlockContainer) fobj; |
| } |
| |
| // --------- Property Resolution related functions --------- // |
| |
| /** {@inheritDoc} */ |
| @Override |
| public boolean getGeneratesReferenceArea() { |
| return true; |
| } |
| |
| /** {@inheritDoc} */ |
| @Override |
| public boolean getGeneratesBlockArea() { |
| return true; |
| } |
| |
| /** {@inheritDoc} */ |
| public boolean handleOverflow(int milliPoints) { |
| if (milliPoints > this.horizontalOverflow) { |
| this.horizontalOverflow = milliPoints; |
| } |
| return true; |
| } |
| |
| } |
| |
| |