From bcfda76c15640c34c91f2d8e29d85b9326ab8d25 Mon Sep 17 00:00:00 2001 From: Vincent Hennebert Date: Wed, 26 Aug 2009 18:50:10 +0000 Subject: Merged back ChangingIPDHack branch into Trunk git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@808157 13f79535-47bb-0310-9956-ffa450edef68 --- .../fop/layoutmgr/AbstractBaseLayoutManager.java | 20 + .../org/apache/fop/layoutmgr/AbstractBreaker.java | 146 +++++- .../fop/layoutmgr/AbstractLayoutManager.java | 40 +- .../AbstractPageSequenceLayoutManager.java | 5 + .../org/apache/fop/layoutmgr/AreaAdditionUtil.java | 2 +- .../fop/layoutmgr/BlockContainerLayoutManager.java | 306 +++++++++++ .../apache/fop/layoutmgr/BlockLayoutManager.java | 36 +- .../fop/layoutmgr/BlockStackingLayoutManager.java | 402 ++++++++++----- .../apache/fop/layoutmgr/BreakingAlgorithm.java | 21 +- .../apache/fop/layoutmgr/FlowLayoutManager.java | 219 ++++---- .../apache/fop/layoutmgr/InlineKnuthSequence.java | 16 +- .../org/apache/fop/layoutmgr/LayoutContext.java | 39 +- .../org/apache/fop/layoutmgr/LayoutManager.java | 33 ++ .../org/apache/fop/layoutmgr/LeafPosition.java | 11 +- src/java/org/apache/fop/layoutmgr/PageBreaker.java | 35 +- .../fop/layoutmgr/PageBreakingAlgorithm.java | 62 +++ .../org/apache/fop/layoutmgr/PageProvider.java | 27 + src/java/org/apache/fop/layoutmgr/Position.java | 5 + .../org/apache/fop/layoutmgr/SpaceResolver.java | 6 +- .../fop/layoutmgr/StaticContentLayoutManager.java | 103 +--- .../fop/layoutmgr/inline/ContentLayoutManager.java | 10 +- .../fop/layoutmgr/inline/InlineLayoutManager.java | 10 - .../inline/InlineStackingLayoutManager.java | 32 +- .../fop/layoutmgr/inline/LineLayoutManager.java | 558 +++++++-------------- .../fop/layoutmgr/list/ListItemLayoutManager.java | 7 + .../org/apache/fop/render/pdf/PDFEventProducer.xml | 3 +- .../standard-testcases/flow_changing-ipd_1.xml | 97 ++++ .../standard-testcases/flow_changing-ipd_2.xml | 75 +++ .../standard-testcases/flow_changing-ipd_3.xml | 86 ++++ .../standard-testcases/flow_changing-ipd_4.xml | 159 ++++++ .../flow_changing-ipd_block-container_1.xml | 101 ++++ .../flow_changing-ipd_block-container_2.xml | 89 ++++ 32 files changed, 1945 insertions(+), 816 deletions(-) create mode 100644 test/layoutengine/standard-testcases/flow_changing-ipd_1.xml create mode 100644 test/layoutengine/standard-testcases/flow_changing-ipd_2.xml create mode 100644 test/layoutengine/standard-testcases/flow_changing-ipd_3.xml create mode 100644 test/layoutengine/standard-testcases/flow_changing-ipd_4.xml create mode 100644 test/layoutengine/standard-testcases/flow_changing-ipd_block-container_1.xml create mode 100644 test/layoutengine/standard-testcases/flow_changing-ipd_block-container_2.xml diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java index f7acf3eb5..8c213d7d5 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractBaseLayoutManager.java @@ -19,8 +19,12 @@ package org.apache.fop.layoutmgr; +import java.util.List; +import java.util.Stack; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.apache.fop.datatypes.LengthBase; import org.apache.fop.datatypes.PercentBaseContext; import org.apache.fop.fo.FObj; @@ -253,4 +257,20 @@ public abstract class AbstractBaseLayoutManager return fobj; } + /** {@inheritDoc} */ + public void reset() { + throw new UnsupportedOperationException("Not implemented"); + } + + /** {@inheritDoc} */ + public boolean isRestartable() { + return false; + } + + /** {@inheritDoc} */ + public List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack, + Position positionAtIPDChange, LayoutManager restartAtLM) { + throw new UnsupportedOperationException("Not implemented"); + } + } diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java index 6393935ae..1a6f7cfb9 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java @@ -19,6 +19,8 @@ package org.apache.fop.layoutmgr; +import java.util.Collections; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; @@ -27,6 +29,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.fop.fo.Constants; +import org.apache.fop.layoutmgr.BreakingAlgorithm.KnuthNode; import org.apache.fop.traits.MinOptMax; import org.apache.fop.util.ListUtil; @@ -247,6 +250,11 @@ public abstract class AbstractBreaker { */ protected abstract List getNextKnuthElements(LayoutContext context, int alignment); + protected List getNextKnuthElements(LayoutContext context, int alignment, + Position positionAtIPDChange, LayoutManager restartAtLM) { + throw new UnsupportedOperationException("TODO: implement acceptable fallback"); + } + /** @return true if there's no content that could be handled. */ public boolean isEmpty() { return (this.blockLists.isEmpty()); @@ -290,14 +298,6 @@ public abstract class AbstractBreaker { ElementListObserver.observe(elementList, "breaker", null); } - /** - * Starts the page breaking process. - * @param flowBPD the constant available block-progression-dimension (used for every part) - */ - public void doLayout(int flowBPD) { - doLayout(flowBPD, false); - } - /** * Starts the page breaking process. * @param flowBPD the constant available block-progression-dimension (used for every part) @@ -354,7 +354,6 @@ public abstract class AbstractBreaker { getPageProvider(), createLayoutListener(), alignment, alignmentLast, footnoteSeparatorLength, isPartOverflowRecoveryActivated(), autoHeight, isSinglePartFavored()); - int iOptPageCount; BlockSequence effectiveList; if (getCurrentDisplayAlign() == Constants.EN_X_FILL) { @@ -365,21 +364,107 @@ public abstract class AbstractBreaker { effectiveList = blockList; } - //iOptPageCount = alg.firstFit(effectiveList, flowBPD, 1, true); alg.setConstantLineWidth(flowBPD); - iOptPageCount = alg.findBreakingPoints(effectiveList, /*flowBPD,*/ - 1, true, BreakingAlgorithm.ALL_BREAKS); - log.debug("PLM> iOptPageCount= " + iOptPageCount - + " pageBreaks.size()= " + alg.getPageBreaks().size()); + int optimalPageCount = alg.findBreakingPoints(effectiveList, 1, true, + BreakingAlgorithm.ALL_BREAKS); + if (alg.ipdChanged()) { + KnuthNode optimalBreak = alg.getBestNodeBeforeIPDChange(); + int positionIndex = optimalBreak.position; + KnuthElement elementAtBreak = alg.getElement(positionIndex); + Position positionAtBreak = elementAtBreak.getPosition(); + if (!(positionAtBreak instanceof SpaceResolver.SpaceHandlingBreakPosition)) { + throw new UnsupportedOperationException( + "Don't know how to restart at position" + positionAtBreak); + } + /* Retrieve the original position wrapped into this space position */ + positionAtBreak = positionAtBreak.getPosition(); + LayoutManager restartAtLM = null; + List firstElements = Collections.EMPTY_LIST; + if (containsNonRestartableLM(positionAtBreak)) { + firstElements = new LinkedList(); + boolean boxFound = false; + Iterator iter = effectiveList.listIterator(++positionIndex); + Position position = null; + while (iter.hasNext() + && (position == null || containsNonRestartableLM(position))) { + KnuthElement element = (KnuthElement) iter.next(); + positionIndex++; + position = element.getPosition(); + if (element.isBox()) { + boxFound = true; + firstElements.add(element); + } else if (boxFound) { + firstElements.add(element); + } + } + if (position instanceof SpaceResolver.SpaceHandlingBreakPosition) { + /* Retrieve the original position wrapped into this space position */ + positionAtBreak = position.getPosition(); + } + } + if (positionAtBreak.getIndex() == -1) { + /* + * This is an indication that we are between two blocks + * (possibly surrounded by another block), not inside a + * paragraph. + */ + Position position; + Iterator iter = effectiveList.listIterator(positionIndex + 1); + do { + KnuthElement nextElement = (KnuthElement) iter.next(); + position = nextElement.getPosition(); + } while (position == null + || position instanceof SpaceResolver.SpaceHandlingPosition + || position instanceof SpaceResolver.SpaceHandlingBreakPosition + && position.getPosition().getIndex() == -1); + LayoutManager surroundingLM = positionAtBreak.getLM(); + while (position.getLM() != surroundingLM) { + position = position.getPosition(); + } + restartAtLM = position.getPosition().getLM(); + } + log.trace("IPD changes after page " + optimalPageCount + " at index " + + optimalBreak.position); + doPhase3(alg, optimalPageCount, blockList, effectiveList); + + blockLists.clear(); + blockListIndex = -1; + nextSequenceStartsOn = getNextBlockList(childLC, Constants.EN_COLUMN, + positionAtBreak, restartAtLM, firstElements); + } else { + log.debug("PLM> iOptPageCount= " + optimalPageCount + + " pageBreaks.size()= " + alg.getPageBreaks().size()); - //*** Phase 3: Add areas *** - doPhase3(alg, iOptPageCount, blockList, effectiveList); + //*** Phase 3: Add areas *** + doPhase3(alg, optimalPageCount, blockList, effectiveList); + } } } } + /** + * Returns {@code true} if the given position or one of its descendants + * corresponds to a non-restartable LM. + * + * @param position a position + * @return {@code true} if there is a non-restartable LM in the hierarchy + */ + private boolean containsNonRestartableLM(Position position) { + LayoutManager lm = position.getLM(); + if (lm != null && !lm.isRestartable()) { + return true; + } else { + Position subPosition = position.getPosition(); + if (subPosition == null) { + return false; + } else { + return containsNonRestartableLM(subPosition); + } + } + } + /** * Phase 3 of Knuth algorithm: Adds the areas * @param alg PageBreakingAlgorithm instance which determined the breaks @@ -559,6 +644,7 @@ public abstract class AbstractBreaker { protected int handleSpanChange(LayoutContext childLC, int nextSequenceStartsOn) { return nextSequenceStartsOn; } + /** * Gets the next block list (sequence) and adds it to a list of block lists if it's not empty. * @param childLC LayoutContext to use @@ -567,12 +653,38 @@ public abstract class AbstractBreaker { */ protected int getNextBlockList(LayoutContext childLC, int nextSequenceStartsOn) { + return getNextBlockList(childLC, nextSequenceStartsOn, null, null, null); + } + + /** + * Gets the next block list (sequence) and adds it to a list of block lists + * if it's not empty. + * + * @param childLC LayoutContext to use + * @param nextSequenceStartsOn indicates on what page the next sequence + * should start + * @param positionAtIPDChange last element on the part before an IPD change + * @param restartAtLM the layout manager from which to restart, if IPD + * change occurs between two LMs + * @param firstElements elements from non-restartable LMs on the new page + * @return the page on which the next content should appear after a hard + * break + */ + protected int getNextBlockList(LayoutContext childLC, int nextSequenceStartsOn, + Position positionAtIPDChange, LayoutManager restartAtLM, List firstElements) { updateLayoutContext(childLC); //Make sure the span change signal is reset childLC.signalSpanChange(Constants.NOT_SET); BlockSequence blockList; - List returnedList = getNextKnuthElements(childLC, alignment); + List returnedList; + if (positionAtIPDChange == null) { + returnedList = getNextKnuthElements(childLC, alignment); + } else { + returnedList = getNextKnuthElements(childLC, alignment, positionAtIPDChange, + restartAtLM); + returnedList.addAll(0, firstElements); + } if (returnedList != null) { if (returnedList.isEmpty()) { nextSequenceStartsOn = handleSpanChange(childLC, nextSequenceStartsOn); diff --git a/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java index 8dca1c749..82f0599eb 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractLayoutManager.java @@ -47,22 +47,22 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager private static Log log = LogFactory.getLog(AbstractLayoutManager.class); /** Parent LayoutManager for this LayoutManager */ - protected LayoutManager parentLM = null; + protected LayoutManager parentLM; /** List of child LayoutManagers */ - protected List childLMs = null; + protected List childLMs; /** Iterator for child LayoutManagers */ - protected ListIterator fobjIter = null; + protected ListIterator fobjIter; /** Marker map for markers related to this LayoutManager */ - private Map markers = null; + private Map markers; /** True if this LayoutManager has handled all of its content. */ - private boolean isFinished = false; + private boolean isFinished; /** child LM during getNextKnuthElement phase */ - protected LayoutManager curChildLM = null; + protected LayoutManager curChildLM; /** child LM iterator during getNextKnuthElement phase */ - protected ListIterator childLMiter = null; + protected ListIterator childLMiter; private int lastGeneratedPosition = -1; private int smallestPosNumberChecked = Integer.MAX_VALUE; @@ -122,6 +122,14 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager return null; } + protected void setCurrentChildLM(LayoutManager childLM) { + curChildLM = childLM; + childLMiter = new LMiter(this); + do { + curChildLM = (LayoutManager) childLMiter.next(); + } while (curChildLM != childLM); + } + /** * Return indication if getChildLM will return another LM. * @return true if another child LM is still available @@ -450,4 +458,22 @@ public abstract class AbstractLayoutManager extends AbstractBaseLayoutManager return (super.toString() + (fobj != null ? "[fobj=" + fobj.toString() + "]" : "")); } + /** {@inheritDoc} */ + public void reset() { + isFinished = false; + curChildLM = null; + childLMiter = new LMiter(this); + /* + * Reset the children LM. Can't rely on childLMiter since it may have + * been set to null in checkEndOfLayout. + */ + for (LMiter iter = new LMiter(this); iter.hasNext();) { + ((LayoutManager) iter.next()).reset(); + } + if (fobj != null) { + markers = fobj.getMarkers(); + } + lastGeneratedPosition = -1; + } + } diff --git a/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java index 758761303..0fa046aee 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java @@ -382,4 +382,9 @@ public abstract class AbstractPageSequenceLayoutManager extends AbstractLayoutMa } } + /** {@inheritDoc} */ + public void reset() { + throw new IllegalStateException(); + } + } diff --git a/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java b/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java index a429359ad..14183c52e 100644 --- a/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java +++ b/src/java/org/apache/fop/layoutmgr/AreaAdditionUtil.java @@ -117,7 +117,7 @@ public class AreaAdditionUtil { // set space after for each LM, in order to implement // display-align = distribute lc.setSpaceAfter(layoutContext.getSpaceAfter()); - lc.setStackLimitsFrom(layoutContext); + lc.setStackLimitBP(layoutContext.getStackLimitBP()); childLM.addAreas(childPosIter, lc); } diff --git a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java index 4a4becebf..e86c5feaf 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java @@ -24,6 +24,7 @@ import java.awt.geom.Rectangle2D; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.Stack; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -397,6 +398,311 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager return returnList; } + /** {@inheritDoc} */ + public List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack, + Position restartPosition, LayoutManager restartAtLM) { + resetSpaces(); + if (isAbsoluteOrFixed()) { + return getNextKnuthElementsAbsolute(context, alignment); + } + + autoHeight = false; + //boolean rotated = (getBlockContainerFO().getReferenceOrientation() % 180 != 0); + int maxbpd = context.getStackLimitBP().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 block-progression-dimension)" (XSL 1.0, 7.14.1) + allocBPD = maxbpd; + autoHeight = true; + if (getBlockContainerFO().getReferenceOrientation() == 0) { + //Cannot easily inline element list when ref-or="180" + inlineElementList = 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); + + updateRelDims(contentRectOffsetX, contentRectOffsetY, autoHeight); + + int availableIPD = referenceIPD - getIPIndents(); + if (getContentAreaIPD() > availableIPD) { + BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get( + getBlockContainerFO().getUserAgent().getEventBroadcaster()); + eventProducer.objectTooWide(this, getBlockContainerFO().getName(), + getContentAreaIPD(), context.getRefIPD(), + getBlockContainerFO().getLocator()); + } + + MinOptMax stackLimit = new MinOptMax(relDims.bpd); + + List returnedList; + List contentList = new LinkedList(); + List returnList = new LinkedList(); + + if (!breakBeforeServed) { + breakBeforeServed = true; + if (!context.suppressBreakBefore()) { + if (addKnuthElementsForBreakBefore(returnList, context)) { + return returnList; + } + } + } + + if (!firstVisibleMarkServed) { + addKnuthElementsForSpaceBefore(returnList, alignment); + context.updateKeepWithPreviousPending(getKeepWithPrevious()); + } + + addKnuthElementsForBorderPaddingBefore(returnList, !firstVisibleMarkServed); + firstVisibleMarkServed = true; + + if (autoHeight && inlineElementList) { + //Spaces, border and padding to be repeated at each break + addPendingMarks(context); + + BlockLevelLayoutManager curLM; // currently active LM + BlockLevelLayoutManager prevLM = null; // previously active LM + + LayoutContext childLC = new LayoutContext(0); + if (lmStack.isEmpty()) { + assert restartAtLM != null && restartAtLM.getParent() == this; + curLM = (BlockLevelLayoutManager) restartAtLM; + curLM.reset(); + setCurrentChildLM(curLM); + + childLC.copyPendingMarksFrom(context); + childLC.setStackLimitBP(MinOptMax.subtract(context.getStackLimitBP(), stackLimit)); + childLC.setRefIPD(relDims.ipd); + childLC.setWritingMode(getBlockContainerFO().getWritingMode()); + if (curLM == this.childLMs.get(0)) { + childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE); + //Handled already by the parent (break collapsing, see above) + } + + // get elements from curLM + returnedList = curLM.getNextKnuthElements(childLC, alignment); + } else { + curLM = (BlockLevelLayoutManager) lmStack.pop(); + setCurrentChildLM(curLM); + + childLC.copyPendingMarksFrom(context); + childLC.setStackLimitBP(MinOptMax.subtract(context.getStackLimitBP(), stackLimit)); + childLC.setRefIPD(relDims.ipd); + childLC.setWritingMode(getBlockContainerFO().getWritingMode()); + if (curLM == this.childLMs.get(0)) { + childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE); + //Handled already by the parent (break collapsing, see above) + } + + // get elements from curLM + returnedList = curLM.getNextKnuthElements(childLC, alignment, lmStack, + restartPosition, restartAtLM); + } + if (contentList.isEmpty() && childLC.isKeepWithPreviousPending()) { + //Propagate keep-with-previous up from the first child + context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending()); + childLC.clearKeepWithPreviousPending(); + } + if (returnedList.size() == 1 + && ((ListElement)returnedList.get(0)).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 + addInBetweenBreak(contentList, context, childLC); + } + contentList.addAll(returnedList); + if (!returnedList.isEmpty()) { + if (((ListElement) ListUtil.getLast(returnedList)) + .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; + } + } + } + // propagate and clear + context.updateKeepWithNextPending(childLC.getKeepWithNextPending()); + childLC.clearKeepsPending(); + prevLM = curLM; + + while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) { + curLM.reset(); + childLC = new LayoutContext(0); + childLC.copyPendingMarksFrom(context); + // curLM is a ? + childLC.setStackLimitBP(MinOptMax.subtract(context.getStackLimitBP(), stackLimit)); + childLC.setRefIPD(relDims.ipd); + childLC.setWritingMode(getBlockContainerFO().getWritingMode()); + if (curLM == this.childLMs.get(0)) { + childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE); + //Handled already by the parent (break collapsing, see above) + } + + // get elements from curLM + returnedList = curLM.getNextKnuthElements(childLC, alignment); + if (contentList.isEmpty() && childLC.isKeepWithPreviousPending()) { + //Propagate keep-with-previous up from the first child + context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending()); + childLC.clearKeepWithPreviousPending(); + } + if (returnedList.size() == 1 + && ((ListElement)returnedList.get(0)).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 + addInBetweenBreak(contentList, context, childLC); + } + contentList.addAll(returnedList); + if (returnedList.isEmpty()) { + //Avoid NoSuchElementException below (happens with empty blocks) + continue; + } + if (((ListElement) ListUtil.getLast(returnedList)) + .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; + } + } + // propagate and clear + context.updateKeepWithNextPending(childLC.getKeepWithNextPending()); + childLC.clearKeepsPending(); + 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, autoHeight); + boolean contentOverflows = breaker.isOverflow(); + if (autoHeight) { + //Update content BPD now that it is known + int newHeight = breaker.deferredAlg.totalWidth; + boolean switchedProgressionDirection + = (getBlockContainerFO().getReferenceOrientation() % 180 != 0); + if (switchedProgressionDirection) { + setContentAreaIPD(newHeight); + } else { + vpContentBPD = newHeight; + } + updateRelDims(contentRectOffsetX, contentRectOffsetY, false); + } + + 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) { + BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider.get( + getBlockContainerFO().getUserAgent().getEventBroadcaster()); + boolean canRecover = (getBlockContainerFO().getOverflow() != EN_ERROR_IF_OVERFLOW); + eventProducer.viewportOverflow(this, getBlockContainerFO().getName(), + breaker.getOverflowAmount(), needClip(), canRecover, + getBlockContainerFO().getLocator()); + } + } + addKnuthElementsForBorderPaddingAfter(returnList, true); + addKnuthElementsForSpaceAfter(returnList, alignment); + + //All child content is processed. Only break-after can occur now, so... + context.clearPendingMarks(); + addKnuthElementsForBreakAfter(returnList, context); + + context.updateKeepWithNextPending(getKeepWithNext()); + + setFinished(true); + return returnList; + } + + /** {@inheritDoc} */ + public boolean isRestartable() { + return true; + } + private List getNextKnuthElementsAbsolute(LayoutContext context, int alignment) { autoHeight = false; diff --git a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java index 20895a38e..126ab3796 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockLayoutManager.java @@ -22,6 +22,7 @@ package org.apache.fop.layoutmgr; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.Stack; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -67,9 +68,6 @@ public class BlockLayoutManager extends BlockStackingLayoutManager private MinOptMax effSpaceBefore; private MinOptMax effSpaceAfter; - /** The list of child BreakPoss instances. */ - protected List childBreaks = new java.util.ArrayList(); - /** * Creates a new BlockLayoutManager. * @param inBlock the block FO object to create the layout manager for. @@ -114,8 +112,19 @@ public class BlockLayoutManager extends BlockStackingLayoutManager /** {@inheritDoc} */ public List getNextKnuthElements(LayoutContext context, int alignment) { + return getNextKnuthElements(context, alignment, null, null, null); + } + + /** {@inheritDoc} */ + public List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack, + Position restartPosition, LayoutManager restartAtLM) { resetSpaces(); - return super.getNextKnuthElements(context, alignment); + if (lmStack == null) { + return super.getNextKnuthElements(context, alignment); + } else { + return super.getNextKnuthElements(context, alignment, lmStack, restartPosition, + restartAtLM); + } } private void resetSpaces() { @@ -249,8 +258,8 @@ public class BlockLayoutManager extends BlockStackingLayoutManager // and put them in a new list; LinkedList positionList = new LinkedList(); Position pos; - boolean bSpaceBefore = false; - boolean bSpaceAfter = false; + boolean spaceBefore = false; + boolean spaceAfter = false; Position firstPos = null; Position lastPos = null; while (parentIter.hasNext()) { @@ -273,11 +282,11 @@ public class BlockLayoutManager extends BlockStackingLayoutManager // this means the space was not discarded if (positionList.size() == 0) { // pos was in the element representing space-before - bSpaceBefore = true; + spaceBefore = true; //log.trace(" space before"); } else { // pos was in the element representing space-after - bSpaceAfter = true; + spaceAfter = true; //log.trace(" space-after"); } } else if (innerPosition.getLM() == this @@ -302,7 +311,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager // the Positions in positionList were inside the elements // created by the LineLM childPosIter = new StackingIter(positionList.listIterator()); - } else { + } else { // the Positions in positionList were inside the elements // created by the BlockLM in the createUnitElements() method //if (((Position) positionList.getLast()) instanceof @@ -341,7 +350,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager // + " spacing"); // add space before and / or after the paragraph // to reach a multiple of bpUnit - if (bSpaceBefore && bSpaceAfter) { + if (spaceBefore && spaceAfter) { foSpaceBefore = new SpaceVal(getBlockFO() .getCommonMarginBlock().spaceBefore, this).getSpace(); foSpaceAfter = new SpaceVal(getBlockFO() @@ -354,7 +363,7 @@ public class BlockLayoutManager extends BlockStackingLayoutManager + foSpaceBefore.min + foSpaceAfter.min) * bpUnit - splitLength - adjustedSpaceBefore; - } else if (bSpaceBefore) { + } else if (spaceBefore) { adjustedSpaceBefore = neededUnits(splitLength + foSpaceBefore.min) * bpUnit - splitLength; @@ -547,5 +556,10 @@ public class BlockLayoutManager extends BlockStackingLayoutManager } } + /** {@inheritDoc} */ + public boolean isRestartable() { + return true; + } + } diff --git a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java index 73c8eb00d..53c529eaa 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockStackingLayoutManager.java @@ -23,6 +23,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.Stack; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -30,12 +31,12 @@ import org.apache.commons.logging.LogFactory; import org.apache.fop.area.Area; import org.apache.fop.area.Block; import org.apache.fop.area.BlockParent; -import org.apache.fop.fo.FObj; import org.apache.fop.fo.Constants; +import org.apache.fop.fo.FObj; import org.apache.fop.fo.properties.BreakPropertySet; import org.apache.fop.fo.properties.CommonBorderPaddingBackground; -import org.apache.fop.fo.properties.SpaceProperty; import org.apache.fop.fo.properties.KeepProperty; +import org.apache.fop.fo.properties.SpaceProperty; import org.apache.fop.layoutmgr.inline.InlineLayoutManager; import org.apache.fop.layoutmgr.inline.LineLayoutManager; import org.apache.fop.traits.MinOptMax; @@ -54,31 +55,26 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager */ private static Log log = LogFactory.getLog(BlockStackingLayoutManager.class); - /** - * Reference to FO whose areas it's managing or to the traits - * of the FO. - */ - //protected LayoutManager curChildLM = null; AbstractLayoutManager also defines this! - protected BlockParent parentArea = null; + protected BlockParent parentArea; /** Value of the block-progression-unit (non-standard property) */ - protected int bpUnit = 0; + protected int bpUnit; /** space-before value adjusted for block-progression-unit handling */ - protected int adjustedSpaceBefore = 0; + protected int adjustedSpaceBefore; /** space-after value adjusted for block-progression-unit handling */ - protected int adjustedSpaceAfter = 0; + protected int adjustedSpaceAfter; /** Only used to store the original list when createUnitElements is called */ - protected List storedList = null; + protected List storedList; /** Indicates whether break before has been served or not */ - protected boolean breakBeforeServed = false; + protected boolean breakBeforeServed; /** Indicates whether the first visible mark has been returned by this LM, yet */ - protected boolean firstVisibleMarkServed = false; + protected boolean firstVisibleMarkServed; /** Reference IPD available */ - protected int referenceIPD = 0; + protected int referenceIPD; /** the effective start-indent value */ - protected int startIndent = 0; + protected int startIndent; /** the effective end-indent value */ - protected int endIndent = 0; + protected int endIndent; /** * Holds the (one-time use) fo:block space-before * and -after properties. Large fo:blocks are split @@ -88,13 +84,13 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager * Block and space-after at the end of the last Block * used in rendering the fo:block. */ - protected MinOptMax foSpaceBefore = null; + protected MinOptMax foSpaceBefore; /** see foSpaceBefore */ - protected MinOptMax foSpaceAfter = null; + protected MinOptMax foSpaceAfter; private Position auxiliaryPosition; - private int contentAreaIPD = 0; + private int contentAreaIPD; /** * @param node the fo this LM deals with @@ -248,38 +244,27 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager /** {@inheritDoc} */ public List getNextKnuthElements(LayoutContext context, int alignment) { - //log.debug("BLM.getNextKnuthElements> keep-together = " - // + layoutProps.keepTogether.getType()); - //log.debug(" keep-with-previous = " + - // layoutProps.keepWithPrevious.getType()); - //log.debug(" keep-with-next = " + - // layoutProps.keepWithNext.getType()); - BlockLevelLayoutManager curLM; // currently active LM - BlockLevelLayoutManager prevLM = null; // previously active LM - referenceIPD = context.getRefIPD(); - updateContentAreaIPDwithOverconstrainedAdjust(); - List returnedList = null; List contentList = new LinkedList(); - List returnList = new LinkedList(); + List elements = new LinkedList(); if (!breakBeforeServed) { breakBeforeServed = true; if (!context.suppressBreakBefore()) { - if (addKnuthElementsForBreakBefore(returnList, context)) { - return returnList; + if (addKnuthElementsForBreakBefore(elements, context)) { + return elements; } } } if (!firstVisibleMarkServed) { - addKnuthElementsForSpaceBefore(returnList, alignment); + addKnuthElementsForSpaceBefore(elements, alignment); context.updateKeepWithPreviousPending(getKeepWithPrevious()); } - addKnuthElementsForBorderPaddingBefore(returnList, !firstVisibleMarkServed); + addKnuthElementsForBorderPaddingBefore(elements, !firstVisibleMarkServed); firstVisibleMarkServed = true; //Spaces, border and padding to be repeated at each break @@ -288,142 +273,311 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager //Used to indicate a special break-after case when all content has already been generated. BreakElement forcedBreakAfterLast = null; - while ((curLM = (BlockLevelLayoutManager) getChildLM()) != null) { + LayoutManager currentChildLM; + while ((currentChildLM = (LayoutManager) getChildLM()) != null) { LayoutContext childLC = new LayoutContext(0); - childLC.copyPendingMarksFrom(context); - if (curLM instanceof LineLayoutManager) { - // curLM is a LineLayoutManager - // set stackLimit for lines (stack limit is now i-p-direction, not b-p-direction!) - childLC.setStackLimitBP(context.getStackLimitBP()); - childLC.setStackLimitIP(new MinOptMax(getContentAreaIPD())); - childLC.setRefIPD(getContentAreaIPD()); - } else { - // curLM is a ? - //childLC.setStackLimit(MinOptMax.subtract(context - // .getStackLimit(), stackSize)); - childLC.setStackLimitBP(context.getStackLimitBP()); - childLC.setRefIPD(referenceIPD); - } - if (curLM == this.childLMs.get(0)) { - childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE); - //Handled already by the parent (break collapsing, see above) - } - // get elements from curLM - returnedList = curLM.getNextKnuthElements(childLC, alignment); - if (contentList.isEmpty() && childLC.isKeepWithPreviousPending()) { + List childrenElements = getNextChildElements(currentChildLM, context, childLC, + alignment); + + if (contentList.isEmpty()) { //Propagate keep-with-previous up from the first child context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending()); - childLC.clearKeepWithPreviousPending(); } - if (returnedList != null - && returnedList.size() == 1 - && ((ListElement) returnedList.get(0)).isForcedBreak()) { + if (childrenElements != null && !childrenElements.isEmpty()) { + if (!contentList.isEmpty() + && !ElementListUtils.startsWithForcedBreak(childrenElements)) { + // there is a block handled by prevLM before the one + // handled by curLM, and the one handled + // by the current LM does not begin with a break + addInBetweenBreak(contentList, context, childLC); + } + if (childrenElements.size() == 1 + && ElementListUtils.startsWithForcedBreak(childrenElements)) { + + if (currentChildLM.isFinished() && !hasNextChildLM()) { + // a descendant of this block has break-before + forcedBreakAfterLast = (BreakElement) childrenElements.get(0); + context.clearPendingMarks(); + break; + } - if (curLM.isFinished() && !hasNextChildLM()) { + if (contentList.isEmpty()) { + // Empty fo:block, zero-length box makes sure the IDs and/or markers + // are registered and borders/padding are painted. + elements.add(new KnuthBox(0, notifyPos(new Position(this)), false)); + } // a descendant of this block has break-before - forcedBreakAfterLast = (BreakElement) returnedList.get(0); + contentList.addAll(childrenElements); + + wrapPositionElements(contentList, elements); + + return elements; + } else { + contentList.addAll(childrenElements); + if (ElementListUtils.endsWithForcedBreak(childrenElements)) { + // a descendant of this block has break-after + if (currentChildLM.isFinished() && !hasNextChildLM()) { + forcedBreakAfterLast = (BreakElement) ListUtil.removeLast(contentList); + context.clearPendingMarks(); + break; + } + + wrapPositionElements(contentList, elements); + + return elements; + } + } + context.updateKeepWithNextPending(childLC.getKeepWithNextPending()); + } + } + + if (!contentList.isEmpty()) { + wrapPositionElements(contentList, elements); + } else if (forcedBreakAfterLast == null) { + // Empty fo:block, zero-length box makes sure the IDs and/or markers + // are registered. + elements.add(new KnuthBox(0, notifyPos(new Position(this)), true)); + } + + addKnuthElementsForBorderPaddingAfter(elements, true); + addKnuthElementsForSpaceAfter(elements, alignment); + + //All child content is processed. Only break-after can occur now, so... + context.clearPendingMarks(); + if (forcedBreakAfterLast == null) { + addKnuthElementsForBreakAfter(elements, context); + } else { + forcedBreakAfterLast.clearPendingMarks(); + elements.add(forcedBreakAfterLast); + } + + context.updateKeepWithNextPending(getKeepWithNext()); + + setFinished(true); + + return elements; + } + + /** {@inheritDoc} */ + public List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack, + Position restartPosition, LayoutManager restartAtLM) { + referenceIPD = context.getRefIPD(); + updateContentAreaIPDwithOverconstrainedAdjust(); + + List contentList = new LinkedList(); + List elements = new LinkedList(); + + if (!breakBeforeServed) { + breakBeforeServed = true; + if (!context.suppressBreakBefore()) { + if (addKnuthElementsForBreakBefore(elements, context)) { + return elements; + } + } + } + + if (!firstVisibleMarkServed) { + addKnuthElementsForSpaceBefore(elements, alignment); + context.updateKeepWithPreviousPending(getKeepWithPrevious()); + } + + addKnuthElementsForBorderPaddingBefore(elements, !firstVisibleMarkServed); + firstVisibleMarkServed = true; + + //Spaces, border and padding to be repeated at each break + addPendingMarks(context); + + //Used to indicate a special break-after case when all content has already been generated. + BreakElement forcedBreakAfterLast = null; + + LayoutContext childLC = new LayoutContext(0); + List childrenElements; + LayoutManager currentChildLM; + if (lmStack.isEmpty()) { + assert restartAtLM != null && restartAtLM.getParent() == this; + currentChildLM = restartAtLM; + currentChildLM.reset(); + setCurrentChildLM(currentChildLM); + + childrenElements = getNextChildElements(currentChildLM, context, childLC, + alignment); + } else { + currentChildLM = (BlockLevelLayoutManager) lmStack.pop(); + setCurrentChildLM(currentChildLM); + childrenElements = getNextChildElements(currentChildLM, context, childLC, alignment, + lmStack, restartPosition, restartAtLM); + } + + if (contentList.isEmpty()) { + //Propagate keep-with-previous up from the first child + context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending()); + } + if (childrenElements != null && !childrenElements.isEmpty()) { + if (!contentList.isEmpty() + && !ElementListUtils.startsWithForcedBreak(childrenElements)) { + // there is a block handled by prevLM before the one + // handled by curLM, and the one handled + // by the current LM does not begin with a break + addInBetweenBreak(contentList, context, childLC); + } + if (childrenElements.size() == 1 + && ElementListUtils.startsWithForcedBreak(childrenElements)) { + + if (currentChildLM.isFinished() && !hasNextChildLM()) { + // a descendant of this block has break-before + forcedBreakAfterLast = (BreakElement) childrenElements.get(0); context.clearPendingMarks(); - break; +// break; TODO } if (contentList.isEmpty()) { // Empty fo:block, zero-length box makes sure the IDs and/or markers // are registered and borders/padding are painted. - returnList.add(new KnuthBox(0, notifyPos(new Position(this)), false)); + elements.add(new KnuthBox(0, notifyPos(new Position(this)), false)); } // a descendant of this block has break-before - contentList.addAll(returnedList); - - /* extension: conversione di tutta la sequenza fin'ora ottenuta */ - if (bpUnit > 0) { - storedList = contentList; - contentList = createUnitElements(contentList); - } - /* end of extension */ + contentList.addAll(childrenElements); - // "wrap" the Position inside each element - // moving the elements from contentList to returnList - returnedList = new LinkedList(); - wrapPositionElements(contentList, returnList); + wrapPositionElements(contentList, elements); - return returnList; + return elements; } else { - if (returnedList == null || returnedList.isEmpty()) { - //Avoid NoSuchElementException below (happens with empty blocks) - continue; + contentList.addAll(childrenElements); + if (ElementListUtils.endsWithForcedBreak(childrenElements)) { + // a descendant of this block has break-after + if (currentChildLM.isFinished() && !hasNextChildLM()) { + forcedBreakAfterLast = (BreakElement) ListUtil.removeLast(contentList); + context.clearPendingMarks(); +// break; TODO + } + + wrapPositionElements(contentList, elements); + + return elements; } - if (prevLM != null - && !ElementListUtils.startsWithForcedBreak(returnedList)) { + } + context.updateKeepWithNextPending(childLC.getKeepWithNextPending()); + } + + while ((currentChildLM = (LayoutManager) getChildLM()) != null) { + currentChildLM.reset(); // TODO won't work with forced breaks + + childLC = new LayoutContext(0); + + childrenElements = getNextChildElements(currentChildLM, context, childLC, + alignment); + + if (contentList.isEmpty()) { + //Propagate keep-with-previous up from the first child + context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending()); + } + if (childrenElements != null && !childrenElements.isEmpty()) { + if (!contentList.isEmpty() + && !ElementListUtils.startsWithForcedBreak(childrenElements)) { // there is a block handled by prevLM before the one // handled by curLM, and the one handled // by the current LM does not begin with a break addInBetweenBreak(contentList, context, childLC); } - contentList.addAll(returnedList); - if (ElementListUtils.endsWithForcedBreak(returnedList)) { - // a descendant of this block has break-after - if (curLM.isFinished() && !hasNextChildLM()) { - forcedBreakAfterLast = (BreakElement) ListUtil - .removeLast(contentList); + if (childrenElements.size() == 1 + && ElementListUtils.startsWithForcedBreak(childrenElements)) { + + if (currentChildLM.isFinished() && !hasNextChildLM()) { + // a descendant of this block has break-before + forcedBreakAfterLast = (BreakElement) childrenElements.get(0); context.clearPendingMarks(); break; } - /* extension: conversione di tutta la sequenza fin'ora ottenuta */ - if (bpUnit > 0) { - storedList = contentList; - contentList = createUnitElements(contentList); + if (contentList.isEmpty()) { + // Empty fo:block, zero-length box makes sure the IDs and/or markers + // are registered and borders/padding are painted. + elements.add(new KnuthBox(0, notifyPos(new Position(this)), false)); } - /* end of extension */ + // a descendant of this block has break-before + contentList.addAll(childrenElements); - returnedList = new LinkedList(); - wrapPositionElements(contentList, returnList); + wrapPositionElements(contentList, elements); - return returnList; + return elements; + } else { + contentList.addAll(childrenElements); + if (ElementListUtils.endsWithForcedBreak(childrenElements)) { + // a descendant of this block has break-after + if (currentChildLM.isFinished() && !hasNextChildLM()) { + forcedBreakAfterLast = (BreakElement) ListUtil.removeLast(contentList); + context.clearPendingMarks(); + break; + } + + wrapPositionElements(contentList, elements); + + return elements; + } } + context.updateKeepWithNextPending(childLC.getKeepWithNextPending()); } - // propagate and clear - context.updateKeepWithNextPending(childLC.getKeepWithNextPending()); - childLC.clearKeepsPending(); - prevLM = curLM; - } - - /* Extension: conversione di tutta la sequenza fin'ora ottenuta */ - if (bpUnit > 0) { - storedList = contentList; - contentList = createUnitElements(contentList); } - /* end of extension */ - returnedList = new LinkedList(); if (!contentList.isEmpty()) { - wrapPositionElements(contentList, returnList); + wrapPositionElements(contentList, elements); } else if (forcedBreakAfterLast == null) { // Empty fo:block, zero-length box makes sure the IDs and/or markers // are registered. - returnList.add(new KnuthBox(0, notifyPos(new Position(this)), true)); + elements.add(new KnuthBox(0, notifyPos(new Position(this)), true)); } - addKnuthElementsForBorderPaddingAfter(returnList, true); - addKnuthElementsForSpaceAfter(returnList, alignment); + addKnuthElementsForBorderPaddingAfter(elements, true); + addKnuthElementsForSpaceAfter(elements, alignment); //All child content is processed. Only break-after can occur now, so... context.clearPendingMarks(); if (forcedBreakAfterLast == null) { - addKnuthElementsForBreakAfter(returnList, context); - } - - if (forcedBreakAfterLast != null) { + addKnuthElementsForBreakAfter(elements, context); + } else { forcedBreakAfterLast.clearPendingMarks(); - returnList.add(forcedBreakAfterLast); + elements.add(forcedBreakAfterLast); } context.updateKeepWithNextPending(getKeepWithNext()); setFinished(true); - return returnList; + return elements; + } + + private List getNextChildElements(LayoutManager childLM, LayoutContext context, + LayoutContext childLC, int alignment) { + return getNextChildElements(childLM, context, childLC, alignment, null, null, null); + } + + private List getNextChildElements(LayoutManager childLM, LayoutContext context, + LayoutContext childLC, int alignment, Stack lmStack, Position restartPosition, + LayoutManager restartAtLM) { + childLC.copyPendingMarksFrom(context); + childLC.setStackLimitBP(context.getStackLimitBP()); + if (childLM instanceof LineLayoutManager) { + childLC.setRefIPD(getContentAreaIPD()); + } else { + childLC.setRefIPD(referenceIPD); + } + if (childLM == this.childLMs.get(0)) { + childLC.setFlags(LayoutContext.SUPPRESS_BREAK_BEFORE); + //Handled already by the parent (break collapsing, see above) + } + + if (lmStack == null) { + return childLM.getNextKnuthElements(childLC, alignment); + } else { + if (childLM instanceof LineLayoutManager) { + return ((LineLayoutManager) childLM).getNextKnuthElements(childLC, alignment, + (LeafPosition) restartPosition); + } else { + return childLM.getNextKnuthElements(childLC, alignment, + lmStack, restartPosition, restartAtLM); + } + } } /** @@ -1654,5 +1808,13 @@ public abstract class BlockStackingLayoutManager extends AbstractLayoutManager return -1; } + /** {@inheritDoc} */ + public void reset() { + super.reset(); + breakBeforeServed = false; + firstVisibleMarkServed = false; + // TODO startIndent, endIndent + } + } diff --git a/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java index c6bd7bcec..3a688cce8 100644 --- a/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java +++ b/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java @@ -491,6 +491,9 @@ public abstract class BreakingAlgorithm { elementIndex, previousIsBox, allowedBreaks).isBox(); if (activeNodeCount == 0) { + if (ipdChanged()) { + return handleIpdChange(); + } if (!force) { log.debug("Could not find a set of breaking points " + threshold); return 0; @@ -535,6 +538,14 @@ public abstract class BreakingAlgorithm { return line; } + protected boolean ipdChanged() { + return false; + } + + protected int handleIpdChange() { + throw new IllegalStateException(); + } + /** * Recover from a {@link KnuthNode} leading to a line that is too long. * The default implementation creates a new node corresponding to a break @@ -1283,12 +1294,8 @@ public abstract class BreakingAlgorithm { * @return the width/length in millipoints */ protected int getLineWidth(int line) { - if (this.lineWidth < 0) { - throw new IllegalStateException("lineWidth must be set" - + (this.lineWidth != 0 ? " and positive, but it is: " + this.lineWidth : "")); - } else { - return this.lineWidth; - } + assert lineWidth >= 0; + return this.lineWidth; } /** @return the constant line/part width or -1 if there is no such value */ @@ -1321,7 +1328,7 @@ public abstract class BreakingAlgorithm { * @param par the corresponding paragraph * @param total the number of lines into which the paragraph will be broken */ - private void calculateBreakPoints(KnuthNode node, KnuthSequence par, + protected void calculateBreakPoints(KnuthNode node, KnuthSequence par, int total) { KnuthNode bestActiveNode = node; // use bestActiveNode to determine the optimum breakpoints diff --git a/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java b/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java index 5a80c3318..67b9b4254 100644 --- a/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/FlowLayoutManager.java @@ -22,6 +22,7 @@ package org.apache.fop.layoutmgr; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; +import java.util.Stack; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -29,8 +30,6 @@ import org.apache.commons.logging.LogFactory; import org.apache.fop.area.Area; import org.apache.fop.area.BlockParent; import org.apache.fop.fo.pagination.Flow; -import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager; -import org.apache.fop.layoutmgr.inline.WrapperLayoutManager; /** * LayoutManager for an fo:flow object. @@ -64,108 +63,151 @@ public class FlowLayoutManager extends BlockStackingLayoutManager /** {@inheritDoc} */ public List getNextKnuthElements(LayoutContext context, int alignment) { - // set layout dimensions - int flowIPD = getCurrentPV().getCurrentSpan().getColumnWidth(); - int flowBPD = getCurrentPV().getBodyRegion().getBPD(); + List elements = new LinkedList(); - // currently active LM - LayoutManager curLM; - List returnedList; - List returnList = new LinkedList(); - - while ((curLM = getChildLM()) != null) { - if (!(curLM instanceof WrapperLayoutManager) - && curLM instanceof InlineLevelLayoutManager) { - log.error("inline area not allowed under flow - ignoring"); - curLM.setFinished(true); - continue; + LayoutManager currentChildLM; + while ((currentChildLM = getChildLM()) != null) { + if (addChildElements(elements, currentChildLM, context, alignment) != null) { + return elements; } + } + + SpaceResolver.resolveElementList(elements); + setFinished(true); + + assert !elements.isEmpty(); + return elements; + } + + /** {@inheritDoc} */ + public List getNextKnuthElements(LayoutContext context, int alignment, + Position positionAtIPDChange, LayoutManager restartAtLM) { + + List elements = new LinkedList(); - int span = EN_NONE; - int disableColumnBalancing = EN_FALSE; - if (curLM instanceof BlockLayoutManager) { - span = ((BlockLayoutManager)curLM).getBlockFO().getSpan(); - disableColumnBalancing = ((BlockLayoutManager) curLM).getBlockFO() - .getDisableColumnBalancing(); - } else if (curLM instanceof BlockContainerLayoutManager) { - span = ((BlockContainerLayoutManager)curLM).getBlockContainerFO().getSpan(); - disableColumnBalancing = ((BlockContainerLayoutManager) curLM).getBlockContainerFO() - .getDisableColumnBalancing(); + LayoutManager currentChildLM = positionAtIPDChange.getLM(); + if (currentChildLM == null) { + throw new IllegalStateException( + "Cannot find layout manager from where to re-start layout after IPD change"); + } + if (restartAtLM != null && restartAtLM.getParent() == this) { + currentChildLM = restartAtLM; + setCurrentChildLM(currentChildLM); + currentChildLM.reset(); + if (addChildElements(elements, currentChildLM, context, alignment) != null) { + return elements; + } + } else { + Stack lmStack = new Stack(); + while (currentChildLM.getParent() != this) { + lmStack.push(currentChildLM); + currentChildLM = currentChildLM.getParent(); } + setCurrentChildLM(currentChildLM); + if (addChildElements(elements, currentChildLM, context, alignment, lmStack, + positionAtIPDChange, restartAtLM) != null) { + return elements; + } + } - int currentSpan = context.getCurrentSpan(); - if (currentSpan != span) { - if (span == EN_ALL) { - context.setDisableColumnBalancing(disableColumnBalancing); - } - log.debug("span change from " + currentSpan + " to " + span); - context.signalSpanChange(span); - SpaceResolver.resolveElementList(returnList); - return returnList; + while ((currentChildLM = getChildLM()) != null) { + currentChildLM.reset(); // TODO won't work with forced breaks + if (addChildElements(elements, currentChildLM, context, alignment) != null) { + return elements; } + } - // Set up a LayoutContext - //MinOptMax bpd = context.getStackLimit(); + SpaceResolver.resolveElementList(elements); + setFinished(true); - LayoutContext childLC = new LayoutContext(0); - childLC.setStackLimitBP(context.getStackLimitBP()); - childLC.setRefIPD(context.getRefIPD()); - childLC.setWritingMode(getCurrentPage().getSimplePageMaster().getWritingMode()); + assert !elements.isEmpty(); + return elements; + } - // get elements from curLM - returnedList = curLM.getNextKnuthElements(childLC, alignment); - //log.debug("FLM.getNextKnuthElements> returnedList.size() = " + returnedList.size()); - if (returnList.size() == 0 && childLC.isKeepWithPreviousPending()) { - context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending()); - childLC.clearKeepWithPreviousPending(); - } + private List addChildElements(List elements, LayoutManager childLM, LayoutContext context, + int alignment) { + return addChildElements(elements, childLM, context, alignment, null, null, null); + } - // "wrap" the Position inside each element - List tempList = returnedList; - returnedList = new LinkedList(); - wrapPositionElements(tempList, returnedList); - - if (returnedList.size() == 1 - && ElementListUtils.endsWithForcedBreak(returnedList)) { - // a descendant of this flow has break-before - returnList.addAll(returnedList); - SpaceResolver.resolveElementList(returnList); - return returnList; - } else if (returnedList.size() > 0) { - if (returnList.size() > 0 - && !ElementListUtils.startsWithForcedBreak(returnedList)) { - addInBetweenBreak(returnList, context, childLC); - } - returnList.addAll(returnedList); - if (ElementListUtils.endsWithForcedBreak(returnList)) { - if (curLM.isFinished() && !hasNextChildLM()) { - //If the layout manager is finished at this point, the pending - //marks become irrelevant. - childLC.clearPendingMarks(); - //setFinished(true); - break; - } - // a descendant of this flow has break-after - SpaceResolver.resolveElementList(returnList); - return returnList; - } + private List addChildElements(List elements, LayoutManager childLM, LayoutContext context, + int alignment, Stack lmStack, Position position, LayoutManager restartAtLM) { + if (handleSpanChange(childLM, elements, context)) { + SpaceResolver.resolveElementList(elements); + return elements; + } + + LayoutContext childLC = new LayoutContext(0); + List childrenElements = getNextChildElements(childLM, context, childLC, alignment, lmStack, + position, restartAtLM); + if (elements.isEmpty()) { + context.updateKeepWithPreviousPending(childLC.getKeepWithPreviousPending()); + } + if (!elements.isEmpty() + && !ElementListUtils.startsWithForcedBreak(childrenElements)) { + addInBetweenBreak(elements, context, childLC); + } + context.updateKeepWithNextPending(childLC.getKeepWithNextPending()); + + elements.addAll(childrenElements); + + if (ElementListUtils.endsWithForcedBreak(elements)) { + // a descendant of this flow has break-before or break-after + if (childLM.isFinished() && !hasNextChildLM()) { + setFinished(true); } + SpaceResolver.resolveElementList(elements); + return elements; + } + return null; + } - //Propagate and clear - context.updateKeepWithNextPending(childLC.getKeepWithNextPending()); - childLC.clearKeepWithNextPending(); + private boolean handleSpanChange(LayoutManager childLM, List elements, LayoutContext context) { + int span = EN_NONE; + int disableColumnBalancing = EN_FALSE; + if (childLM instanceof BlockLayoutManager) { + span = ((BlockLayoutManager)childLM).getBlockFO().getSpan(); + disableColumnBalancing = ((BlockLayoutManager) childLM).getBlockFO() + .getDisableColumnBalancing(); + } else if (childLM instanceof BlockContainerLayoutManager) { + span = ((BlockContainerLayoutManager)childLM).getBlockContainerFO().getSpan(); + disableColumnBalancing = ((BlockContainerLayoutManager) childLM).getBlockContainerFO() + .getDisableColumnBalancing(); + } - context.updateKeepWithNextPending(getKeepWithNext()); + int currentSpan = context.getCurrentSpan(); + if (currentSpan != span) { + if (span == EN_ALL) { + context.setDisableColumnBalancing(disableColumnBalancing); + } + log.debug("span change from " + currentSpan + " to " + span); + context.signalSpanChange(span); + return true; + } else { + return false; } + } - SpaceResolver.resolveElementList(returnList); - setFinished(true); + private List getNextChildElements(LayoutManager childLM, LayoutContext context, + LayoutContext childLC, int alignment, Stack lmStack, Position restartPosition, + LayoutManager restartLM) { + childLC.setStackLimitBP(context.getStackLimitBP()); + childLC.setRefIPD(context.getRefIPD()); + childLC.setWritingMode(getCurrentPage().getSimplePageMaster().getWritingMode()); - if (returnList.size() > 0) { - return returnList; + List childrenElements; + if (lmStack == null) { + childrenElements = childLM.getNextKnuthElements(childLC, alignment); } else { - return null; + childrenElements = childLM.getNextKnuthElements(childLC, + alignment, lmStack, restartPosition, restartLM); } + assert !childrenElements.isEmpty(); + + // "wrap" the Position inside each element + List tempList = childrenElements; + childrenElements = new LinkedList(); + wrapPositionElements(tempList, childrenElements); + return childrenElements; } /** @@ -353,5 +395,10 @@ public class FlowLayoutManager extends BlockStackingLayoutManager return getCurrentPV().getBodyRegion().getBPD(); } + /** {@inheritDoc} */ + public boolean isRestartable() { + return true; + } + } diff --git a/src/java/org/apache/fop/layoutmgr/InlineKnuthSequence.java b/src/java/org/apache/fop/layoutmgr/InlineKnuthSequence.java index 6d11a3c24..104c71131 100644 --- a/src/java/org/apache/fop/layoutmgr/InlineKnuthSequence.java +++ b/src/java/org/apache/fop/layoutmgr/InlineKnuthSequence.java @@ -57,16 +57,12 @@ public class InlineKnuthSequence extends KnuthSequence { return true; } - /* (non-Javadoc) - * {@inheritDoc} - */ + /** {@inheritDoc} */ public boolean canAppendSequence(KnuthSequence sequence) { return sequence.isInlineSequence() && !isClosed; } - /* (non-Javadoc) - * {@inheritDoc} - */ + /** {@inheritDoc} */ public boolean appendSequence(KnuthSequence sequence) { if (!canAppendSequence(sequence)) { return false; @@ -83,18 +79,14 @@ public class InlineKnuthSequence extends KnuthSequence { return true; } - /* (non-Javadoc) - * {@inheritDoc} - */ + /** {@inheritDoc} */ public boolean appendSequence(KnuthSequence sequence, boolean keepTogether, BreakElement breakElement) { return appendSequence(sequence); } - /* (non-Javadoc) - * {@inheritDoc} - */ + /** {@inheritDoc} */ public KnuthSequence endSequence() { if (!isClosed) { add(new KnuthPenalty(0, -KnuthElement.INFINITE, false, null, false)); diff --git a/src/java/org/apache/fop/layoutmgr/LayoutContext.java b/src/java/org/apache/fop/layoutmgr/LayoutContext.java index 41d4c5cfc..81726e57b 100644 --- a/src/java/org/apache/fop/layoutmgr/LayoutContext.java +++ b/src/java/org/apache/fop/layoutmgr/LayoutContext.java @@ -78,15 +78,6 @@ public class LayoutContext { * level LM to allow them to optimize returned break possibilities. */ private MinOptMax stackLimitBP; - /** - * Total available stacking dimension for a "galley-level" layout - * manager in inline-progression-direction. It is passed by the - * parent LM. For LineLM, the block LM determines this based on - * indent properties. - * These LM may wish to pass this information down to lower - * level LM to allow them to optimize returned break possibilities. - */ - private MinOptMax stackLimitIP; /** to keep track of spanning in multi-column layout */ private int currentSpan = Constants.NOT_SET; @@ -158,7 +149,7 @@ public class LayoutContext { this.flags = parentLC.flags; this.refIPD = parentLC.refIPD; this.writingMode = parentLC.writingMode; - setStackLimitsFrom(parentLC); + setStackLimitBP(parentLC.getStackLimitBP()); this.leadingSpace = parentLC.leadingSpace; //??? this.trailingSpace = parentLC.trailingSpace; //??? this.hyphContext = parentLC.hyphContext; @@ -183,7 +174,6 @@ public class LayoutContext { this.flags = flags; this.refIPD = 0; stackLimitBP = new MinOptMax(0); - stackLimitIP = new MinOptMax(0); leadingSpace = null; trailingSpace = null; } @@ -397,31 +387,6 @@ public class LayoutContext { return stackLimitBP; } - /** - * Sets the stack limit in inline-progression-dimension. - * @param limit the stack limit - */ - public void setStackLimitIP(MinOptMax limit) { - stackLimitIP = limit; - } - - /** - * Returns the stack limit in inline-progression-dimension. - * @return the stack limit - */ - public MinOptMax getStackLimitIP() { - return stackLimitIP; - } - - /** - * Sets (Copies) the stack limits in both directions from another layout context. - * @param context the layout context to take the values from - */ - public void setStackLimitsFrom(LayoutContext context) { - setStackLimitBP(context.getStackLimitBP()); - setStackLimitIP(context.getStackLimitIP()); - } - /** * Sets the inline-progression-dimension of the nearest ancestor reference area. */ @@ -662,8 +627,6 @@ public class LayoutContext { return "Layout Context:" + "\nStack Limit BPD: \t" + (getStackLimitBP() == null ? "null" : getStackLimitBP().toString()) - + "\nStack Limit IPD: \t" - + (getStackLimitIP() == null ? "null" : getStackLimitIP().toString()) + "\nTrailing Space: \t" + (getTrailingSpace() == null ? "null" : getTrailingSpace().toString()) + "\nLeading Space: \t" diff --git a/src/java/org/apache/fop/layoutmgr/LayoutManager.java b/src/java/org/apache/fop/layoutmgr/LayoutManager.java index f19588a77..454b8b366 100644 --- a/src/java/org/apache/fop/layoutmgr/LayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/LayoutManager.java @@ -20,6 +20,7 @@ package org.apache.fop.layoutmgr; import java.util.List; +import java.util.Stack; import org.apache.fop.area.Area; import org.apache.fop.datatypes.PercentBaseContext; @@ -219,4 +220,36 @@ public interface LayoutManager extends PercentBaseContext { * @return the same Position but with a position index */ Position notifyPos(Position pos); + + /** + * Re-initializes this layout manager in order to re-generate its Knuth + * elements according to a new IPD value. + */ + void reset(); + + /** + * Returns {@code true} if this layout manager is able to re-generate its + * Knuth elements after an IPD change. + * + * @return {@code true} if this layout manager can be restarted after an IPD + * change + */ + boolean isRestartable(); + + /** + * Returns an updated list of Knuth elements corresponding to this layout + * manager, after a change of IPD has been detected. + * + * @param context the layout context + * @param alignment the alignment + * @param lmStack the stack of LMs that are active at the IPD change + * @param positionAtIPDChange the position corresponding to the element + * finishing the page before the IPD change + * @param restartAtLM if not null, the layout manager from which to restart. + * That is, the IPD change occurs between two block elements and not inside + * a paragraph + * @return an updated list of elements, taking the new IPD into account + */ + List getNextKnuthElements(LayoutContext context, int alignment, Stack lmStack, + Position positionAtIPDChange, LayoutManager restartAtLM); } diff --git a/src/java/org/apache/fop/layoutmgr/LeafPosition.java b/src/java/org/apache/fop/layoutmgr/LeafPosition.java index ed8cc94e2..8b2a5f4bc 100644 --- a/src/java/org/apache/fop/layoutmgr/LeafPosition.java +++ b/src/java/org/apache/fop/layoutmgr/LeafPosition.java @@ -21,15 +21,20 @@ package org.apache.fop.layoutmgr; public class LeafPosition extends Position { - private int iLeafPos; + private int leafPos; public LeafPosition(LayoutManager lm, int pos) { super(lm); - iLeafPos = pos; + leafPos = pos; + } + + public LeafPosition(LayoutManager layoutManager, int pos, int index) { + super(layoutManager, index); + leafPos = pos; } public int getLeafPos() { - return iLeafPos; + return leafPos; } public boolean generatesAreas() { diff --git a/src/java/org/apache/fop/layoutmgr/PageBreaker.java b/src/java/org/apache/fop/layoutmgr/PageBreaker.java index ed0b37602..86c3fccf8 100644 --- a/src/java/org/apache/fop/layoutmgr/PageBreaker.java +++ b/src/java/org/apache/fop/layoutmgr/PageBreaker.java @@ -77,6 +77,14 @@ public class PageBreaker extends AbstractBreaker { return pslm.getPageProvider(); } + /** + * Starts the page breaking process. + * @param flowBPD the constant available block-progression-dimension (used for every part) + */ + void doLayout(int flowBPD) { + doLayout(flowBPD, false); + } + /** {@inheritDoc} */ protected PageBreakingLayoutListener createLayoutListener() { return new PageBreakingLayoutListener() { @@ -121,6 +129,12 @@ public class PageBreaker extends AbstractBreaker { /** {@inheritDoc} */ protected int getNextBlockList(LayoutContext childLC, int nextSequenceStartsOn) { + return getNextBlockList(childLC, nextSequenceStartsOn, null, null, null); + } + + /** {@inheritDoc} */ + protected int getNextBlockList(LayoutContext childLC, int nextSequenceStartsOn, + Position positionAtIPDChange, LayoutManager restartLM, List firstElements) { if (!firstPart) { // if this is the first page that will be created by // the current BlockSequence, it could have a break @@ -132,7 +146,8 @@ public class PageBreaker extends AbstractBreaker { pageBreakHandled = true; pageProvider.setStartOfNextElementList(pslm.getCurrentPageNum(), pslm.getCurrentPV().getCurrentSpan().getCurrentFlowIndex()); - return super.getNextBlockList(childLC, nextSequenceStartsOn); + return super.getNextBlockList(childLC, nextSequenceStartsOn, positionAtIPDChange, + restartLM, firstElements); } private boolean containsFootnotes(List contentList, LayoutContext context) { @@ -209,6 +224,24 @@ public class PageBreaker extends AbstractBreaker { return contentList; } + /** {@inheritDoc} */ + protected List getNextKnuthElements(LayoutContext context, int alignment, + Position positionAtIPDChange, LayoutManager restartAtLM) { + List contentList = null; + + do { + contentList = childFLM.getNextKnuthElements(context, alignment, positionAtIPDChange, + restartAtLM); + } while (!childFLM.isFinished() && contentList == null); + + // scan contentList, searching for footnotes + if (containsFootnotes(contentList, context)) { + // handle the footnote separator + handleFootnoteSeparator(); + } + return contentList; + } + /** * @return current display alignment */ diff --git a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java index fc0226cec..52238e9be 100644 --- a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java +++ b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java @@ -96,6 +96,9 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { //Controls whether a single part should be forced if possible (ex. block-container) private boolean favorSinglePart = false; + private boolean ipdChange; + private KnuthNode bestNodeForIPDChange; + //Used to keep track of switches in keep-context private int currentKeepContext = Constants.EN_AUTO; private KnuthNode lastBeforeKeepContextSwitch; @@ -1073,4 +1076,63 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { } + /** {@inheritDoc} */ + protected boolean ipdChanged() { + return ipdChange; + } + + /** {@inheritDoc} */ + protected int handleIpdChange() { + log.trace("Best node for ipd change:" + bestNodeForIPDChange); + // TODO finish() + /* + * The third parameter is used to determine if this is the last page, so + * if the content must be vertically justified or not. If we are here + * this means that there is further content and the next page has a + * different ipd. So tweak the parameter to fall into the non-last-page + * case. + */ + calculateBreakPoints(bestNodeForIPDChange, par, bestNodeForIPDChange.line + 1); + activeLines = null; + return bestNodeForIPDChange.line; + } + + /** + * Add a node at the end of the given line's existing active nodes. + * If this is the first node in the line, adjust endLine accordingly. + * @param line number of the line ending at the node's corresponding breakpoint + * @param node the active node to add + */ + protected void addNode(int line, KnuthNode node) { + if (node.position < par.size() - 1 && line > 0 && ipdChange(line - 1)) { + log.trace("IPD changes at page " + line); + ipdChange = true; + if (bestNodeForIPDChange == null + || node.totalDemerits < bestNodeForIPDChange.totalDemerits) { + bestNodeForIPDChange = node; + } + } else { + if (node.position == par.size() - 1) { + /* + * The whole sequence could actually fit on the last page before + * the IPD change. No need to do any special handling. + */ + ipdChange = false; + } + super.addNode(line, node); + } + } + + KnuthNode getBestNodeBeforeIPDChange() { + return bestNodeForIPDChange; + } + + /** {@inheritDoc} */ + protected boolean ipdChange(int line) { + if (pageProvider == null) { + return false; + } + return pageProvider.ipdChange(line); + } + } diff --git a/src/java/org/apache/fop/layoutmgr/PageProvider.java b/src/java/org/apache/fop/layoutmgr/PageProvider.java index c03aea091..bd556366a 100644 --- a/src/java/org/apache/fop/layoutmgr/PageProvider.java +++ b/src/java/org/apache/fop/layoutmgr/PageProvider.java @@ -160,6 +160,33 @@ public class PageProvider implements Constants { return new int[] {colIndex, columnCount}; } + /** + * Returns true if the part following the given one has a different IPD. + * + * @param index index of the current part + * @return true if the following part has a different IPD, false otherwise + */ + public boolean ipdChange(int index) { + int columnCount = 0; + int colIndex = startColumnOfCurrentElementList + index; + int pageIndex = -1; + Page page; + do { + colIndex -= columnCount; + pageIndex++; + page = getPage(false, pageIndex, RELTO_CURRENT_ELEMENT_LIST); + columnCount = page.getPageViewport().getCurrentSpan().getColumnCount(); + } while (colIndex >= columnCount); + if (colIndex + 1 < columnCount) { + // Next part is a column on same page => same IPD + return false; + } else { + Page nextPage = getPage(false, pageIndex + 1, RELTO_CURRENT_ELEMENT_LIST); + return page.getPageViewport().getBodyRegion().getIPD() + != nextPage.getPageViewport().getBodyRegion().getIPD(); + } + } + /** * Checks if a break at the passed index would start a new page * @param index the index of the element before the break diff --git a/src/java/org/apache/fop/layoutmgr/Position.java b/src/java/org/apache/fop/layoutmgr/Position.java index 42034cab4..10ad5878e 100644 --- a/src/java/org/apache/fop/layoutmgr/Position.java +++ b/src/java/org/apache/fop/layoutmgr/Position.java @@ -28,6 +28,11 @@ public class Position { layoutManager = lm; } + public Position(LayoutManager lm, int index) { + this(lm); + setIndex(index); + } + public LayoutManager getLM() { return layoutManager; } diff --git a/src/java/org/apache/fop/layoutmgr/SpaceResolver.java b/src/java/org/apache/fop/layoutmgr/SpaceResolver.java index 416ee924d..ff464ce6a 100644 --- a/src/java/org/apache/fop/layoutmgr/SpaceResolver.java +++ b/src/java/org/apache/fop/layoutmgr/SpaceResolver.java @@ -19,7 +19,6 @@ package org.apache.fop.layoutmgr; -import java.util.LinkedList; import java.util.List; import java.util.ListIterator; @@ -565,6 +564,11 @@ public class SpaceResolver { public Position getOriginalBreakPosition() { return this.originalPosition; } + + public Position getPosition() { + return originalPosition; + } + } /** diff --git a/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java index 4bfbd46f5..d5949f4a2 100644 --- a/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java @@ -21,10 +21,6 @@ package org.apache.fop.layoutmgr; import java.util.LinkedList; import java.util.List; -import java.util.ListIterator; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.fop.area.Area; import org.apache.fop.area.Block; @@ -35,10 +31,7 @@ import org.apache.fop.fo.pagination.PageSequence; import org.apache.fop.fo.pagination.SideRegion; import org.apache.fop.fo.pagination.StaticContent; import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener; -import org.apache.fop.layoutmgr.inline.InlineLevelLayoutManager; import org.apache.fop.layoutmgr.inline.TextLayoutManager; -import org.apache.fop.traits.MinOptMax; -import org.apache.fop.util.ListUtil; /** * LayoutManager for an fo:flow object. @@ -48,11 +41,6 @@ import org.apache.fop.util.ListUtil; */ public class StaticContentLayoutManager extends BlockStackingLayoutManager { - /** - * logging instance - */ - private static Log log = LogFactory.getLog(StaticContentLayoutManager.class); - private RegionReference targetRegion; private Block targetBlock; private SideRegion regionFO; @@ -89,96 +77,7 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager { /** {@inheritDoc} */ public List getNextKnuthElements(LayoutContext context, int alignment) { - if (true) { - throw new UnsupportedOperationException( - "Shouldn't this method be emptied because it's never called at all?"); - } - //TODO Empty this method?!? - // set layout dimensions - setContentAreaIPD(context.getRefIPD()); - setContentAreaBPD(context.getStackLimitBP().opt); - - //TODO Copied from elsewhere. May be worthwhile to factor out the common parts. - // currently active LM - BlockLevelLayoutManager curLM; - BlockLevelLayoutManager prevLM = null; - MinOptMax stackSize = new MinOptMax(); - List returnedList; - List returnList = new LinkedList(); - - while ((curLM = ((BlockLevelLayoutManager) getChildLM())) != null) { - if (curLM instanceof InlineLevelLayoutManager) { - log.error("inline area not allowed under flow - ignoring"); - curLM.setFinished(true); - continue; - } - - // Set up a LayoutContext - MinOptMax bpd = context.getStackLimitBP(); - - LayoutContext childLC = new LayoutContext(0); - childLC.setStackLimitBP(MinOptMax.subtract(bpd, stackSize)); - childLC.setRefIPD(context.getRefIPD()); - - // get elements from curLM - returnedList = curLM.getNextKnuthElements(childLC, alignment); - //log.debug("FLM.getNextKnuthElements> returnedList.size() = " - // + returnedList.size()); - - // "wrap" the Position inside each element - List tempList = returnedList; - KnuthElement tempElement; - returnedList = new LinkedList(); - ListIterator listIter = tempList.listIterator(); - while (listIter.hasNext()) { - tempElement = (KnuthElement)listIter.next(); - tempElement.setPosition(new NonLeafPosition(this, tempElement.getPosition())); - returnedList.add(tempElement); - } - - if (returnedList.size() == 1 - && ((KnuthElement)returnedList.get(0)).isPenalty() - && ((KnuthPenalty)returnedList.get(0)).getP() == -KnuthElement.INFINITE) { - // a descendant of this flow has break-before - returnList.addAll(returnedList); - return returnList; - } else { - if (!returnList.isEmpty()) { - // there is a block before this one - if (prevLM.mustKeepWithNext() - || curLM.mustKeepWithPrevious()) { - // add an infinite penalty to forbid a break between blocks - returnList.add(new KnuthPenalty(0, - KnuthElement.INFINITE, false, - new Position(this), false)); - } else if (!((KnuthElement) ListUtil.getLast(returnList)) - .isGlue()) { - // add a null penalty to allow a break between blocks - returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false)); - } - } -/*LF*/ if (!returnedList.isEmpty()) { // controllare! - returnList.addAll(returnedList); - final KnuthElement last = (KnuthElement) ListUtil - .getLast(returnedList); - if (last.isPenalty() - && ((KnuthPenalty) last).getP() == -KnuthElement.INFINITE) { - // a descendant of this flow has break-after -/*LF*/ //log.debug("FLM - break after!!"); - return returnList; - } -/*LF*/ } - } - prevLM = curLM; - } - - setFinished(true); - - if (returnList.isEmpty()) { - return null; - } else { - return returnList; - } + throw new IllegalStateException(); } /** diff --git a/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java index a5247d652..95f798161 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/ContentLayoutManager.java @@ -27,6 +27,7 @@ import java.util.ListIterator; 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.LineArea; @@ -43,7 +44,6 @@ import org.apache.fop.layoutmgr.PageSequenceLayoutManager; import org.apache.fop.layoutmgr.Position; import org.apache.fop.layoutmgr.PositionIterator; import org.apache.fop.layoutmgr.SpaceSpecifier; -import org.apache.fop.traits.MinOptMax; /** * Content Layout Manager. @@ -111,8 +111,6 @@ public class ContentLayoutManager extends AbstractBaseLayoutManager LayoutContext childLC = new LayoutContext(LayoutContext.NEW_AREA); childLC.setLeadingSpace(new SpaceSpecifier(false)); childLC.setTrailingSpace(new SpaceSpecifier(false)); - // set stackLimit for lines - childLC.setStackLimitIP(new MinOptMax(ipd)); childLC.setRefIPD(ipd); int lineHeight = 14000; @@ -129,8 +127,7 @@ public class ContentLayoutManager extends AbstractBaseLayoutManager stackSize = 0; - List contentList = - getNextKnuthElements(childLC, Constants.EN_START); + List contentList = getNextKnuthElements(childLC, Constants.EN_START); ListIterator contentIter = contentList.listIterator(); while (contentIter.hasNext()) { KnuthElement element = (KnuthElement) contentIter.next(); @@ -149,8 +146,7 @@ public class ContentLayoutManager extends AbstractBaseLayoutManager lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true); lc.setLeadingSpace(new SpaceSpecifier(false)); lc.setTrailingSpace(new SpaceSpecifier(false)); - KnuthPossPosIter contentPosIter = - new KnuthPossPosIter(contentList, 0, contentList.size()); + KnuthPossPosIter contentPosIter = new KnuthPossPosIter(contentList, 0, contentList.size()); curLM.addAreas(contentPosIter, lc); } diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java index 0c332281f..6e0c34a82 100755 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineLayoutManager.java @@ -248,8 +248,6 @@ public class InlineLayoutManager extends InlineStackingLayoutManager { List returnList = new LinkedList(); KnuthSequence lastSequence = null; - SpaceSpecifier leadingSpace = context.getLeadingSpace(); - if (fobj instanceof Title) { alignmentContext = new AlignmentContext(font, lineHeight.getOptimum(this).getLength().getValue(this), @@ -274,14 +272,6 @@ public class InlineLayoutManager extends InlineStackingLayoutManager { if (getSpaceStart() != null) { context.getLeadingSpace().addSpace(new SpaceVal(getSpaceStart(), this)); } - - // Check for "fence" - if (hasLeadingFence(!context.isFirstArea())) { - // Reset leading space sequence for child areas - leadingSpace = new SpaceSpecifier(false); - } - // Reset state variables - clearPrevIPD(); // Clear stored prev content dimensions } StringBuffer trace = new StringBuffer("InlineLM:"); diff --git a/src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java index 963b98b37..65e59554f 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/InlineStackingLayoutManager.java @@ -19,12 +19,13 @@ package org.apache.fop.layoutmgr.inline; -import java.util.LinkedList; import java.util.Iterator; +import java.util.LinkedList; import java.util.List; import java.util.ListIterator; -import java.util.HashMap; +import org.apache.fop.area.Area; +import org.apache.fop.area.inline.Space; import org.apache.fop.fo.FObj; import org.apache.fop.fo.properties.SpaceProperty; import org.apache.fop.layoutmgr.AbstractLayoutManager; @@ -34,8 +35,6 @@ import org.apache.fop.layoutmgr.LayoutManager; import org.apache.fop.layoutmgr.NonLeafPosition; import org.apache.fop.layoutmgr.Position; import org.apache.fop.layoutmgr.PositionIterator; -import org.apache.fop.area.Area; -import org.apache.fop.area.inline.Space; import org.apache.fop.traits.MinOptMax; /** @@ -62,12 +61,6 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager } } - - /** - * Size of any start or end borders and padding. - */ - private MinOptMax allocIPD = new MinOptMax(0); - /** * Size of border and padding in BPD (ie, before and after). */ @@ -78,9 +71,6 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager /** The child layout context */ protected LayoutContext childLC; - /** Used to store previous content IPD for each child LM. */ - private HashMap hmPrevIPD = new HashMap(); - /** * Create an inline stacking layout manager. * This is used for fo's that create areas that @@ -148,22 +138,6 @@ public abstract class InlineStackingLayoutManager extends AbstractLayoutManager return null; } - /** - * TODO: Explain this method - * @param lm ??? - * @return ??? - */ - protected MinOptMax getPrevIPD(LayoutManager lm) { - return (MinOptMax) hmPrevIPD.get(lm); - } - - /** - * Clear the previous IPD calculation. - */ - protected void clearPrevIPD() { - hmPrevIPD.clear(); - } - /** * Returns the current area. * @return the current area diff --git a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java index aea851f54..7c30ab9bb 100644 --- a/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/inline/LineLayoutManager.java @@ -20,6 +20,7 @@ package org.apache.fop.layoutmgr.inline; import java.util.ArrayList; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; @@ -116,8 +117,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager * inline break positions. */ private static class LineBreakPosition extends LeafPosition { - private int iParIndex; // index of the Paragraph this Position refers to - private int iStartIndex; //index of the first element this Position refers to + private int parIndex; // index of the Paragraph this Position refers to + private int startIndex; //index of the first element this Position refers to private int availableShrink; private int availableStretch; private int difference; @@ -130,16 +131,16 @@ public class LineLayoutManager extends InlineStackingLayoutManager private int spaceAfter; private int baseline; - LineBreakPosition(LayoutManager lm, int index, int iStartIndex, int iBreakIndex, + LineBreakPosition(LayoutManager lm, int index, int startIndex, int breakIndex, int shrink, int stretch, int diff, double ipdA, double adjust, int ind, int lh, int lw, int sb, int sa, int bl) { - super(lm, iBreakIndex); + super(lm, breakIndex); availableShrink = shrink; availableStretch = stretch; difference = diff; - iParIndex = index; - this.iStartIndex = iStartIndex; + parIndex = index; + this.startIndex = startIndex; ipdAdjust = ipdA; dAdjust = adjust; startIndent = ind; @@ -167,18 +168,18 @@ public class LineLayoutManager extends InlineStackingLayoutManager private Length lineHeight; private int lead; private int follow; - private AlignmentContext alignmentContext = null; + private AlignmentContext alignmentContext; - private List knuthParagraphs = null; - private int iReturnedLBP = 0; - - // parameters of Knuth's algorithm: - // penalty value for flagged penalties - private int flaggedPenalty = 50; + private List knuthParagraphs; private LineLayoutPossibilities lineLayouts; private List lineLayoutsList; - private int iLineWidth = 0; + private int ipd = 0; + /** + * When layout must be re-started due to a change of IPD, there is no need + * to perform hyphenation on the remaining Knuth sequence once again. + */ + private boolean hyphenationPerformed; /** * this constant is used to create elements when text-align is center: @@ -238,7 +239,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager } else { lineFiller = new MinOptMax(lastLineEndIndent, lastLineEndIndent, - layoutManager.iLineWidth); + layoutManager.ipd); } // add auxiliary elements at the beginning of the paragraph @@ -319,11 +320,9 @@ public class LineLayoutManager extends InlineStackingLayoutManager private int activePossibility; private int addedPositions; private int textIndent; - private int fillerMinWidth; private int lineHeight; private int lead; private int follow; - private int maxDiff; private static final double MAX_DEMERITS = 10e6; public LineBreakingAlgorithm (int pageAlign, @@ -334,22 +333,17 @@ public class LineLayoutManager extends InlineStackingLayoutManager super(textAlign, textAlignLast, first, false, maxFlagCount); pageAlignment = pageAlign; textIndent = indent; - fillerMinWidth = fillerWidth; lineHeight = lh; lead = ld; follow = fl; thisLLM = llm; activePossibility = -1; - maxDiff = fobj.getWidows() >= fobj.getOrphans() - ? fobj.getWidows() - : fobj.getOrphans(); } public void updateData1(int lineCount, double demerits) { lineLayouts.addPossibility(lineCount, demerits); - if (super.log.isTraceEnabled()) { - super.log.trace( - "Layout possibility in " + lineCount + " lines; break at position:"); + if (log.isTraceEnabled()) { + log.trace("Layout possibility in " + lineCount + " lines; break at position:"); } } @@ -430,7 +424,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager // true if this line contains only zero-height, auxiliary boxes // and the actual line width is 0; in this case, the line "collapses" // i.e. the line area will have bpd = 0 - boolean bZeroHeightLine = (difference == iLineWidth); + boolean bZeroHeightLine = (difference == ipd); // if line-stacking-strategy is "font-height", the line height // is not affected by its content @@ -486,7 +480,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager firstElementIndex, lastElementIndex, availableShrink, availableStretch, difference, ratio, 0, indent, - 0, iLineWidth, 0, 0, 0); + 0, ipd, 0, 0, 0); } else { return new LineBreakPosition(thisLLM, knuthParagraphs.indexOf(par), @@ -494,18 +488,11 @@ public class LineLayoutManager extends InlineStackingLayoutManager availableShrink, availableStretch, difference, ratio, 0, indent, lineLead + lineFollow, - iLineWidth, spaceBefore, spaceAfter, + ipd, spaceBefore, spaceAfter, lineLead); } } - public int findBreakingPoints(Paragraph par, /*int lineWidth,*/ - double threshold, boolean force, - int allowedBreaks) { - return super.findBreakingPoints(par, /*lineWidth,*/ - threshold, force, allowedBreaks); - } - protected int filterActiveNodes() { KnuthNode bestActiveNode = null; @@ -578,13 +565,10 @@ public class LineLayoutManager extends InlineStackingLayoutManager FontInfo fi = fobj.getFOEventHandler().getFontInfo(); FontTriplet[] fontkeys = fobj.getCommonFont().getFontState(fi); Font fs = fi.getFontInstance(fontkeys[0], fobj.getCommonFont().fontSize.getValue(this)); - alignmentContext - = new AlignmentContext(fs, lineHeight.getValue(this), context.getWritingMode()); + alignmentContext = new AlignmentContext(fs, lineHeight.getValue(this), + context.getWritingMode()); context.setAlignmentContext(alignmentContext); - // Get a break from currently active child LM - // Set up constraints for inline level managers - - clearPrevIPD(); + ipd = context.getRefIPD(); //PHASE 1: Create Knuth elements if (knuthParagraphs == null) { @@ -606,34 +590,33 @@ public class LineLayoutManager extends InlineStackingLayoutManager //PHASE 2: Create line breaks return createLineBreaks(context.getBPAlignment(), context); - /* - LineBreakPosition lbp = null; - if (breakpoints == null) { - // find the optimal line breaking points for each paragraph - breakpoints = new ArrayList(); - ListIterator paragraphsIterator - = knuthParagraphs.listIterator(knuthParagraphs.size()); - Paragraph currPar = null; - while (paragraphsIterator.hasPrevious()) { - currPar = (Paragraph) paragraphsIterator.previous(); - findBreakingPoints(currPar, context.getStackLimit().opt); - } - }*/ + } - //PHASE 3: Return lines + public List getNextKnuthElements(LayoutContext context, int alignment, + LeafPosition restartPosition) { + log.trace("Restarting line breaking from index " + restartPosition.getIndex()); + int parIndex = restartPosition.getLeafPos(); + Paragraph paragraph = (Paragraph) knuthParagraphs.get(parIndex); + for (int i = 0; i <= restartPosition.getIndex(); i++) { + paragraph.remove(0); + } + Iterator iter = paragraph.iterator(); + while (iter.hasNext() && !((KnuthElement) iter.next()).isBox()) { + iter.remove(); + } + if (!iter.hasNext()) { + knuthParagraphs.remove(parIndex); + } - /* - // get a break point from the list - lbp = (LineBreakPosition) breakpoints.get(iReturnedLBP ++); - if (iReturnedLBP == breakpoints.size()) { + // return finished when there's no content + if (knuthParagraphs.size() == 0) { setFinished(true); + return null; } - BreakPoss curLineBP = new BreakPoss(lbp); - curLineBP.setFlag(BreakPoss.ISLAST, isFinished()); - curLineBP.setStackingSize(new MinOptMax(lbp.lineHeight)); - return curLineBP; - */ + ipd = context.getRefIPD(); + //PHASE 2: Create line breaks + return createLineBreaks(context.getBPAlignment(), context); } /** @@ -643,22 +626,18 @@ public class LineLayoutManager extends InlineStackingLayoutManager private void collectInlineKnuthElements(LayoutContext context) { LayoutContext inlineLC = new LayoutContext(context); - InlineLevelLayoutManager curLM; - List returnedList = null; - iLineWidth = context.getStackLimitIP().opt; - // convert all the text in a sequence of paragraphs made // of KnuthBox, KnuthGlue and KnuthPenalty objects - boolean bPrevWasKnuthBox = false; + boolean previousIsBox = false; StringBuffer trace = new StringBuffer("LineLM:"); Paragraph lastPar = null; + InlineLevelLayoutManager curLM; while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) { - returnedList = curLM.getNextKnuthElements(inlineLC, effectiveAlignment); - if (returnedList == null - || returnedList.size() == 0) { + List inlineElements = curLM.getNextKnuthElements(inlineLC, effectiveAlignment); + if (inlineElements == null || inlineElements.size() == 0) { /* curLM.getNextKnuthElements() returned null or an empty list; * this can happen if there is nothing more to layout, * so just iterate once more to see if there are other children */ @@ -666,7 +645,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager } if (lastPar != null) { - KnuthSequence firstSeq = (KnuthSequence) returnedList.get(0); + KnuthSequence firstSeq = (KnuthSequence) inlineElements.get(0); // finish last paragraph before a new block sequence if (!firstSeq.isInlineSequence()) { @@ -676,7 +655,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager if (log.isTraceEnabled()) { trace.append(" ]"); } - bPrevWasKnuthBox = false; + previousIsBox = false; } // does the first element of the first paragraph add to an existing word? @@ -684,27 +663,24 @@ public class LineLayoutManager extends InlineStackingLayoutManager KnuthElement thisElement; thisElement = (KnuthElement) firstSeq.get(0); if (thisElement.isBox() && !thisElement.isAuxiliary() - && bPrevWasKnuthBox) { + && previousIsBox) { lastPar.addALetterSpace(); } } } // loop over the KnuthSequences (and single KnuthElements) in returnedList - ListIterator iter = returnedList.listIterator(); + ListIterator iter = inlineElements.listIterator(); while (iter.hasNext()) { KnuthSequence sequence = (KnuthSequence) iter.next(); // the sequence contains inline Knuth elements if (sequence.isInlineSequence()) { // look at the last element ListElement lastElement = sequence.getLast(); - if (lastElement == null) { - throw new NullPointerException( - "Sequence was empty! lastElement is null"); - } - bPrevWasKnuthBox = lastElement.isBox() - && !((KnuthElement) lastElement).isAuxiliary() - && ((KnuthElement) lastElement).getW() != 0; + assert lastElement != null; + previousIsBox = lastElement.isBox() + && !((KnuthElement) lastElement).isAuxiliary() + && ((KnuthElement) lastElement).getW() != 0; // if last paragraph is open, add the new elements to the paragraph // else this is the last paragraph @@ -729,8 +705,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager // finish last paragraph if it was closed with a linefeed if (lastElement.isPenalty() - && ((KnuthPenalty) lastElement).getP() - == -KnuthPenalty.INFINITE) { + && ((KnuthPenalty) lastElement).getP() == -KnuthPenalty.INFINITE) { // a penalty item whose value is -inf // represents a preserved linefeed, // which forces a line break @@ -738,7 +713,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager if (!lastPar.containsBox()) { //only a forced linefeed on this line //-> compensate with an auxiliary glue - lastPar.add(new KnuthGlue(iLineWidth, 0, iLineWidth, null, true)); + lastPar.add(new KnuthGlue(ipd, 0, ipd, null, true)); } lastPar.endParagraph(); ElementListObserver.observe(lastPar, "line", null); @@ -746,7 +721,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager if (log.isTraceEnabled()) { trace.append(" ]"); } - bPrevWasKnuthBox = false; + previousIsBox = false; } } else { // the sequence is a block sequence // the positions will be wrapped with this LM in postProcessLineBreaks @@ -767,134 +742,6 @@ public class LineLayoutManager extends InlineStackingLayoutManager log.trace(trace); } - /** - * Find a set of breaking points. - * This method is called only once by getNextBreakPoss, and it - * subsequently calls the other findBreakingPoints() method with - * different parameters, until a set of breaking points is found. - * - * @param par the list of elements that must be parted - * into lines - * @param lineWidth the desired length ot the lines - */ - /* - private void findBreakingPoints(Paragraph par, int lineWidth) { - // maximum adjustment ratio permitted - float maxAdjustment = 1; - - // first try - if (!findBreakingPoints(par, lineWidth, maxAdjustment, false)) { - // the first try failed, now try something different - log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment); - if (hyphenationProperties.hyphenate == Constants.EN_TRUE) { - // consider every hyphenation point as a legal break - findHyphenationPoints(par); - } else { - // try with a higher threshold - maxAdjustment = 5; - } - - if (!findBreakingPoints(par, lineWidth, maxAdjustment, false)) { - // the second try failed too, try with a huge threshold; - // if this fails too, use a different algorithm - log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment - + (hyphenationProperties.hyphenate == Constants.EN_TRUE ? " and hyphenation" : "")); - maxAdjustment = 20; - if (!findBreakingPoints(par, lineWidth, maxAdjustment, true)) { - log.debug("No set of breaking points found, using first-fit algorithm"); - } - } - } - } - - private boolean findBreakingPoints(Paragraph par, int lineWidth, - double threshold, boolean force) { - KnuthParagraph knuthPara = new KnuthParagraph(par); - int lines = knuthPara.findBreakPoints(lineWidth, threshold, force); - if (lines == 0) { - return false; - } - - for (int i = lines-1; i >= 0; i--) { - int line = i+1; - if (log.isTraceEnabled()) { - log.trace("Making line from " + knuthPara.getStart(i) + " to " + - knuthPara.getEnd(i)); - } - // compute indent and adjustment ratio, according to - // the value of text-align and text-align-last - - int difference = knuthPara.getDifference(i); - if (line == lines) { - difference += par.lineFillerWidth; - } - int textAlign = (line < lines) - ? textAlignment : textAlignmentLast; - int indent = (textAlign == EN_CENTER) - ? difference / 2 - : (textAlign == EN_END) ? difference : 0; - indent += (line == 1 && knuthParagraphs.indexOf(par) == 0) - ? textIndent.getValue(this) : 0; - double ratio = (textAlign == EN_JUSTIFY) - ? knuthPara.getAdjustRatio(i) : 0; - - int start = knuthPara.getStart(i); - int end = knuthPara.getEnd(i); - makeLineBreakPosition(par, start, end, 0, ratio, indent); - } - return true; - } - - private void makeLineBreakPosition(Paragraph par, - int firstElementIndex, int lastElementIndex, - int insertIndex, double ratio, int indent) { - // line height calculation - - int halfLeading = (lineHeight - lead - follow) / 2; - // height above the main baseline - int lineLead = lead + halfLeading; - // maximum size of top and bottom alignment - int lineFollow = follow + halfLeading; - - ListIterator inlineIterator - = par.listIterator(firstElementIndex); - for (int j = firstElementIndex; - j <= lastElementIndex; - j++) { - KnuthElement element = (KnuthElement) inlineIterator.next(); - if (element.isBox()) { - KnuthInlineBox box = (KnuthInlineBox)element; - if (box.getLead() > lineLead) { - lineLead = box.getLead(); - } - if (box.getTotal() > lineFollow) { - lineFollow = box.getTotal(); - } - if (box.getMiddle() > lineLead + middleShift) { - lineLead += box.getMiddle() - - lineLead - middleShift; - } - if (box.getMiddle() > middlefollow - middleShift) { - middlefollow += box.getMiddle() - - middlefollow + middleShift; - } - } - } - - if (lineFollow - lineLead > middlefollow) { - middlefollow = lineFollow - lineLead; - } - - breakpoints.add(insertIndex, - new LineBreakPosition(this, - knuthParagraphs.indexOf(par), - lastElementIndex , - ratio, 0, indent, - lineLead + middlefollow, - lineLead)); - }*/ - - /** * Phase 2 of Knuth algorithm: find optimal break points. * @param alignment alignment in BP direction of the paragraph @@ -902,10 +749,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager * @return a list of Knuth elements representing broken lines */ private List createLineBreaks(int alignment, LayoutContext context) { - // find the optimal line breaking points for each paragraph - ListIterator paragraphsIterator - = knuthParagraphs.listIterator(knuthParagraphs.size()); + ListIterator paragraphsIterator = knuthParagraphs.listIterator(knuthParagraphs.size()); lineLayoutsList = new ArrayList(knuthParagraphs.size()); LineLayoutPossibilities llPoss; while (paragraphsIterator.hasPrevious()) { @@ -947,7 +792,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager this); if (hyphenationProperties.hyphenate.getEnum() == EN_TRUE - && fobj.getWrapOption() != EN_NO_WRAP) { + && fobj.getWrapOption() != EN_NO_WRAP && !hyphenationPerformed) { + hyphenationPerformed = true; findHyphenationPoints(currPar); } @@ -958,7 +804,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager } else { allowedBreaks = BreakingAlgorithm.NO_FLAGGED_PENALTIES; } - alg.setConstantLineWidth(iLineWidth); + alg.setConstantLineWidth(ipd); iBPcount = alg.findBreakingPoints(currPar, maxAdjustment, false, allowedBreaks); if (iBPcount == 0 || alignment == EN_JUSTIFY) { @@ -1014,26 +860,26 @@ public class LineLayoutManager extends InlineStackingLayoutManager alg.resetAlgorithm(); lineLayouts.savePossibilities(true); // try with shorter lines - int savedLineWidth = iLineWidth; - iLineWidth = (int) (iLineWidth * 0.95); + int savedLineWidth = ipd; + ipd = (int) (ipd * 0.95); iBPcount = alg.findBreakingPoints(currPar, - maxAdjustment, true, allowedBreaks); + maxAdjustment, true, allowedBreaks); // use normal lines, when possible lineLayouts.restorePossibilities(); - iLineWidth = savedLineWidth; + ipd = savedLineWidth; } if (!lineLayouts.canUseLessLines()) { alg.resetAlgorithm(); lineLayouts.savePossibilities(true); // try with longer lines - int savedLineWidth = iLineWidth; - iLineWidth = (int) (iLineWidth * 1.05); - alg.setConstantLineWidth(iLineWidth); + int savedLineWidth = ipd; + ipd = (int) (ipd * 1.05); + alg.setConstantLineWidth(ipd); iBPcount = alg.findBreakingPoints(currPar, maxAdjustment, true, allowedBreaks); // use normal lines, when possible lineLayouts.restorePossibilities(); - iLineWidth = savedLineWidth; + ipd = savedLineWidth; } //log.debug("LLM.getNextKnuthElements> now, layouts with more lines? " + lineLayouts.canUseMoreLines()); //log.debug(" now, layouts with fewer lines? " + lineLayouts.canUseLessLines()); @@ -1052,6 +898,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager List returnList = new LinkedList(); + int endIndex = -1; for (int p = 0; p < knuthParagraphs.size(); p++) { // penalty between paragraphs if (p > 0) { @@ -1089,7 +936,6 @@ public class LineLayoutManager extends InlineStackingLayoutManager } else { /* "normal" vertical alignment: create a sequence whose boxes represent effective lines, and contain LineBreakPositions */ - Position returnPosition = new LeafPosition(this, p); int startIndex = 0; for (int i = 0; i < llPoss.getChosenLineCount(); @@ -1101,13 +947,12 @@ public class LineLayoutManager extends InlineStackingLayoutManager // penalty allowing a page break between lines Keep keep = getKeepTogether(); returnList.add(new BreakElement( - new Position(this), + new LeafPosition(this, p, endIndex), keep.getPenalty(), keep.getContext(), context)); } - int endIndex - = ((LineBreakPosition) llPoss.getChosenPosition(i)).getLeafPos(); + endIndex = ((LineBreakPosition) llPoss.getChosenPosition(i)).getLeafPos(); // create a list of the FootnoteBodyLM handling footnotes // whose citations are in this line List footnoteList = new LinkedList(); @@ -1115,15 +960,14 @@ public class LineLayoutManager extends InlineStackingLayoutManager while (elementIterator.nextIndex() <= endIndex) { KnuthElement element = (KnuthElement) elementIterator.next(); if (element instanceof KnuthInlineBox - && ((KnuthInlineBox) element).isAnchor()) { + && ((KnuthInlineBox) element).isAnchor()) { footnoteList.add(((KnuthInlineBox) element).getFootnoteBodyLM()); } else if (element instanceof KnuthBlockBox) { footnoteList.addAll(((KnuthBlockBox) element).getFootnoteBodyLMs()); } } startIndex = endIndex + 1; - LineBreakPosition lbp - = (LineBreakPosition) llPoss.getChosenPosition(i); + LineBreakPosition lbp = (LineBreakPosition) llPoss.getChosenPosition(i); returnList.add(new KnuthBlockBox (lbp.lineHeight + lbp.spaceBefore + lbp.spaceAfter, footnoteList, lbp, false)); @@ -1597,7 +1441,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager Position pos = (Position) parentIter.next(); boolean isLastPosition = !parentIter.hasNext(); if (pos instanceof LineBreakPosition) { - addInlineArea(context, pos, isLastPosition); + addInlineArea(context, (LineBreakPosition) pos, isLastPosition); } else if ((pos instanceof NonLeafPosition) && pos.generatesAreas()) { addBlockArea(context, pos, isLastPosition); } else { @@ -1617,147 +1461,129 @@ public class LineLayoutManager extends InlineStackingLayoutManager * @param pos the position for which the line is generated * @param isLastPosition true if this is the last position of this LM */ - private void addInlineArea(LayoutContext context, Position pos, boolean isLastPosition) { - ListIterator seqIterator = null; - KnuthElement tempElement = null; - // the TLM which created the last KnuthElement in this line - LayoutManager lastLM = null; - - LineBreakPosition lbp = (LineBreakPosition) pos; - int iCurrParIndex; - iCurrParIndex = lbp.iParIndex; - KnuthSequence seq = (KnuthSequence) knuthParagraphs.get(iCurrParIndex); - int iStartElement = lbp.iStartIndex; - int iEndElement = lbp.getLeafPos(); - - LineArea lineArea - = new LineArea((lbp.getLeafPos() < seq.size() - 1 - ? textAlignment : textAlignmentLast), - lbp.difference, lbp.availableStretch, lbp.availableShrink); - if (lbp.startIndent != 0) { - lineArea.addTrait(Trait.START_INDENT, new Integer(lbp.startIndent)); - } - lineArea.setBPD(lbp.lineHeight); - lineArea.setIPD(lbp.lineWidth); - lineArea.addTrait(Trait.SPACE_BEFORE, new Integer(lbp.spaceBefore)); - lineArea.addTrait(Trait.SPACE_AFTER, new Integer(lbp.spaceAfter)); - alignmentContext.resizeLine(lbp.lineHeight, lbp.baseline); - - if (seq instanceof Paragraph) { - Paragraph currPar = (Paragraph) seq; - // ignore the first elements added by the LineLayoutManager - iStartElement += (iStartElement == 0) ? currPar.ignoreAtStart : 0; - - // if this is the last line area that for this paragraph, - // ignore the last elements added by the LineLayoutManager and - // subtract the last-line-end-indent from the area ipd - if (iEndElement == (currPar.size() - 1)) { - iEndElement -= currPar.ignoreAtEnd; - lineArea.setIPD(lineArea.getIPD() - lastLineEndIndent.getValue(this)); - } + private void addInlineArea(LayoutContext context, LineBreakPosition lbp, + boolean isLastPosition) { + // the TLM which created the last KnuthElement in this line + LayoutManager lastLM = null; + + KnuthSequence seq = (KnuthSequence) knuthParagraphs.get(lbp.parIndex); + int startElementIndex = lbp.startIndex; + int endElementIndex = lbp.getLeafPos(); + + LineArea lineArea = new LineArea( + (lbp.getLeafPos() < seq.size() - 1 ? textAlignment : textAlignmentLast), + lbp.difference, lbp.availableStretch, lbp.availableShrink); + if (lbp.startIndent != 0) { + lineArea.addTrait(Trait.START_INDENT, new Integer(lbp.startIndent)); + } + lineArea.setBPD(lbp.lineHeight); + lineArea.setIPD(lbp.lineWidth); + lineArea.addTrait(Trait.SPACE_BEFORE, new Integer(lbp.spaceBefore)); + lineArea.addTrait(Trait.SPACE_AFTER, new Integer(lbp.spaceAfter)); + alignmentContext.resizeLine(lbp.lineHeight, lbp.baseline); + + if (seq instanceof Paragraph) { + Paragraph currPar = (Paragraph) seq; + // ignore the first elements added by the LineLayoutManager + startElementIndex += (startElementIndex == 0) ? currPar.ignoreAtStart : 0; + + // if this is the last line area that for this paragraph, + // ignore the last elements added by the LineLayoutManager and + // subtract the last-line-end-indent from the area ipd + if (endElementIndex == (currPar.size() - 1)) { + endElementIndex -= currPar.ignoreAtEnd; + lineArea.setIPD(lineArea.getIPD() - lastLineEndIndent.getValue(this)); } + } - // Remove trailing spaces if allowed so - if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED + // Remove trailing spaces if allowed so + if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED || whiteSpaceTreament == EN_IGNORE || whiteSpaceTreament == EN_IGNORE_IF_BEFORE_LINEFEED) { - // ignore the last element in the line if it is a KnuthGlue object - seqIterator = seq.listIterator(iEndElement); - tempElement = (KnuthElement) seqIterator.next(); - if (tempElement.isGlue()) { - iEndElement--; - // this returns the same KnuthElement - seqIterator.previous(); - if (seqIterator.hasPrevious()) { - tempElement = (KnuthElement) seqIterator.previous(); - } else { - tempElement = null; - } - } - if (tempElement != null) { - lastLM = tempElement.getLayoutManager(); + // ignore the last element in the line if it is a KnuthGlue object + ListIterator seqIterator = seq.listIterator(endElementIndex); + KnuthElement lastElement = (KnuthElement) seqIterator.next(); + lastLM = lastElement.getLayoutManager(); + if (lastElement.isGlue()) { + endElementIndex--; + // this returns the same KnuthElement + seqIterator.previous(); + if (seqIterator.hasPrevious()) { + lastLM = ((KnuthElement) seqIterator.previous()).getLayoutManager(); } } + } - // Remove leading spaces if allowed so - if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED + // Remove leading spaces if allowed so + if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED || whiteSpaceTreament == EN_IGNORE || whiteSpaceTreament == EN_IGNORE_IF_AFTER_LINEFEED) { - // ignore KnuthGlue and KnuthPenalty objects - // at the beginning of the line - seqIterator = seq.listIterator(iStartElement); - tempElement = (KnuthElement) seqIterator.next(); - while (!tempElement.isBox() && seqIterator.hasNext()) { - tempElement = (KnuthElement) seqIterator.next(); - iStartElement++; - } - } - // Add the inline areas to lineArea - PositionIterator inlinePosIter - = new KnuthPossPosIter(seq, iStartElement, iEndElement + 1); - - iStartElement = lbp.getLeafPos() + 1; - if (iStartElement == seq.size()) { - // advance to next paragraph - iStartElement = 0; + // ignore KnuthGlue and KnuthPenalty objects + // at the beginning of the line + ListIterator seqIterator = seq.listIterator(startElementIndex); + while (seqIterator.hasNext() && !((KnuthElement) seqIterator.next()).isBox()) { + startElementIndex++; } + } + // Add the inline areas to lineArea + PositionIterator inlinePosIter = new KnuthPossPosIter(seq, startElementIndex, + endElementIndex + 1); - LayoutContext lc = new LayoutContext(0); - lc.setAlignmentContext(alignmentContext); - lc.setSpaceAdjust(lbp.dAdjust); - lc.setIPDAdjust(lbp.ipdAdjust); - lc.setLeadingSpace(new SpaceSpecifier(true)); - lc.setTrailingSpace(new SpaceSpecifier(false)); - lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true); - - /* - * extension (not in the XSL FO recommendation): if the left and right margins - * have been optimized, recompute indents and / or adjust ratio, according - * to the paragraph horizontal alignment - */ - if (false && textAlignment == EN_JUSTIFY) { - // re-compute space adjust ratio - int updatedDifference = context.getStackLimitIP().opt - - lbp.lineWidth + lbp.difference; - double updatedRatio = 0.0; - if (updatedDifference > 0) { - updatedRatio = (float) updatedDifference / lbp.availableStretch; - } else if (updatedDifference < 0) { - updatedRatio = (float) updatedDifference / lbp.availableShrink; - } - lc.setIPDAdjust(updatedRatio); - //log.debug("LLM.addAreas> old difference = " + lbp.difference + " new difference = " + updatedDifference); - //log.debug(" old ratio = " + lbp.ipdAdjust + " new ratio = " + updatedRatio); - } else if (false && textAlignment == EN_CENTER) { - // re-compute indent - int updatedIndent = lbp.startIndent - + (context.getStackLimitIP().opt - lbp.lineWidth) / 2; - lineArea.addTrait(Trait.START_INDENT, new Integer(updatedIndent)); - } else if (false && textAlignment == EN_END) { - // re-compute indent - int updatedIndent = lbp.startIndent - + (context.getStackLimitIP().opt - lbp.lineWidth); - lineArea.addTrait(Trait.START_INDENT, new Integer(updatedIndent)); - } + LayoutContext lc = new LayoutContext(0); + lc.setAlignmentContext(alignmentContext); + lc.setSpaceAdjust(lbp.dAdjust); + lc.setIPDAdjust(lbp.ipdAdjust); + lc.setLeadingSpace(new SpaceSpecifier(true)); + lc.setTrailingSpace(new SpaceSpecifier(false)); + lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true); - setCurrentArea(lineArea); - setChildContext(lc); - LayoutManager childLM; - while ((childLM = inlinePosIter.getNextChildLM()) != null) { - lc.setFlags(LayoutContext.LAST_AREA, (childLM == lastLM)); - childLM.addAreas(inlinePosIter, lc); - lc.setLeadingSpace(lc.getTrailingSpace()); - lc.setTrailingSpace(new SpaceSpecifier(false)); + /* + * extension (not in the XSL FO recommendation): if the left and right margins + * have been optimized, recompute indents and / or adjust ratio, according + * to the paragraph horizontal alignment + */ + if (false && textAlignment == EN_JUSTIFY) { + // re-compute space adjust ratio + int updatedDifference = context.getRefIPD() + - lbp.lineWidth + lbp.difference; + double updatedRatio = 0.0; + if (updatedDifference > 0) { + updatedRatio = (float) updatedDifference / lbp.availableStretch; + } else if (updatedDifference < 0) { + updatedRatio = (float) updatedDifference / lbp.availableShrink; } + lc.setIPDAdjust(updatedRatio); + //log.debug("LLM.addAreas> old difference = " + lbp.difference + " new difference = " + updatedDifference); + //log.debug(" old ratio = " + lbp.ipdAdjust + " new ratio = " + updatedRatio); + } else if (false && textAlignment == EN_CENTER) { + // re-compute indent + int updatedIndent = lbp.startIndent + + (context.getRefIPD() - lbp.lineWidth) / 2; + lineArea.addTrait(Trait.START_INDENT, new Integer(updatedIndent)); + } else if (false && textAlignment == EN_END) { + // re-compute indent + int updatedIndent = lbp.startIndent + + (context.getRefIPD() - lbp.lineWidth); + lineArea.addTrait(Trait.START_INDENT, new Integer(updatedIndent)); + } - // when can this be null? - // if display-align is distribute, add space after - if (context.getSpaceAfter() > 0 - && (!context.isLastArea() || !isLastPosition)) { - lineArea.setBPD(lineArea.getBPD() + context.getSpaceAfter()); - } - lineArea.finalise(); - parentLM.addChildArea(lineArea); + setCurrentArea(lineArea); + setChildContext(lc); + LayoutManager childLM; + while ((childLM = inlinePosIter.getNextChildLM()) != null) { + lc.setFlags(LayoutContext.LAST_AREA, (childLM == lastLM)); + childLM.addAreas(inlinePosIter, lc); + lc.setLeadingSpace(lc.getTrailingSpace()); + lc.setTrailingSpace(new SpaceSpecifier(false)); + } + + // if display-align is distribute, add space after + if (context.getSpaceAfter() > 0 + && (!context.isLastArea() || !isLastPosition)) { + lineArea.setBPD(lineArea.getBPD() + context.getSpaceAfter()); + } + lineArea.finalise(); + parentLM.addChildArea(lineArea); } /** @@ -1800,7 +1626,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager // set last area flag blocklc.setFlags(LayoutContext.LAST_AREA, (context.isLastArea() && childLM == lastLM)); - blocklc.setStackLimitsFrom(context); + blocklc.setStackLimitBP(context.getStackLimitBP()); // Add the line areas to Area childLM.addAreas(childPosIter, blocklc); blocklc.setLeadingSpace(blocklc.getTrailingSpace()); @@ -1841,5 +1667,11 @@ public class LineLayoutManager extends InlineStackingLayoutManager public boolean getGeneratesLineArea() { return true; } + + /** {@inheritDoc} */ + public boolean isRestartable() { + return true; + } + } diff --git a/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java b/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java index 7000a8748..fb88bb79d 100644 --- a/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/list/ListItemLayoutManager.java @@ -702,6 +702,13 @@ public class ListItemLayoutManager extends BlockStackingLayoutManager } } + /** {@inheritDoc} */ + public void reset() { + super.reset(); + label.reset(); + body.reset(); + } + } diff --git a/src/java/org/apache/fop/render/pdf/PDFEventProducer.xml b/src/java/org/apache/fop/render/pdf/PDFEventProducer.xml index 420f16a09..fd57d5099 100644 --- a/src/java/org/apache/fop/render/pdf/PDFEventProducer.xml +++ b/src/java/org/apache/fop/render/pdf/PDFEventProducer.xml @@ -1,3 +1,4 @@ - + + {count} link target{count,equals,1,,s} could not be fully resolved and now point{count,equals,1,,s} to the top of the page or {count,equals,1,is,are} dysfunctional. diff --git a/test/layoutengine/standard-testcases/flow_changing-ipd_1.xml b/test/layoutengine/standard-testcases/flow_changing-ipd_1.xml new file mode 100644 index 000000000..d4db6e696 --- /dev/null +++ b/test/layoutengine/standard-testcases/flow_changing-ipd_1.xml @@ -0,0 +1,97 @@ + + + + + +

+ This test checks that blocks of texts are re-laid out after a change of the flow ipd. +

+
+ + + + + + + + + + + + + + + + + + In olden times when wishing still helped one, + there lived a king whose daughters were all beautiful, but the youngest was so + beautiful that the sun itself, which has seen so much, was astonished whenever it + shone in her face. + In olden times when wishing still helped one, + there lived a king whose daughters were all beautiful, but the youngest was so + beautiful that the sun itself, which has seen so much, was astonished whenever it + shone in her face. + In olden times when wishing still helped + one, there lived a king whose daughters were all beautiful, but the youngest was so + beautiful that the sun itself, which has seen so much, was astonished whenever it + shone in her face. In olden times when wishing still helped one, there lived a king + whose daughters were all beautiful, but the youngest was so beautiful that the sun + itself, which has seen so much, was astonished whenever it shone in her + face. + In olden times + when wishing still helped one, there lived a king whose daughters were all beautiful, + but the youngest was so beautiful that the sun itself, which has seen so much, was + astonished whenever it shone in her face. + In olden times when wishing still helped one, + there lived a king whose daughters were all beautiful, but the youngest was so + beautiful that the sun itself, which has seen so much, was astonished whenever it + shone in her face. + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/test/layoutengine/standard-testcases/flow_changing-ipd_2.xml b/test/layoutengine/standard-testcases/flow_changing-ipd_2.xml new file mode 100644 index 000000000..dbe002e43 --- /dev/null +++ b/test/layoutengine/standard-testcases/flow_changing-ipd_2.xml @@ -0,0 +1,75 @@ + + + + + +

+ This test checks that blocks of texts are re-laid out after a change of the flow ipd. +

+
+ + + + + + + + + + + + + + + + + In olden times when wishing still helped one, there lived a king whose + daughters were all beautiful, but the youngest was so beautiful that the sun itself, + which has seen so much, was astonished whenever it shone in her face. + In olden times when wishing still helped one, there lived a king whose + daughters were all beautiful, but the youngest was so beautiful that the sun itself, + which has seen so much, was astonished whenever it shone in her face. + In olden times when wishing still helped one, there lived a king whose + daughters were all beautiful, but the youngest was so beautiful that the sun itself, + which has seen so much, was astonished whenever it shone in her face. In olden times + when wishing still helped one, there lived a king whose daughters were all beautiful, + but the youngest was so beautiful that the sun itself, which has seen so much, was + astonished whenever it shone in her face. + In olden times when wishing still helped one, there lived a king whose + daughters were all beautiful, but the youngest was so beautiful that the sun itself, + which has seen so much, was astonished whenever it shone in her face. + In olden times when wishing still helped one, there lived a king whose + daughters were all beautiful, but the youngest was so beautiful that the sun itself, + which has seen so much, was astonished whenever it shone in her face. + + + + + + + + + + + + + + +
diff --git a/test/layoutengine/standard-testcases/flow_changing-ipd_3.xml b/test/layoutengine/standard-testcases/flow_changing-ipd_3.xml new file mode 100644 index 000000000..2badfe71e --- /dev/null +++ b/test/layoutengine/standard-testcases/flow_changing-ipd_3.xml @@ -0,0 +1,86 @@ + + + + + +

+ This test checks that a change of IPD between two blocks is correctly handled. +

+
+ + + + + + + + + + + + + + + + + + In olden times when wishing still helped one, + there lived a king whose daughters were all beautiful, but the youngest was so + beautiful that the sun itself, which has seen so much, was astonished whenever it + shone in her face. + In olden times when wishing still helped one, + there lived a king whose daughters were all beautiful, but the youngest was so + beautiful that the sun itself, which has seen so much, was astonished whenever it + shone in her face. + In olden times when wishing still helped one, + there lived a king whose daughters were all beautiful, but the youngest was so + beautiful that the sun itself, which has seen so much, was astonished whenever it + shone in her face. + In olden times when wishing still helped + one, there lived a king whose daughters were all beautiful, but the youngest was so + beautiful that the sun itself, which has seen so much, was astonished whenever it + shone in her face. + In olden times when wishing still helped one, + there lived a king whose daughters were all beautiful, but the youngest was so + beautiful that the sun itself, which has seen so much, was astonished whenever it + shone in her face. + + + + + + + + + + + + + + + + +
diff --git a/test/layoutengine/standard-testcases/flow_changing-ipd_4.xml b/test/layoutengine/standard-testcases/flow_changing-ipd_4.xml new file mode 100644 index 000000000..fe166d05c --- /dev/null +++ b/test/layoutengine/standard-testcases/flow_changing-ipd_4.xml @@ -0,0 +1,159 @@ + + + + + +

+ This test checks that non-restartable elements still show up at IPD change, even if not + re-laid out. +

+
+ + + + + + + + + + + + + + + + + In olden times when wishing still helped one, there + lived a king whose daughters were all beautiful, but the youngest was so beautiful that + the sun itself, which has seen so much, was astonished whenever it shone in her + face. + In olden times when wishing still helped one, there + lived a king whose daughters were all beautiful, but the youngest was so beautiful that + the sun itself, which has seen so much, was astonished whenever it shone in her + face. + + + + + In olden times when wishing still helped + one, there lived a king whose daughters were all beautiful, but the youngest was + so beautiful that the sun itself, which has seen so much, was astonished + whenever it shone in her face. + In olden times when wishing still helped + one, there lived a king whose daughters were all beautiful, but the youngest was + so beautiful that the sun itself, which has seen so much, was astonished + whenever it shone in her face. + + + + + In olden times when + wishing still helped one, there lived a king whose daughters were all beautiful, but the + youngest was so beautiful that the sun itself, which has seen so much, was astonished + whenever it shone in her face. + In olden times when wishing still helped one, there + lived a king whose daughters were all beautiful, but the youngest was so beautiful that + the sun itself, which has seen so much, was astonished whenever it shone in her + face. + + + + + In olden times when wishing still helped one, there + lived a king whose daughters were all beautiful, but the youngest was so beautiful that + the sun itself, which has seen so much, was astonished whenever it shone in her + face. + In olden times when wishing still helped one, there + lived a king whose daughters were all beautiful, but the youngest was so beautiful that + the sun itself, which has seen so much, was astonished whenever it shone in her + face. + + + + + + + In olden times when wishing still helped + one, there lived a king whose daughters were all beautiful, but the youngest was + so beautiful that the sun itself, which has seen so much, was + astonished… + + + + + + + + In olden times when wishing still helped one, there lived a king + whose daughters were all beautiful, but the youngest was so beautiful that the sun + itself, which has seen so much, was astonished… + + + + In olden times when wishing still helped one, there lived a king whose + daughters were all beautiful, but the youngest was so beautiful that the sun itself, + which has seen so much, was astonished whenever it shone in her face. + In olden times when wishing still helped one, there + lived a king whose daughters were all beautiful, but the youngest was so beautiful that + the sun itself, which has seen so much, was astonished whenever it shone in her + face. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/test/layoutengine/standard-testcases/flow_changing-ipd_block-container_1.xml b/test/layoutengine/standard-testcases/flow_changing-ipd_block-container_1.xml new file mode 100644 index 000000000..82b757d46 --- /dev/null +++ b/test/layoutengine/standard-testcases/flow_changing-ipd_block-container_1.xml @@ -0,0 +1,101 @@ + + + + + +

+ This test checks that block-container elements correctly support an IPD change. +

+ +
+ + + + + + + + + + + + + + + + + + In olden times when wishing still helped one, + there lived a king whose daughters were all beautiful, but the youngest was so + beautiful that the sun itself, which has seen so much, was astonished whenever it + shone in her face. + In olden times when wishing still helped one, + there lived a king whose daughters were all beautiful, but the youngest was so + beautiful that the sun itself, which has seen so much, was astonished whenever it + shone in her face. + + In olden times when wishing still helped + one, there lived a king whose daughters were all beautiful, but the youngest was so + beautiful that the sun itself, which has seen so much, was astonished whenever it + shone in her face. In olden times when wishing still helped one, there lived a king + whose daughters were all beautiful, but the youngest was so beautiful that the sun + itself, which has seen so much, was astonished whenever it shone in her + face. + + In olden times + when wishing still helped one, there lived a king whose daughters were all beautiful, + but the youngest was so beautiful that the sun itself, which has seen so much, was + astonished whenever it shone in her face. + In olden times when wishing still helped one, + there lived a king whose daughters were all beautiful, but the youngest was so + beautiful that the sun itself, which has seen so much, was astonished whenever it + shone in her face. + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/test/layoutengine/standard-testcases/flow_changing-ipd_block-container_2.xml b/test/layoutengine/standard-testcases/flow_changing-ipd_block-container_2.xml new file mode 100644 index 000000000..7a2bb9b19 --- /dev/null +++ b/test/layoutengine/standard-testcases/flow_changing-ipd_block-container_2.xml @@ -0,0 +1,89 @@ + + + + + +

+ This test checks that a change of IPD between two blocks surrounded by a block-container is + correctly handled. +

+ +
+ + + + + + + + + + + + + + + + + + In olden times when wishing still helped one, + there lived a king whose daughters were all beautiful, but the youngest was so + beautiful that the sun itself, which has seen so much, was astonished whenever it + shone in her face. + In olden times when wishing still helped one, + there lived a king whose daughters were all beautiful, but the youngest was so + beautiful that the sun itself, which has seen so much, was astonished whenever it + shone in her face. + In olden times when wishing still helped one, + there lived a king whose daughters were all beautiful, but the youngest was so + beautiful that the sun itself, which has seen so much, was astonished whenever it + shone in her face. + In olden times when wishing still helped + one, there lived a king whose daughters were all beautiful, but the youngest was so + beautiful that the sun itself, which has seen so much, was astonished whenever it + shone in her face. + In olden times when wishing still helped one, + there lived a king whose daughters were all beautiful, but the youngest was so + beautiful that the sun itself, which has seen so much, was astonished whenever it + shone in her face. + + + + + + + + + + + + + + + + +
-- cgit v1.2.3