aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSimon Steiner <ssteiner@apache.org>2016-01-06 12:13:52 +0000
committerSimon Steiner <ssteiner@apache.org>2016-01-06 12:13:52 +0000
commit51f22319512e0a50b6f2c0e63da01b79a392275c (patch)
tree401b2816e43bd2d610321c93096a57c55045b588 /src
parentd88b17f110e6d3e9683c316d460ae5e6e2ed4f1b (diff)
downloadxmlgraphics-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')
-rw-r--r--src/java/org/apache/fop/area/BodyRegion.java9
-rw-r--r--src/java/org/apache/fop/area/MainReference.java6
-rw-r--r--src/java/org/apache/fop/fo/pagination/PageSequence.java10
-rw-r--r--src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java18
-rw-r--r--src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java17
-rw-r--r--src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java5
-rw-r--r--src/java/org/apache/fop/fo/pagination/Root.java10
-rw-r--r--src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java5
-rw-r--r--src/java/org/apache/fop/fo/pagination/SubSequenceSpecifier.java5
-rw-r--r--src/java/org/apache/fop/layoutmgr/AbstractBreaker.java226
-rw-r--r--src/java/org/apache/fop/layoutmgr/AbstractPageSequenceLayoutManager.java8
-rw-r--r--src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.java17
-rw-r--r--src/java/org/apache/fop/layoutmgr/BlockLevelEventProducer.xml2
-rw-r--r--src/java/org/apache/fop/layoutmgr/PageBreaker.java128
-rw-r--r--src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java8
-rw-r--r--src/java/org/apache/fop/layoutmgr/PageProvider.java35
-rw-r--r--src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java10
-rw-r--r--src/java/org/apache/fop/layoutmgr/table/TableContentPosition.java4
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;
+ }
}