diff options
author | Simon Steiner <ssteiner@apache.org> | 2016-01-06 12:13:52 +0000 |
---|---|---|
committer | Simon Steiner <ssteiner@apache.org> | 2016-01-06 12:13:52 +0000 |
commit | 51f22319512e0a50b6f2c0e63da01b79a392275c (patch) | |
tree | 401b2816e43bd2d610321c93096a57c55045b588 /src | |
parent | d88b17f110e6d3e9683c316d460ae5e6e2ed4f1b (diff) | |
download | xmlgraphics-fop-51f22319512e0a50b6f2c0e63da01b79a392275c.tar.gz xmlgraphics-fop-51f22319512e0a50b6f2c0e63da01b79a392275c.zip |
FOP-2335: Content is missing after IPD change
git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@1723297 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src')
18 files changed, 408 insertions, 115 deletions
diff --git a/src/java/org/apache/fop/area/BodyRegion.java b/src/java/org/apache/fop/area/BodyRegion.java index 89bb206f2..df7e914b7 100644 --- a/src/java/org/apache/fop/area/BodyRegion.java +++ b/src/java/org/apache/fop/area/BodyRegion.java @@ -81,6 +81,15 @@ public class BodyRegion extends RegionReference { return this.columnGap; } + int getContentIPD() { + RegionViewport rv = getRegionViewport(); + return getIPD() - rv.getBorderAndPaddingWidthStart() - rv.getBorderAndPaddingWidthEnd(); + } + + public int getColumnIPD() { + return (getContentIPD() - (columnCount - 1) * columnGap) / columnCount; + } + /** * Get the main reference area. * diff --git a/src/java/org/apache/fop/area/MainReference.java b/src/java/org/apache/fop/area/MainReference.java index efc16515d..9778db87f 100644 --- a/src/java/org/apache/fop/area/MainReference.java +++ b/src/java/org/apache/fop/area/MainReference.java @@ -59,12 +59,8 @@ public class MainReference extends Area { //Remove the current one if it is empty spanAreas.remove(spanAreas.size() - 1); } - RegionViewport rv = parent.getRegionViewport(); - int ipdWidth = parent.getIPD() - - rv.getBorderAndPaddingWidthStart() - rv.getBorderAndPaddingWidthEnd(); - Span newSpan = new Span(((spanAll) ? 1 : getColumnCount()), - getColumnGap(), ipdWidth); + getColumnGap(), parent.getContentIPD()); spanAreas.add(newSpan); return getCurrentSpan(); } diff --git a/src/java/org/apache/fop/fo/pagination/PageSequence.java b/src/java/org/apache/fop/fo/pagination/PageSequence.java index 6afd15e81..a1a966e90 100644 --- a/src/java/org/apache/fop/fo/pagination/PageSequence.java +++ b/src/java/org/apache/fop/fo/pagination/PageSequence.java @@ -454,4 +454,14 @@ public class PageSequence extends AbstractPageSequence implements WritingModeTra this.flowMap.clear(); } + public SimplePageMaster getLastSimplePageMaster(int page, boolean isFirstPage, boolean isBlank) { + boolean isOddPage = ((page % 2) != 0); // please findbugs... + log.debug("getNextSimplePageMaster(page=" + page + " isOdd=" + isOddPage + " isFirst=" + + isFirstPage + " isLast=true" + " isBlank=" + isBlank + ")"); + if (pageSequenceMaster == null) { + return simplePageMaster; + } + return pageSequenceMaster.getLastSimplePageMaster(isOddPage, isFirstPage, isBlank, getMainFlow() + .getFlowName()); + } } diff --git a/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java b/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java index 86d5ff663..f218e43b4 100644 --- a/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java +++ b/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java @@ -254,6 +254,24 @@ public class PageSequenceMaster extends FObj { return FO_PAGE_SEQUENCE_MASTER; } + public SimplePageMaster getLastSimplePageMaster(boolean isOddPage, boolean isFirstPage, boolean isBlank, + String flowName) { + if (currentSubSequence == null) { + currentSubSequence = getNextSubSequence(); + if (currentSubSequence == null) { + blockLevelEventProducer.missingSubsequencesInPageSequenceMaster(this, masterName, + getLocator()); + } + if (currentSubSequence.isInfinite() && !currentSubSequence.canProcess(flowName)) { + throw new PageProductionException( + "The current sub-sequence will not terminate whilst processing the main flow"); + } + } + + SimplePageMaster pageMaster = currentSubSequence.getLastPageMaster(isOddPage, isFirstPage, isBlank, + blockLevelEventProducer); + return pageMaster; + } } diff --git a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java index dc69c600d..2914fb9a8 100644 --- a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java +++ b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java @@ -31,6 +31,7 @@ import org.apache.fop.fo.FObj; import org.apache.fop.fo.PropertyList; import org.apache.fop.fo.ValidationException; import org.apache.fop.fo.properties.Property; +import org.apache.fop.layoutmgr.BlockLevelEventProducer; /** * Class modelling the <a href="http://www.w3.org/TR/xsl/#fo_repeatable-page-master-alternatives"> @@ -136,6 +137,22 @@ public class RepeatablePageMasterAlternatives extends FObj return null; } + public SimplePageMaster getLastPageMaster(boolean isOddPage, boolean isFirstPage, boolean isBlankPage, + BlockLevelEventProducer blockLevelEventProducer) { + for (ConditionalPageMasterReference cpmr : conditionalPageMasterRefs) { + if (cpmr.isValid(isOddPage, isFirstPage, true, isBlankPage)) { + return cpmr.getMaster(); + } + } + blockLevelEventProducer.lastPageMasterReferenceMissing(this, getLocator()); + for (ConditionalPageMasterReference cpmr : conditionalPageMasterRefs) { + if (cpmr.isValid(isOddPage, isFirstPage, false, isBlankPage)) { + return cpmr.getMaster(); + } + } + throw new PageProductionException("Last page master not found: oddpage=" + isOddPage + + " firstpage=" + isFirstPage + " blankpage=" + isBlankPage); + } /** * Adds a new conditional page master reference. diff --git a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java index f6d41ce8b..5e43c02b1 100644 --- a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java +++ b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java @@ -101,6 +101,11 @@ public class RepeatablePageMasterReference extends FObj return master; } + public SimplePageMaster getLastPageMaster(boolean isOddPage, boolean isFirstPage, boolean isEmptyPage, + BlockLevelEventProducer blockLevelEventProducer) { + return getNextPageMaster(isOddPage, isFirstPage, true, isEmptyPage); + } + /** * Get the value of the <code>maximum-repeats</code> property. * @return the "maximum-repeats" property diff --git a/src/java/org/apache/fop/fo/pagination/Root.java b/src/java/org/apache/fop/fo/pagination/Root.java index cb433a064..51309a65d 100644 --- a/src/java/org/apache/fop/fo/pagination/Root.java +++ b/src/java/org/apache/fop/fo/pagination/Root.java @@ -75,6 +75,16 @@ public class Root extends FObj implements CommonAccessibilityHolder { */ private FOEventHandler foEventHandler; + private PageSequence lastSeq; + + public void setLastSeq(PageSequence seq) { + lastSeq = seq; + } + + public PageSequence getLastSeq() { + return lastSeq; + } + /** * Base constructor * diff --git a/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java b/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java index ed0c041dd..2600909cb 100644 --- a/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java +++ b/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java @@ -100,6 +100,11 @@ public class SinglePageMasterReference extends FObj } } + public SimplePageMaster getLastPageMaster(boolean isOddPage, boolean isFirstPage, boolean isBlankPage, + BlockLevelEventProducer blockLevelEventProducer) { + return getNextPageMaster(isOddPage, isFirstPage, true, isBlankPage); + } + /** {@inheritDoc} */ public void reset() { this.state = FIRST; diff --git a/src/java/org/apache/fop/fo/pagination/SubSequenceSpecifier.java b/src/java/org/apache/fop/fo/pagination/SubSequenceSpecifier.java index 271d80a95..0905ee8a8 100644 --- a/src/java/org/apache/fop/fo/pagination/SubSequenceSpecifier.java +++ b/src/java/org/apache/fop/fo/pagination/SubSequenceSpecifier.java @@ -20,6 +20,7 @@ package org.apache.fop.fo.pagination; import org.apache.fop.fo.ValidationException; +import org.apache.fop.layoutmgr.BlockLevelEventProducer; /** * Classes that implement this interface can be added to a {@link PageSequenceMaster}, @@ -43,6 +44,10 @@ public interface SubSequenceSpecifier { boolean isBlankPage) throws PageProductionException; + SimplePageMaster getLastPageMaster(boolean isOddPage, boolean isFirstPage, boolean isBlankPage, + BlockLevelEventProducer blockLevelEventProducer) + throws PageProductionException; + /** * Called before a new page sequence is rendered so subsequences can reset * any state they keep during the formatting process. diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java index e1c6b3a74..d0594ce8a 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java @@ -42,6 +42,10 @@ public abstract class AbstractBreaker { /** logging instance */ protected static final Log log = LogFactory.getLog(AbstractBreaker.class); + private LayoutManager originalRestartAtLM; + private Position positionAtBreak; + private List firstElementsForRestart; + /** * A page break position. */ @@ -408,17 +412,36 @@ public abstract class AbstractBreaker { alg.setConstantLineWidth(flowBPD); int optimalPageCount = alg.findBreakingPoints(blockList, 1, true, BreakingAlgorithm.ALL_BREAKS); - + boolean ipdChangesOnNextPage = (alg.getIPDdifference() != 0); + boolean onLastPageAndIPDChanges = false; + if (!ipdChangesOnNextPage) { + onLastPageAndIPDChanges = (lastPageHasIPDChange() && !thereIsANonRestartableLM(alg) + && (shouldRedoLayout() || (wasLayoutRedone() && optimalPageCount > 1))); + } if (alg.handlingFloat()) { nextSequenceStartsOn = handleFloatLayout(alg, optimalPageCount, blockList, childLC); - } else if (Math.abs(alg.getIPDdifference()) > 1) { - addAreas(alg, optimalPageCount, blockList, blockList); - // *** redo Phase 1 *** - log.trace("IPD changes after page " + optimalPageCount); + } else if (ipdChangesOnNextPage || onLastPageAndIPDChanges) { + boolean visitedBefore = false; + if (onLastPageAndIPDChanges) { + visitedBefore = wasLayoutRedone(); + prepareToRedoLayout(alg, optimalPageCount, blockList, blockList); + } + + firstElementsForRestart = null; + LayoutManager restartAtLM = getRestartAtLM(alg, ipdChangesOnNextPage, onLastPageAndIPDChanges, + visitedBefore, blockList, 1); + if (restartAtLM == null) { + firstElementsForRestart = null; + restartAtLM = getRestartAtLM(alg, ipdChangesOnNextPage, onLastPageAndIPDChanges, + visitedBefore, blockList, 0); + } + if (ipdChangesOnNextPage) { + addAreas(alg, optimalPageCount, blockList, blockList); + } blockLists.clear(); - nextSequenceStartsOn = getNextBlockListChangedIPD(childLC, alg, - blockList); blockListIndex = -1; + nextSequenceStartsOn = getNextBlockList(childLC, Constants.EN_COLUMN, positionAtBreak, + restartAtLM, firstElementsForRestart); } else { log.debug("PLM> optimalPageCount= " + optimalPageCount + " pageBreaks.size()= " + alg.getPageBreaks().size()); @@ -433,6 +456,92 @@ public abstract class AbstractBreaker { blockLists = null; } + private LayoutManager getRestartAtLM(PageBreakingAlgorithm alg, boolean ipdChangesOnNextPage, + boolean onLastPageAndIPDChanges, boolean visitedBefore, + BlockSequence blockList, int start) { + KnuthNode optimalBreak = ipdChangesOnNextPage ? alg.getBestNodeBeforeIPDChange() : alg + .getBestNodeForLastPage(); + if (onLastPageAndIPDChanges && visitedBefore && this.originalRestartAtLM == null) { + optimalBreak = null; + } + + int positionIndex = (optimalBreak != null) ? optimalBreak.position : start; + KnuthElement elementAtBreak = alg.getElement(positionIndex); + if (elementAtBreak.getPosition() == null) { + elementAtBreak = alg.getElement(0); + } + positionAtBreak = elementAtBreak.getPosition(); + /* Retrieve the original position wrapped into this space position */ + positionAtBreak = positionAtBreak.getPosition(); + if (ipdChangesOnNextPage || (positionAtBreak != null && positionAtBreak.getIndex() > -1)) { + firstElementsForRestart = Collections.EMPTY_LIST; + if (ipdChangesOnNextPage) { + if (containsNonRestartableLM(positionAtBreak)) { + if (alg.getIPDdifference() > 0) { + EventBroadcaster eventBroadcaster = getCurrentChildLM().getFObj() + .getUserAgent().getEventBroadcaster(); + BlockLevelEventProducer eventProducer = BlockLevelEventProducer.Provider + .get(eventBroadcaster); + eventProducer.nonRestartableContentFlowingToNarrowerPage(this); + } + firstElementsForRestart = new LinkedList(); + boolean boxFound = false; + Iterator iter = blockList.listIterator(positionIndex + 1); + Position position = null; + while (iter.hasNext() + && (position == null || containsNonRestartableLM(position))) { + positionIndex++; + KnuthElement element = (KnuthElement) iter.next(); + position = element.getPosition(); + if (element.isBox()) { + boxFound = true; + firstElementsForRestart.add(element); + } else if (boxFound) { + firstElementsForRestart.add(element); + } + } + if (position instanceof SpaceResolver.SpaceHandlingBreakPosition) { + /* Retrieve the original position wrapped into this space position */ + positionAtBreak = position.getPosition(); + } else { + positionAtBreak = null; + } + } + } + } + LayoutManager restartAtLM = null; + if (ipdChangesOnNextPage || !(positionAtBreak != null && positionAtBreak.getIndex() > -1)) { + if (positionAtBreak != null && positionAtBreak.getIndex() == -1) { + Position position; + Iterator iter = blockList.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(); + } + if (onLastPageAndIPDChanges && restartAtLM != null) { + if (originalRestartAtLM == null) { + originalRestartAtLM = restartAtLM; + } else { + restartAtLM = originalRestartAtLM; + } + firstElementsForRestart = Collections.EMPTY_LIST; + } + } + if (onLastPageAndIPDChanges && !visitedBefore && positionAtBreak.getPosition() != null) { + restartAtLM = positionAtBreak.getPosition().getLM(); + } + return restartAtLM; + } + /** * Returns {@code true} if the given position or one of its descendants * corresponds to a non-restartable LM. @@ -709,84 +818,39 @@ public abstract class AbstractBreaker { return nextSequenceStartsOn; } - /** - * @param childLC LayoutContext to use - * @param alg the pagebreaking algorithm - * @param effectiveList the list of Knuth elements to be reused - * @return the page on which the next content should appear after a hard break - */ - private int getNextBlockListChangedIPD(LayoutContext childLC, PageBreakingAlgorithm alg, - BlockSequence effectiveList) { - int nextSequenceStartsOn; - KnuthNode optimalBreak = alg.getBestNodeBeforeIPDChange(); - int positionIndex = optimalBreak.position; - log.trace("IPD changes at index " + positionIndex); - 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<KnuthElement> firstElements = Collections.emptyList(); - if (containsNonRestartableLM(positionAtBreak)) { - if (alg.getIPDdifference() > 0) { - EventBroadcaster eventBroadcaster = getCurrentChildLM().getFObj() - .getUserAgent().getEventBroadcaster(); - BlockLevelEventProducer eventProducer - = BlockLevelEventProducer.Provider.get(eventBroadcaster); - eventProducer.nonRestartableContentFlowingToNarrowerPage(this); - } - firstElements = new LinkedList<KnuthElement>(); - boolean boxFound = false; - Iterator<KnuthElement> iter = effectiveList.listIterator(positionIndex + 1); - Position position = null; - while (iter.hasNext() - && (position == null || containsNonRestartableLM(position))) { - positionIndex++; - KnuthElement element = iter.next(); - 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(); - } else { - positionAtBreak = null; + protected boolean shouldRedoLayout() { + return false; + } + + protected void prepareToRedoLayout(PageBreakingAlgorithm alg, int partCount, + BlockSequence originalList, BlockSequence effectiveList) { + return; + } + + protected boolean wasLayoutRedone() { + return false; + } + + private boolean thereIsANonRestartableLM(PageBreakingAlgorithm alg) { + KnuthNode optimalBreak = alg.getBestNodeForLastPage(); + if (optimalBreak != null) { + int positionIndex = optimalBreak.position; + KnuthElement elementAtBreak = alg.getElement(positionIndex); + Position positionAtBreak = elementAtBreak.getPosition(); + if (!(positionAtBreak instanceof SpaceResolver.SpaceHandlingBreakPosition)) { + return false; } - } - if (positionAtBreak != null && 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<KnuthElement> iter = effectiveList.listIterator(positionIndex + 1); - do { - KnuthElement nextElement = 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(); + /* Retrieve the original position wrapped into this space position */ + positionAtBreak = positionAtBreak.getPosition(); + if (positionAtBreak != null && containsNonRestartableLM(positionAtBreak)) { + return true; } - restartAtLM = position.getPosition().getLM(); } + return false; + } - nextSequenceStartsOn = getNextBlockList(childLC, Constants.EN_COLUMN, - positionAtBreak, restartAtLM, firstElements); - return nextSequenceStartsOn; + protected boolean lastPageHasIPDChange() { + return false; } protected int handleFloatLayout(PageBreakingAlgorithm alg, int optimalPageCount, BlockSequence blockList, diff --git a/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java b/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java index 7faa0565e..8435ad093 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java @@ -383,6 +383,11 @@ public abstract class AbstractPageSequenceLayoutManager extends AbstractLayoutMa if (curPage != null) { finishPage(); } + + while (forcePageCount != Constants.EN_NO_FORCE && getCurrentPageNum() < getLastPageNumber()) { + curPage = makeNewPage(true); + finishPage(); + } } /** {@inheritDoc} */ @@ -390,4 +395,7 @@ public abstract class AbstractPageSequenceLayoutManager extends AbstractLayoutMa throw new IllegalStateException(); } + protected int getLastPageNumber() { + return currentPageNum; + } } diff --git a/src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.java b/src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.java index 6a407f266..d043456be 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.java +++ b/src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.java @@ -202,4 +202,21 @@ public interface BlockLevelEventProducer extends EventProducer { * @event.severity WARN */ void nonRestartableContentFlowingToNarrowerPage(Object source); + + /** + * A feasible layout has reached the given number of parts (columns or pages). + * + * @param source the event source + * @param partCount the number of parts that the layout has reached + * @event.severity INFO + */ + void layoutHasReachedParts(Object source, int partCount); + + /** + * Last page master reference missing. + * + * @param source the event source + * @event.severity WARN + */ + void lastPageMasterReferenceMissing(Object source, Locator loc); } diff --git a/src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.xml b/src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.xml index 6eb772db1..de040bdfe 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.xml +++ b/src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.xml @@ -31,4 +31,6 @@ <message key="missingSubsequencesInPageSequenceMaster">No subsequences in page-sequence-master "{pageSequenceMasterName}".{{locator}}</message> <message key="noMatchingPageMaster">No simple-page-master matching "{pageMasterName}" in page-sequence-master "{pageSequenceMasterName}".{{locator}}</message> <message key="nonRestartableContentFlowingToNarrowerPage">Content that cannot handle IPD changes is flowing to a narrower page. Part of it may be clipped by the page border.</message> + <message key="layoutHasReachedParts">A layout has reached {partCount} part(s).</message> + <message key="lastPageMasterReferenceMissing">page-position="last" master reference missing.{{locator}}</message> </catalogue> diff --git a/src/java/org/apache/fop/layoutmgr/PageBreaker.java b/src/java/org/apache/fop/layoutmgr/PageBreaker.java index ba676ab89..8cc9db790 100644 --- a/src/java/org/apache/fop/layoutmgr/PageBreaker.java +++ b/src/java/org/apache/fop/layoutmgr/PageBreaker.java @@ -51,6 +51,8 @@ public class PageBreaker extends AbstractBreaker { private PageProvider pageProvider; private Block separatorArea; private boolean spanAllActive; + private boolean layoutRedone; + private int previousIndex; private boolean handlingStartOfFloat; private boolean handlingEndOfFloat; private int floatHeight; @@ -161,7 +163,7 @@ public class PageBreaker extends AbstractBreaker { /** {@inheritDoc} */ protected int getNextBlockList(LayoutContext childLC, int nextSequenceStartsOn, Position positionAtIPDChange, LayoutManager restartLM, List firstElements) { - if (!handlingFloat()) { + if (!layoutRedone && !handlingFloat()) { if (!firstPart) { // if this is the first page that will be created by // the current BlockSequence, it could have a break @@ -330,21 +332,55 @@ public class PageBreaker extends AbstractBreaker { return; } - boolean lastPageMasterDefined = pslm.getPageSequence().hasPagePositionLast() - || pslm.getPageSequence().hasPagePositionOnly() && pslm.isOnFirstPage(partCount - 1); - if (!hasMoreContent()) { - //last part is reached - if (lastPageMasterDefined) { - //last-page condition - redoLayout(alg, partCount, originalList, effectiveList); - return; - } + if (shouldRedoLayout(partCount)) { + redoLayout(alg, partCount, originalList, effectiveList); + return; } //nothing special: just add the areas now addAreas(alg, partCount, originalList, effectiveList); } + protected void prepareToRedoLayout(PageBreakingAlgorithm alg, int partCount, + BlockSequence originalList, + BlockSequence effectiveList) { + int newStartPos = 0; + int restartPoint = pageProvider.getStartingPartIndexForLastPage(partCount); + if (restartPoint > 0 && !layoutRedone) { + // Add definitive areas for the parts before the + // restarting point + addAreas(alg, restartPoint, originalList, effectiveList); + // Get page break from which we restart + PageBreakPosition pbp = alg.getPageBreaks().get(restartPoint - 1); + newStartPos = alg.par.getFirstBoxIndex(pbp.getLeafPos() + 1); + // Handle page break right here to avoid any side-effects + if (newStartPos > 0) { + handleBreakTrait(Constants.EN_PAGE); + } + } + pageBreakHandled = true; + // Update so the available BPD is reported correctly + int currentPageNum = pslm.getCurrentPageNum(); + int currentColumn = pslm.getCurrentPV().getCurrentSpan().getCurrentFlowIndex(); + pageProvider.setStartOfNextElementList(currentPageNum, currentColumn, spanAllActive); + + // Make sure we only add the areas we haven't added already + effectiveList.ignoreAtStart = newStartPos; + if (!layoutRedone) { + // Handle special page-master for last page + setLastPageIndex(currentPageNum); +// BodyRegion lastBody = pageProvider.getPage(false, currentPageNum).getPageViewport().getBodyRegion(); + pslm.setCurrentPage(pageProvider.getPage(false, currentPageNum)); + previousIndex = pageProvider.getIndexOfCachedLastPage(); + } else { + setLastPageIndex(currentPageNum + 1); +// pslm.setCurrentPage(previousPage); + pageProvider.discardCacheStartingWith(previousIndex); + pslm.setCurrentPage(pageProvider.getPage(false, currentPageNum)); + } + layoutRedone = true; + } + /** * Restart the algorithm at the break corresponding to the given partCount. Used to * re-do the part after the last break in case of either column-balancing or a last @@ -565,6 +601,7 @@ public class PageBreaker extends AbstractBreaker { return; case Constants.EN_COLUMN: case Constants.EN_AUTO: + case Constants.EN_PAGE: case -1: PageViewport pv = curPage.getPageViewport(); @@ -580,26 +617,35 @@ public class PageBreaker extends AbstractBreaker { log.trace("Forcing new page with span"); curPage = pslm.makeNewPage(false); curPage.getPageViewport().createSpan(true); - } else if (pv.getCurrentSpan().hasMoreFlows()) { - log.trace("Moving to next flow"); - pv.getCurrentSpan().moveToNextFlow(); } else { - log.trace("Making new page"); - /*curPage = */pslm.makeNewPage(false); + if (breakVal == Constants.EN_PAGE) { + handleBreakBeforeFollowingPage(breakVal); + } else { + if (pv.getCurrentSpan().hasMoreFlows()) { + log.trace("Moving to next flow"); + pv.getCurrentSpan().moveToNextFlow(); + } else { + log.trace("Making new page"); + pslm.makeNewPage(false); + } + } } return; - case Constants.EN_PAGE: default: - log.debug("handling break-before after page " + pslm.getCurrentPageNum() - + " breakVal=" + getBreakClassName(breakVal)); - if (needBlankPageBeforeNew(breakVal)) { - log.trace("Inserting blank page"); - /*curPage = */pslm.makeNewPage(true); - } - if (needNewPage(breakVal)) { - log.trace("Making new page"); - /*curPage = */pslm.makeNewPage(false); - } + handleBreakBeforeFollowingPage(breakVal); + } + } + + private void handleBreakBeforeFollowingPage(int breakVal) { + log.debug("handling break-before after page " + pslm.getCurrentPageNum() + " breakVal=" + + getBreakClassName(breakVal)); + if (needBlankPageBeforeNew(breakVal)) { + log.trace("Inserting blank page"); + /* curPage = */pslm.makeNewPage(true); + } + if (needNewPage(breakVal)) { + log.trace("Making new page"); + /* curPage = */pslm.makeNewPage(false); } } @@ -641,6 +687,36 @@ public class PageBreaker extends AbstractBreaker { } } + protected boolean shouldRedoLayout() { + return shouldRedoLayout(-1); + } + + protected boolean shouldRedoLayout(int partCount) { + boolean lastPageMasterDefined = pslm.getPageSequence().hasPagePositionLast(); + if (!lastPageMasterDefined && partCount != -1) { + lastPageMasterDefined = pslm.getPageSequence().hasPagePositionOnly() && pslm.isOnFirstPage(partCount - 1); + } + return (!hasMoreContent() && lastPageMasterDefined && !layoutRedone); + } + + protected boolean wasLayoutRedone() { + return layoutRedone; + } + + protected boolean lastPageHasIPDChange() { + boolean lastPageMasterDefined = pslm.getPageSequence().hasPagePositionLast(); + boolean onlyPageMasterDefined = pslm.getPageSequence().hasPagePositionOnly(); + if (lastPageMasterDefined && !onlyPageMasterDefined) { + // code not very robust and unable to handle situations were only and last are defined + int currentIPD = this.pageProvider.getCurrentIPD(); + int lastPageIPD = this.pageProvider.getLastPageIPD(); + if (lastPageIPD != -1 && currentIPD != lastPageIPD) { + return true; + } + } + return false; + } + protected boolean handlingStartOfFloat() { return handlingStartOfFloat; } diff --git a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java index 9327f8f8c..b72124c77 100644 --- a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java +++ b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java @@ -95,6 +95,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { private int ipdDifference; private KnuthNode bestNodeForIPDChange; + public KnuthNode bestNodeForLastPage; //Used to keep track of switches in keep-context private int currentKeepContext = Constants.EN_AUTO; @@ -1258,6 +1259,9 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { * the IPD change. No need to do any special handling. */ ipdDifference = 0; + } else if (line > 0 /*&& (bestNodeForLastPage == null + || node.totalDemerits < bestNodeForLastPage.totalDemerits)*/) { + bestNodeForLastPage = node; } super.addNode(line, node); } @@ -1274,6 +1278,10 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { return pageProvider.compareIPDs(line); } + KnuthNode getBestNodeForLastPage() { + return bestNodeForLastPage; + } + protected boolean handlingFloat() { return (handlingStartOfFloat || handlingEndOfFloat); } diff --git a/src/java/org/apache/fop/layoutmgr/PageProvider.java b/src/java/org/apache/fop/layoutmgr/PageProvider.java index 142f7ad72..ca41c8c1e 100644 --- a/src/java/org/apache/fop/layoutmgr/PageProvider.java +++ b/src/java/org/apache/fop/layoutmgr/PageProvider.java @@ -201,8 +201,8 @@ public class PageProvider implements Constants { return 0; } else { Page nextPage = getPage(false, column.pageIndex + 1, RELTO_CURRENT_ELEMENT_LIST); - return column.page.getPageViewport().getBodyRegion().getIPD() - - nextPage.getPageViewport().getBodyRegion().getIPD(); + return column.page.getPageViewport().getBodyRegion().getColumnIPD() + - nextPage.getPageViewport().getBodyRegion().getColumnIPD(); } } @@ -332,7 +332,7 @@ public class PageProvider implements Constants { return page; } - private void discardCacheStartingWith(int index) { + protected void discardCacheStartingWith(int index) { while (index < cachedPages.size()) { this.cachedPages.remove(cachedPages.size() - 1); if (!pageSeq.goToPreviousSimplePageMaster()) { @@ -352,9 +352,38 @@ public class PageProvider implements Constants { page.getPageViewport().setForeignAttributes(spm.getForeignAttributes()); page.getPageViewport().setWritingModeTraits(pageSeq); cachedPages.add(page); + if (isLastPage) { + pageSeq.getRoot().setLastSeq(pageSeq); + } else if (!isFirstPage) { + pageSeq.getRoot().setLastSeq(null); + } return page; } + public int getIndexOfCachedLastPage() { + return indexOfCachedLastPage; + } + + public int getLastPageIndex() { + return lastPageIndex; + } + + public int getLastPageIPD() { + int index = this.cachedPages.size(); + boolean isFirstPage = (startPageOfPageSequence == index); + SimplePageMaster spm = pageSeq.getLastSimplePageMaster(index, isFirstPage, false); + Page page = new Page(spm, index, "", false, false); + if (pageSeq.getRoot().getLastSeq() != null && pageSeq.getRoot().getLastSeq() != pageSeq) { + return -1; + } + return page.getPageViewport().getBodyRegion().getColumnIPD(); + } + + public int getCurrentIPD() { + return getPageFromColumnIndex(startColumnOfCurrentElementList).getPageViewport().getBodyRegion() + .getColumnIPD(); + } + /** * Indicates whether the column/page at the given index is on the first page of the page sequence. * diff --git a/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java b/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java index 2e2bd0a22..0ee7121af 100644 --- a/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java @@ -254,6 +254,16 @@ public class PageSequenceLayoutManager extends AbstractPageSequenceLayoutManager return pageProvider.isOnFirstPage(partIndex); } + protected int getLastPageNumber() { + return pageProvider.getLastPageIndex(); + } + + protected int getWidthOfCurrentPage() { + if (curPage != null) { + return (int) curPage.getPageViewport().getViewArea().getWidth(); + } + return 0; + } /** * Registers the given footnotes so that they can be added to the current page, before any other footnote. * diff --git a/src/java/org/apache/fop/layoutmgr/table/TableContentPosition.java b/src/java/org/apache/fop/layoutmgr/table/TableContentPosition.java index 457cfaef3..e6dc5b22d 100644 --- a/src/java/org/apache/fop/layoutmgr/table/TableContentPosition.java +++ b/src/java/org/apache/fop/layoutmgr/table/TableContentPosition.java @@ -124,4 +124,8 @@ class TableContentPosition extends Position { sb.append(")"); return sb.toString(); } + + public Position getPosition() { + return this; + } } |