aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/java/org/apache/fop/fo/pagination/ConditionalPageMasterReference.java29
-rw-r--r--src/java/org/apache/fop/fo/pagination/PageSequence.java27
-rw-r--r--src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java17
-rw-r--r--src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java27
-rw-r--r--src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java9
-rw-r--r--src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java8
-rw-r--r--src/java/org/apache/fop/fo/pagination/SubSequenceSpecifier.java7
-rw-r--r--src/java/org/apache/fop/layoutmgr/AbstractBreaker.java18
-rw-r--r--src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java11
-rw-r--r--src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java229
-rw-r--r--src/java/org/apache/fop/render/rtf/RTFHandler.java2
-rw-r--r--status.xml3
-rw-r--r--test/layoutengine/standard-testcases/page-position_last_1.xml168
13 files changed, 458 insertions, 97 deletions
diff --git a/src/java/org/apache/fop/fo/pagination/ConditionalPageMasterReference.java b/src/java/org/apache/fop/fo/pagination/ConditionalPageMasterReference.java
index d688b0fab..1ac786751 100644
--- a/src/java/org/apache/fop/fo/pagination/ConditionalPageMasterReference.java
+++ b/src/java/org/apache/fop/fo/pagination/ConditionalPageMasterReference.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2005 The Apache Software Foundation.
+ * Copyright 1999-2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -90,28 +90,32 @@ public class ConditionalPageMasterReference extends FObj {
* matches.
* @param isOddPage True if page number odd
* @param isFirstPage True if page is first page
+ * @param isLastPage True if page is last page
* @param isBlankPage True if page is blank
* @return True if the conditions for this reference are met
*/
protected boolean isValid(boolean isOddPage,
boolean isFirstPage,
+ boolean isLastPage,
boolean isBlankPage) {
// page-position
if (isFirstPage) {
if (pagePosition == EN_REST) {
return false;
} else if (pagePosition == EN_LAST) {
- // ?? how can one know at this point?
- getLogger().debug("LAST PagePosition NYI");
+ return false;
+ }
+ } else if (isLastPage) {
+ if (pagePosition == EN_REST) {
+ return false;
+ } else if (pagePosition == EN_FIRST) {
return false;
}
} else {
if (pagePosition == EN_FIRST) {
return false;
} else if (pagePosition == EN_LAST) {
- // ?? how can one know at this point?
- getLogger().debug("LAST PagePosition NYI");
- // potentially valid, don't return
+ return false;
}
}
@@ -139,21 +143,22 @@ public class ConditionalPageMasterReference extends FObj {
return true;
}
- /**
- * Returns the "master-reference" property.
- */
+ /** @return the "master-reference" property. */
public String getMasterReference() {
return masterReference;
}
+ /** @return the page-position property value */
+ public int getPagePosition() {
+ return this.pagePosition;
+ }
+
/** @see org.apache.fop.fo.FONode#getLocalName() */
public String getLocalName() {
return "conditional-page-master-reference";
}
- /**
- * @see org.apache.fop.fo.FObj#getNameId()
- */
+ /** @see org.apache.fop.fo.FObj#getNameId() */
public int getNameId() {
return FO_CONDITIONAL_PAGE_MASTER_REFERENCE;
}
diff --git a/src/java/org/apache/fop/fo/pagination/PageSequence.java b/src/java/org/apache/fop/fo/pagination/PageSequence.java
index 010bf9699..b79f194d1 100644
--- a/src/java/org/apache/fop/fo/pagination/PageSequence.java
+++ b/src/java/org/apache/fop/fo/pagination/PageSequence.java
@@ -436,15 +436,18 @@ public class PageSequence extends FObj {
/**
* Public accessor for determining the next page master to use within this page sequence.
* @param page the page number of the page to be created
- * @param bIsFirstPage indicator whether this page is the first page of the
+ * @param isFirstPage indicator whether this page is the first page of the
* page sequence
- * @param bIsBlank indicator whether the page will be blank
+ * @param isLastPage indicator whether this page is the last page of the
+ * page sequence
+ * @param isBlank indicator whether the page will be blank
* @return the SimplePageMaster to use for this page
* @throws FOPException if there's a problem determining the page master
*/
public SimplePageMaster getNextSimplePageMaster(int page,
- boolean bIsFirstPage,
- boolean bIsBlank) throws FOPException {
+ boolean isFirstPage,
+ boolean isLastPage,
+ boolean isBlank) throws FOPException {
if (pageSequenceMaster == null) {
return simplePageMaster;
@@ -453,11 +456,12 @@ public class PageSequence extends FObj {
if (getLogger().isDebugEnabled()) {
getLogger().debug("getNextSimplePageMaster(page=" + page
+ " isOdd=" + isOddPage
- + " isFirst=" + bIsFirstPage
- + " isBlank=" + bIsBlank + ")");
+ + " isFirst=" + isFirstPage
+ + " isLast=" + isLastPage
+ + " isBlank=" + isBlank + ")");
}
return pageSequenceMaster.getNextSimplePageMaster(isOddPage,
- bIsFirstPage, bIsBlank);
+ isFirstPage, isLastPage, isBlank);
}
/**
@@ -472,6 +476,15 @@ public class PageSequence extends FObj {
}
}
+ /** @return true if the page-sequence has a page-master with page-position="last" */
+ public boolean hasPagePositionLast() {
+ if (pageSequenceMaster == null) {
+ return false;
+ } else {
+ return pageSequenceMaster.hasPagePositionLast();
+ }
+ }
+
/**
* Retrieves the string representation of a page number applicable
* for this page sequence
diff --git a/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java b/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java
index 1f1b012c6..a55e6920c 100644
--- a/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java
+++ b/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2005 The Apache Software Foundation.
+ * Copyright 1999-2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -159,16 +159,27 @@ public class PageSequenceMaster extends FObj {
return (currentSubSequence != null);
}
+ /** @return true if the page-sequence-master has a page-master with page-position="last" */
+ public boolean hasPagePositionLast() {
+ if (currentSubSequence != null) {
+ return currentSubSequence.hasPagePositionLast();
+ } else {
+ return false;
+ }
+ }
+
/**
* Returns the next simple-page-master.
* @param isOddPage True if the next page number is odd
* @param isFirstPage True if the next page is the first
+ * @param isLastPage True if the next page is the last
* @param isBlankPage True if the next page is blank
* @return the requested page master
* @throws FOPException if there's a problem determining the next page master
*/
public SimplePageMaster getNextSimplePageMaster(boolean isOddPage,
boolean isFirstPage,
+ boolean isLastPage,
boolean isBlankPage)
throws FOPException {
if (currentSubSequence == null) {
@@ -179,7 +190,7 @@ public class PageSequenceMaster extends FObj {
}
}
String pageMasterName = currentSubSequence
- .getNextPageMasterName(isOddPage, isFirstPage, isBlankPage);
+ .getNextPageMasterName(isOddPage, isFirstPage, isLastPage, isBlankPage);
boolean canRecover = true;
while (pageMasterName == null) {
SubSequenceSpecifier nextSubSequence = getNextSubSequence();
@@ -198,7 +209,7 @@ public class PageSequenceMaster extends FObj {
currentSubSequence = nextSubSequence;
}
pageMasterName = currentSubSequence
- .getNextPageMasterName(isOddPage, isFirstPage, isBlankPage);
+ .getNextPageMasterName(isOddPage, isFirstPage, isLastPage, isBlankPage);
}
SimplePageMaster pageMaster = this.layoutMasterSet
.getSimplePageMaster(pageMasterName);
diff --git a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java
index 6ff2f6a4b..5a08c2873 100644
--- a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java
+++ b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2005 The Apache Software Foundation.
+ * Copyright 1999-2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,6 +47,7 @@ public class RepeatablePageMasterAlternatives extends FObj
private int numberConsumed = 0;
private List conditionalPageMasterRefs;
+ private boolean hasPagePositionLast = false;
/**
* @see org.apache.fop.fo.FONode#FONode(FONode)
@@ -93,9 +94,9 @@ public class RepeatablePageMasterAlternatives extends FObj
*/
protected void validateChildNode(Locator loc, String nsURI, String localName)
throws ValidationException {
- if (!(FO_URI.equals(nsURI) &&
- localName.equals("conditional-page-master-reference"))) {
- invalidChildError(loc, nsURI, localName);
+ if (!(FO_URI.equals(nsURI)
+ && localName.equals("conditional-page-master-reference"))) {
+ invalidChildError(loc, nsURI, localName);
}
}
@@ -121,6 +122,7 @@ public class RepeatablePageMasterAlternatives extends FObj
*/
public String getNextPageMasterName(boolean isOddPage,
boolean isFirstPage,
+ boolean isLastPage,
boolean isBlankPage) {
if (getMaximumRepeats() != INFINITE) {
if (numberConsumed < getMaximumRepeats()) {
@@ -128,12 +130,14 @@ public class RepeatablePageMasterAlternatives extends FObj
} else {
return null;
}
+ } else {
+ numberConsumed++;
}
for (int i = 0; i < conditionalPageMasterRefs.size(); i++) {
- ConditionalPageMasterReference cpmr =
- (ConditionalPageMasterReference)conditionalPageMasterRefs.get(i);
- if (cpmr.isValid(isOddPage, isFirstPage, isBlankPage)) {
+ ConditionalPageMasterReference cpmr
+ = (ConditionalPageMasterReference)conditionalPageMasterRefs.get(i);
+ if (cpmr.isValid(isOddPage, isFirstPage, isLastPage, isBlankPage)) {
return cpmr.getMasterReference();
}
}
@@ -147,6 +151,9 @@ public class RepeatablePageMasterAlternatives extends FObj
*/
public void addConditionalPageMasterReference(ConditionalPageMasterReference cpmr) {
this.conditionalPageMasterRefs.add(cpmr);
+ if (cpmr.getPagePosition() == EN_LAST) {
+ this.hasPagePositionLast = true;
+ }
}
/** @see org.apache.fop.fo.pagination.SubSequenceSpecifier#reset() */
@@ -164,6 +171,11 @@ public class RepeatablePageMasterAlternatives extends FObj
}
}
+ /** @see org.apache.fop.fo.pagination.SubSequenceSpecifier#hasPagePositionLast() */
+ public boolean hasPagePositionLast() {
+ return this.hasPagePositionLast;
+ }
+
/** @see org.apache.fop.fo.FONode#getLocalName() */
public String getLocalName() {
return "repeatable-page-master-alternatives";
@@ -173,4 +185,5 @@ public class RepeatablePageMasterAlternatives extends FObj
public int getNameId() {
return FO_REPEATABLE_PAGE_MASTER_ALTERNATIVES;
}
+
}
diff --git a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java
index 1acdf532e..811b30d54 100644
--- a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java
+++ b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2005 The Apache Software Foundation.
+ * Copyright 1999-2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -91,6 +91,7 @@ public class RepeatablePageMasterReference extends FObj
*/
public String getNextPageMasterName(boolean isOddPage,
boolean isFirstPage,
+ boolean isLastPage,
boolean isEmptyPage) {
if (getMaximumRepeats() != INFINITE) {
if (numberConsumed < getMaximumRepeats()) {
@@ -133,6 +134,11 @@ public class RepeatablePageMasterReference extends FObj
}
}
+ /** @see org.apache.fop.fo.pagination.SubSequenceSpecifier#hasPagePositionLast() */
+ public boolean hasPagePositionLast() {
+ return false;
+ }
+
/** @see org.apache.fop.fo.FONode#getLocalName() */
public String getLocalName() {
return "repeatable-page-master-reference";
@@ -142,4 +148,5 @@ public class RepeatablePageMasterReference extends FObj
public int getNameId() {
return FO_REPEATABLE_PAGE_MASTER_REFERENCE;
}
+
}
diff --git a/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java b/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java
index 75b2ac899..cea4a20d7 100644
--- a/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java
+++ b/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2005 The Apache Software Foundation.
+ * Copyright 1999-2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -83,6 +83,7 @@ public class SinglePageMasterReference extends FObj
/** @see org.apache.fop.fo.pagination.SubSequenceSpecifier */
public String getNextPageMasterName(boolean isOddPage,
boolean isFirstPage,
+ boolean isLastPage,
boolean isEmptyPage) {
if (this.state == FIRST) {
this.state = DONE;
@@ -109,6 +110,11 @@ public class SinglePageMasterReference extends FObj
}
}
+ /** @see org.apache.fop.fo.pagination.SubSequenceSpecifier#hasPagePositionLast() */
+ public boolean hasPagePositionLast() {
+ return false;
+ }
+
/** @see org.apache.fop.fo.FONode#getLocalName() */
public String getLocalName() {
return "single-page-master-reference";
diff --git a/src/java/org/apache/fop/fo/pagination/SubSequenceSpecifier.java b/src/java/org/apache/fop/fo/pagination/SubSequenceSpecifier.java
index 39609bb05..2d0138f32 100644
--- a/src/java/org/apache/fop/fo/pagination/SubSequenceSpecifier.java
+++ b/src/java/org/apache/fop/fo/pagination/SubSequenceSpecifier.java
@@ -1,5 +1,5 @@
/*
- * Copyright 1999-2005 The Apache Software Foundation.
+ * Copyright 1999-2006 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,12 +30,14 @@ public interface SubSequenceSpecifier {
* Returns the name of the next page master.
* @param isOddPage True if the next page number is odd
* @param isFirstPage True if the next page is the first
+ * @param isLastPage True if the next page is the last
* @param isBlankPage True if the next page is blank
* @return the page master name
* @throws FOPException if there's a problem determining the next page master
*/
String getNextPageMasterName(boolean isOddPage,
boolean isFirstPage,
+ boolean isLastPage,
boolean isBlankPage)
throws FOPException;
@@ -50,6 +52,9 @@ public interface SubSequenceSpecifier {
* @return true if there is a previous item, false if the current one was the first one.
*/
boolean goToPrevious();
+
+ /** @return true if the subsequence has a page master for page-position "last" */
+ boolean hasPagePositionLast();
}
diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
index 732a0133a..eea83f1ab 100644
--- a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
+++ b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
@@ -318,13 +318,26 @@ public abstract class AbstractBreaker {
*/
protected void addAreas(PageBreakingAlgorithm alg, int partCount,
BlockSequence originalList, BlockSequence effectiveList) {
+ addAreas(alg, 0, partCount, originalList, effectiveList);
+ }
+
+ /**
+ * Phase 3 of Knuth algorithm: Adds the areas
+ * @param alg PageBreakingAlgorithm instance which determined the breaks
+ * @param startPart index of the first part (page) to be rendered
+ * @param partCount number of parts (pages) to be rendered
+ * @param originalList original Knuth element list
+ * @param effectiveList effective Knuth element list (after adjustments)
+ */
+ protected void addAreas(PageBreakingAlgorithm alg, int startPart, int partCount,
+ BlockSequence originalList, BlockSequence effectiveList) {
LayoutContext childLC;
// add areas
ListIterator effectiveListIterator = effectiveList.listIterator();
int startElementIndex = 0;
int endElementIndex = 0;
int lastBreak = -1;
- for (int p = 0; p < partCount; p++) {
+ for (int p = startPart; p < startPart + partCount; p++) {
PageBreakPosition pbp = (PageBreakPosition) alg.getPageBreaks().get(p);
//Check the last break position for forced breaks
@@ -426,7 +439,8 @@ public abstract class AbstractBreaker {
/* *** *** non-standard extension *** *** */
if (displayAlign == Constants.EN_X_FILL) {
- int averageLineLength = optimizeLineLength(effectiveList, startElementIndex, endElementIndex);
+ int averageLineLength = optimizeLineLength(effectiveList,
+ startElementIndex, endElementIndex);
if (averageLineLength != 0) {
childLC.setStackLimit(new MinOptMax(averageLineLength));
}
diff --git a/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java
index 4109836e0..0e7a554b8 100644
--- a/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java
+++ b/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java
@@ -991,4 +991,15 @@ public abstract class BreakingAlgorithm {
bestActiveNode = bestActiveNode.previous;
}
}
+
+ /** @return the alignment for normal lines/parts */
+ public int getAlignment() {
+ return this.alignment;
+ }
+
+ /** @return the alignment for the last line/part */
+ public int getAlignmentLast() {
+ return this.alignmentLast;
+ }
+
}
diff --git a/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java b/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java
index b94b2623c..a410e1838 100644
--- a/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java
+++ b/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java
@@ -154,6 +154,9 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
finishPage();
}
+ /**
+ * Finished the page-sequence and notifies everyone about it.
+ */
public void finishPageSequence() {
pageSeq.getRoot().notifyPageSequenceFinished(currentPageNum,
(currentPageNum - startPageNum) + 1);
@@ -318,62 +321,132 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
protected void doPhase3(PageBreakingAlgorithm alg, int partCount,
BlockSequence originalList, BlockSequence effectiveList) {
if (needColumnBalancing) {
- AbstractBreaker.log.debug("Column balancing now!!!");
- AbstractBreaker.log.debug("===================================================");
- int restartPoint = pageProvider.getStartingPartIndexForLastPage(partCount);
- if (restartPoint > 0) {
- addAreas(alg, restartPoint, originalList, effectiveList);
- }
-
- int newStartPos;
- if (restartPoint > 0) {
- PageBreakPosition pbp = (PageBreakPosition)
- alg.getPageBreaks().get(restartPoint - 1);
- newStartPos = pbp.getLeafPos();
+ doPhase3WithColumnBalancing(alg, partCount, originalList, effectiveList);
+ } else {
+ if (!hasMoreContent() && pageSeq.hasPagePositionLast()) {
+ //last part is reached and we have a "last page" condition
+ doPhase3WithLastPage(alg, partCount, originalList, effectiveList);
} else {
- newStartPos = 0;
+ //Directly add areas after finding the breaks
+ addAreas(alg, partCount, originalList, effectiveList);
}
- AbstractBreaker.log.debug("Restarting at " + restartPoint
- + ", new start position: " + newStartPos);
+ }
+ }
+ private void doPhase3WithLastPage(PageBreakingAlgorithm alg, int partCount,
+ BlockSequence originalList, BlockSequence effectiveList) {
+ int newStartPos;
+ int restartPoint = pageProvider.getStartingPartIndexForLastPage(partCount);
+ if (restartPoint > 0) {
+ //Add definitive areas before last page
+ addAreas(alg, restartPoint, originalList, effectiveList);
+ //Get page break from which we restart
+ PageBreakPosition pbp = (PageBreakPosition)
+ alg.getPageBreaks().get(restartPoint - 1);
+ newStartPos = pbp.getLeafPos();
//Handle page break right here to avoid any side-effects
if (newStartPos > 0) {
handleBreakTrait(EN_PAGE);
}
- pageBreakHandled = true;
- //Update so the available BPD is reported correctly
- pageProvider.setStartOfNextElementList(currentPageNum,
- getCurrentPV().getCurrentSpan().getCurrentFlowIndex());
+ } else {
+ newStartPos = 0;
+ }
+ AbstractBreaker.log.debug("Last page handling now!!!");
+ AbstractBreaker.log.debug("===================================================");
+ AbstractBreaker.log.debug("Restarting at " + restartPoint
+ + ", new start position: " + newStartPos);
- //Restart last page
- PageBreakingAlgorithm algRestart = new BalancingColumnBreakingAlgorithm(
- getTopLevelLM(),
- getPageProvider(),
- alignment, Constants.EN_START, footnoteSeparatorLength,
- isPartOverflowRecoveryActivated(),
- getCurrentPV().getBodyRegion().getColumnCount());
- //alg.setConstantLineWidth(flowBPD);
- int iOptPageCount = algRestart.findBreakingPoints(effectiveList,
- newStartPos,
- 1, true, BreakingAlgorithm.ALL_BREAKS);
- AbstractBreaker.log.debug("restart: iOptPageCount= " + iOptPageCount
- + " pageBreaks.size()= " + algRestart.getPageBreaks().size());
- if (iOptPageCount > getCurrentPV().getBodyRegion().getColumnCount()) {
- AbstractBreaker.log.warn(
- "Breaking algorithm produced more columns than are available.");
- /* reenable when everything works
- throw new IllegalStateException(
- "Breaking algorithm must not produce more columns than available.");
- */
- }
+ pageBreakHandled = true;
+ //Update so the available BPD is reported correctly
+ pageProvider.setStartOfNextElementList(currentPageNum,
+ getCurrentPV().getCurrentSpan().getCurrentFlowIndex());
+ pageProvider.setLastPageIndex(currentPageNum);
+
+ //Restart last page
+ PageBreakingAlgorithm algRestart = new PageBreakingAlgorithm(
+ getTopLevelLM(),
+ getPageProvider(),
+ alg.getAlignment(), alg.getAlignmentLast(),
+ footnoteSeparatorLength,
+ isPartOverflowRecoveryActivated(), false);
+ //alg.setConstantLineWidth(flowBPD);
+ int iOptPageCount = algRestart.findBreakingPoints(effectiveList,
+ newStartPos,
+ 1, true, BreakingAlgorithm.ALL_BREAKS);
+ AbstractBreaker.log.debug("restart: iOptPageCount= " + iOptPageCount
+ + " pageBreaks.size()= " + algRestart.getPageBreaks().size());
+ boolean replaceLastPage
+ = iOptPageCount <= getCurrentPV().getBodyRegion().getColumnCount();
+ if (replaceLastPage) {
+
+ //Replace last page
+ pslm.curPage = pageProvider.getPage(false, currentPageNum);
//Make sure we only add the areas we haven't added already
effectiveList.ignoreAtStart = newStartPos;
addAreas(algRestart, iOptPageCount, originalList, effectiveList);
- AbstractBreaker.log.debug("===================================================");
} else {
- //Directly add areas after finding the breaks
- addAreas(alg, partCount, originalList, effectiveList);
+ effectiveList.ignoreAtStart = newStartPos;
+ addAreas(alg, restartPoint, partCount - restartPoint, originalList, effectiveList);
+ //Add blank last page
+ pageProvider.setLastPageIndex(currentPageNum + 1);
+ pslm.curPage = makeNewPage(true, true);
+ }
+ AbstractBreaker.log.debug("===================================================");
+ }
+
+ private void doPhase3WithColumnBalancing(PageBreakingAlgorithm alg, int partCount,
+ BlockSequence originalList, BlockSequence effectiveList) {
+ AbstractBreaker.log.debug("Column balancing now!!!");
+ AbstractBreaker.log.debug("===================================================");
+ int newStartPos;
+ int restartPoint = pageProvider.getStartingPartIndexForLastPage(partCount);
+ if (restartPoint > 0) {
+ //Add definitive areas
+ addAreas(alg, restartPoint, originalList, effectiveList);
+ //Get page break from which we restart
+ PageBreakPosition pbp = (PageBreakPosition)
+ alg.getPageBreaks().get(restartPoint - 1);
+ newStartPos = pbp.getLeafPos();
+ //Handle page break right here to avoid any side-effects
+ if (newStartPos > 0) {
+ handleBreakTrait(EN_PAGE);
+ }
+ } else {
+ newStartPos = 0;
+ }
+ AbstractBreaker.log.debug("Restarting at " + restartPoint
+ + ", new start position: " + newStartPos);
+
+ pageBreakHandled = true;
+ //Update so the available BPD is reported correctly
+ pageProvider.setStartOfNextElementList(currentPageNum,
+ getCurrentPV().getCurrentSpan().getCurrentFlowIndex());
+
+ //Restart last page
+ PageBreakingAlgorithm algRestart = new BalancingColumnBreakingAlgorithm(
+ getTopLevelLM(),
+ getPageProvider(),
+ alignment, Constants.EN_START, footnoteSeparatorLength,
+ isPartOverflowRecoveryActivated(),
+ getCurrentPV().getBodyRegion().getColumnCount());
+ //alg.setConstantLineWidth(flowBPD);
+ int iOptPageCount = algRestart.findBreakingPoints(effectiveList,
+ newStartPos,
+ 1, true, BreakingAlgorithm.ALL_BREAKS);
+ AbstractBreaker.log.debug("restart: iOptPageCount= " + iOptPageCount
+ + " pageBreaks.size()= " + algRestart.getPageBreaks().size());
+ if (iOptPageCount > getCurrentPV().getBodyRegion().getColumnCount()) {
+ AbstractBreaker.log.warn(
+ "Breaking algorithm produced more columns than are available.");
+ /* reenable when everything works
+ throw new IllegalStateException(
+ "Breaking algorithm must not produce more columns than available.");
+ */
}
+ //Make sure we only add the areas we haven't added already
+ effectiveList.ignoreAtStart = newStartPos;
+ addAreas(algRestart, iOptPageCount, originalList, effectiveList);
+ AbstractBreaker.log.debug("===================================================");
}
protected void startPart(BlockSequence list, int breakClass) {
@@ -728,6 +801,9 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
private int startColumnOfCurrentElementList;
private List cachedPages = new java.util.ArrayList();
+ private int lastPageIndex = -1;
+ private int indexOfCachedLastPage = -1;
+
//Cache to optimize getAvailableBPD() calls
private int lastRequestedIndex = -1;
private int lastReportedBPD = -1;
@@ -757,6 +833,15 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
}
/**
+ * Sets the index of the last page. This is done as soon as the position of the last page
+ * is known or assumed.
+ * @param index the index relative to the first page in the page-sequence
+ */
+ public void setLastPageIndex(int index) {
+ this.lastPageIndex = index;
+ }
+
+ /**
* Returns the available BPD for the part/page indicated by the index parameter.
* The index is the part/page relative to the start of the current element list.
* This method takes multiple columns into account.
@@ -823,60 +908,79 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
/**
* Returns a Page.
- * @param bIsBlank true if this page is supposed to be blank.
+ * @param isBlank true if this page is supposed to be blank.
* @param index Index of the page (see relativeTo)
* @param relativeTo Defines which value the index parameter should be evaluated relative
* to. (One of PageProvider.RELTO_*)
* @return the requested Page
*/
- public Page getPage(boolean bIsBlank, int index, int relativeTo) {
+ public Page getPage(boolean isBlank, int index, int relativeTo) {
if (relativeTo == RELTO_PAGE_SEQUENCE) {
- return getPage(bIsBlank, index);
+ return getPage(isBlank, index);
} else if (relativeTo == RELTO_CURRENT_ELEMENT_LIST) {
int effIndex = startPageOfCurrentElementList + index;
effIndex += startPageOfPageSequence - 1;
- return getPage(bIsBlank, effIndex);
+ return getPage(isBlank, effIndex);
} else {
throw new IllegalArgumentException(
"Illegal value for relativeTo: " + relativeTo);
}
}
- private Page getPage(boolean bIsBlank, int index) {
+ private Page getPage(boolean isBlank, int index) {
+ boolean isLastPage = (lastPageIndex >= 0) && (index == lastPageIndex);
if (log.isTraceEnabled()) {
- log.trace("getPage(" + index + " " + bIsBlank);
+ log.trace("getPage(" + index + " " + (isBlank ? "blank" : "non-blank")
+ + (isLastPage ? " <LAST>" : "") + ")");
}
int intIndex = index - startPageOfPageSequence;
if (log.isTraceEnabled()) {
- if (bIsBlank) {
+ if (isBlank) {
log.trace("blank page requested: " + index);
}
+ if (isLastPage) {
+ log.trace("last page requested: " + index);
+ }
}
while (intIndex >= cachedPages.size()) {
if (log.isTraceEnabled()) {
log.trace("Caching " + index);
}
- cacheNextPage(index, bIsBlank);
+ cacheNextPage(index, isBlank, isLastPage);
}
Page page = (Page)cachedPages.get(intIndex);
- if (page.getPageViewport().isBlank() != bIsBlank) {
+ boolean replace = false;
+ if (page.getPageViewport().isBlank() != isBlank) {
log.debug("blank condition doesn't match. Replacing PageViewport.");
- while (intIndex < cachedPages.size()) {
- this.cachedPages.remove(cachedPages.size() - 1);
- if (!pageSeq.goToPreviousSimplePageMaster()) {
- log.warn("goToPreviousSimplePageMaster() on the first page called!");
- }
- }
- cacheNextPage(index, bIsBlank);
+ replace = true;
+ }
+ if ((isLastPage && indexOfCachedLastPage != intIndex)
+ || (!isLastPage && indexOfCachedLastPage >= 0)) {
+ log.debug("last page condition doesn't match. Replacing PageViewport.");
+ replace = true;
+ indexOfCachedLastPage = (isLastPage ? intIndex : -1);
+ }
+ if (replace) {
+ disardCacheStartingWith(intIndex);
+ page = cacheNextPage(index, isBlank, isLastPage);
}
return page;
}
+
+ private void disardCacheStartingWith(int index) {
+ while (index < cachedPages.size()) {
+ this.cachedPages.remove(cachedPages.size() - 1);
+ if (!pageSeq.goToPreviousSimplePageMaster()) {
+ log.warn("goToPreviousSimplePageMaster() on the first page called!");
+ }
+ }
+ }
- private void cacheNextPage(int index, boolean bIsBlank) {
+ private Page cacheNextPage(int index, boolean isBlank, boolean isLastPage) {
try {
String pageNumberString = pageSeq.makeFormattedPageNumber(index);
SimplePageMaster spm = pageSeq.getNextSimplePageMaster(
- index, (startPageOfPageSequence == index), bIsBlank);
+ index, (startPageOfPageSequence == index), isLastPage, isBlank);
Region body = spm.getRegion(FO_REGION_BODY);
if (!pageSeq.getMainFlow().getFlowName().equals(body.getRegionName())) {
@@ -887,8 +991,9 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
+ spm.getMasterName() + "'. FOP presently "
+ "does not support this.");
}
- Page page = new Page(spm, index, pageNumberString, bIsBlank);
+ Page page = new Page(spm, index, pageNumberString, isBlank);
cachedPages.add(page);
+ return page;
} catch (FOPException e) {
//TODO Maybe improve. It'll mean to propagate this exception up several
//methods calls.
diff --git a/src/java/org/apache/fop/render/rtf/RTFHandler.java b/src/java/org/apache/fop/render/rtf/RTFHandler.java
index 41cc3dba8..6c99ab4fe 100644
--- a/src/java/org/apache/fop/render/rtf/RTFHandler.java
+++ b/src/java/org/apache/fop/render/rtf/RTFHandler.java
@@ -186,7 +186,7 @@ public class RTFHandler extends FOEventHandler {
log.warn("Using default simple-page-master from page-sequence-master...");
PageSequenceMaster master
= pageSeq.getRoot().getLayoutMasterSet().getPageSequenceMaster(reference);
- this.pagemaster = master.getNextSimplePageMaster(false, false, false);
+ this.pagemaster = master.getNextSimplePageMaster(false, false, false, false);
}
}
diff --git a/status.xml b/status.xml
index 65b0221df..964f7d56e 100644
--- a/status.xml
+++ b/status.xml
@@ -28,6 +28,9 @@
<changes>
<release version="FOP Trunk">
<action context="Code" dev="JM" type="add">
+ Initial support for page-position="last" added.
+ </action>
+ <action context="Code" dev="JM" type="add">
Reenabled loading of user-supplied hyphenation patterns that was available in
FOP 0.20.5. (See "hyphenation-base" option in the user configuration)
</action>
diff --git a/test/layoutengine/standard-testcases/page-position_last_1.xml b/test/layoutengine/standard-testcases/page-position_last_1.xml
new file mode 100644
index 000000000..bb83c2808
--- /dev/null
+++ b/test/layoutengine/standard-testcases/page-position_last_1.xml
@@ -0,0 +1,168 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright 2006 The Apache Software Foundation
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<!-- $Id$ -->
+<testcase>
+ <info>
+ <p>
+ This test checks page-position="last".
+ </p>
+ </info>
+ <fo>
+ <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:svg="http://www.w3.org/2000/svg">
+ <fo:layout-master-set>
+ <fo:simple-page-master master-name="normal" page-width="5in" page-height="5in">
+ <fo:region-body margin-bottom="1in" column-count="2" column-gap="10pt" background-color="lightgray"/>
+ <fo:region-after extent="1in" display-align="after"/>
+ </fo:simple-page-master>
+ <fo:simple-page-master master-name="last" page-width="5in" page-height="5in">
+ <fo:region-body margin-bottom="3in" column-count="2" column-gap="10pt" background-color="rgb(250,250,240)"/>
+ <fo:region-after extent="1in" display-align="after"/>
+ </fo:simple-page-master>
+ <fo:page-sequence-master master-name="master">
+ <fo:repeatable-page-master-alternatives>
+ <fo:conditional-page-master-reference master-reference="last" page-position="last"/>
+ <fo:conditional-page-master-reference master-reference="normal" page-position="any"/>
+ </fo:repeatable-page-master-alternatives>
+ </fo:page-sequence-master>
+ </fo:layout-master-set>
+ <fo:page-sequence master-reference="master" id="replace-last">
+ <fo:static-content flow-name="xsl-region-after">
+ <fo:block text-align="end">page <fo:page-number/> of <fo:page-number-citation ref-id="eof"/></fo:block>
+ </fo:static-content>
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block-container id="boxA0" height="1.5in" background-color="yellow" text-align="center" display-align="center" break-after="page">
+ <fo:block>box0</fo:block>
+ </fo:block-container>
+ <fo:block-container id="boxA1" height="1.5in" background-color="yellow" text-align="center" display-align="center">
+ <fo:block>box1</fo:block>
+ </fo:block-container>
+ <fo:block-container id="boxA2" height="1.5in" background-color="orange" text-align="center" display-align="center">
+ <fo:block>box2</fo:block>
+ </fo:block-container>
+ <fo:block-container id="boxA3" height="1.5in" background-color="yellow" text-align="center" display-align="center">
+ <fo:block>box3</fo:block>
+ </fo:block-container>
+ <fo:block-container id="boxA4" height="1.5in" background-color="orange" text-align="center" display-align="center">
+ <fo:block>box4</fo:block>
+ </fo:block-container>
+ <fo:block-container id="boxA5" height="1.5in" background-color="yellow" text-align="center" display-align="center">
+ <fo:block>box5</fo:block>
+ </fo:block-container>
+ <fo:block-container id="boxA6" height="1.5in" background-color="orange" text-align="center" display-align="center">
+ <fo:block>box6</fo:block>
+ </fo:block-container>
+ <fo:block-container id="boxA7" height="1.5in" background-color="yellow" text-align="center" display-align="center">
+ <fo:block>box7</fo:block>
+ </fo:block-container>
+ <fo:block-container id="boxA8" height="1.5in" background-color="orange" text-align="center" display-align="center">
+ <fo:block>box8</fo:block>
+ </fo:block-container>
+ <fo:block-container id="boxA9" height="1.5in" background-color="yellow" text-align="center" display-align="center">
+ <fo:block>box9</fo:block>
+ </fo:block-container>
+ <fo:block-container id="boxA10" height="1.5in" background-color="orange" text-align="center" display-align="center">
+ <fo:block>box10</fo:block>
+ </fo:block-container>
+ <fo:block id="eof"/>
+ </fo:flow>
+ </fo:page-sequence>
+ <fo:page-sequence master-reference="master" id="blank-last">
+ <fo:static-content flow-name="xsl-region-after">
+ <fo:block text-align="end">page <fo:page-number/> of <fo:page-number-citation ref-id="eof"/></fo:block>
+ </fo:static-content>
+ <fo:flow flow-name="xsl-region-body">
+ <fo:block-container id="boxB1" height="1.5in" background-color="yellow" text-align="center" display-align="center">
+ <fo:block>box1</fo:block>
+ </fo:block-container>
+ <fo:block-container id="boxB2" height="1.5in" background-color="orange" text-align="center" display-align="center">
+ <fo:block>box2</fo:block>
+ </fo:block-container>
+ <fo:block-container id="boxB3" height="1.5in" background-color="yellow" text-align="center" display-align="center">
+ <fo:block>box3</fo:block>
+ </fo:block-container>
+ <fo:block-container id="boxB4" height="1.5in" background-color="orange" text-align="center" display-align="center">
+ <fo:block>box4</fo:block>
+ </fo:block-container>
+ <fo:block-container id="boxB5" height="1.5in" background-color="yellow" text-align="center" display-align="center">
+ <fo:block>box5</fo:block>
+ </fo:block-container>
+ <fo:block-container id="boxB6" height="1.5in" background-color="orange" text-align="center" display-align="center">
+ <fo:block>box6</fo:block>
+ </fo:block-container>
+ <fo:block-container id="boxB7" height="1.5in" background-color="yellow" text-align="center" display-align="center">
+ <fo:block>box7</fo:block>
+ </fo:block-container>
+ <fo:block-container id="boxB8" height="1.5in" background-color="orange" text-align="center" display-align="center">
+ <fo:block>box8</fo:block>
+ </fo:block-container>
+ <fo:block-container id="boxB9" height="1.5in" background-color="yellow" text-align="center" display-align="center">
+ <fo:block>box9</fo:block>
+ </fo:block-container>
+ <fo:block-container id="boxB10" height="1.5in" background-color="orange" text-align="center" display-align="center">
+ <fo:block>box10</fo:block>
+ </fo:block-container>
+ <fo:block-container id="boxB11" height="1.5in" background-color="orange" text-align="center" display-align="center">
+ <fo:block>box11</fo:block>
+ </fo:block-container>
+ </fo:flow>
+ </fo:page-sequence>
+ </fo:root>
+ </fo>
+ <checks>
+ <eval expected="4" xpath="count(//pageSequence[1]/pageViewport)"/>
+ <eval expected="1" xpath="//block[@prod-id='boxA0']/ancestor::pageViewport/@nr"/>
+ <eval expected="2" xpath="//block[@prod-id='boxA1']/ancestor::pageViewport/@nr"/>
+ <eval expected="2" xpath="//block[@prod-id='boxA2']/ancestor::pageViewport/@nr"/>
+ <eval expected="2" xpath="//block[@prod-id='boxA3']/ancestor::pageViewport/@nr"/>
+ <eval expected="2" xpath="//block[@prod-id='boxA4']/ancestor::pageViewport/@nr"/>
+ <eval expected="3" xpath="//block[@prod-id='boxA5']/ancestor::pageViewport/@nr"/>
+ <eval expected="3" xpath="//block[@prod-id='boxA6']/ancestor::pageViewport/@nr"/>
+ <eval expected="3" xpath="//block[@prod-id='boxA7']/ancestor::pageViewport/@nr"/>
+ <eval expected="3" xpath="//block[@prod-id='boxA8']/ancestor::pageViewport/@nr"/>
+ <eval expected="4" xpath="//block[@prod-id='boxA9']/ancestor::pageViewport/@nr"/>
+ <eval expected="4" xpath="//block[@prod-id='boxA10']/ancestor::pageViewport/@nr"/>
+ <eval expected="288000" xpath="//pageViewport[@nr='1']/descendant::regionBody/@bpd"/>
+ <eval expected="288000" xpath="//pageViewport[@nr='2']/descendant::regionBody/@bpd"/>
+ <eval expected="288000" xpath="//pageViewport[@nr='3']/descendant::regionBody/@bpd"/>
+ <eval expected="144000" xpath="//pageViewport[@nr='4']/descendant::regionBody/@bpd"/>
+ <eval expected="normal" xpath="//pageViewport[@nr='1']/@simple-page-master-name"/>
+ <eval expected="normal" xpath="//pageViewport[@nr='2']/@simple-page-master-name"/>
+ <eval expected="normal" xpath="//pageViewport[@nr='3']/@simple-page-master-name"/>
+ <eval expected="last" xpath="//pageViewport[@nr='4']/@simple-page-master-name"/>
+
+ <eval expected="4" xpath="count(//pageSequence[2]/pageViewport)"/>
+ <eval expected="5" xpath="//block[@prod-id='boxB1']/ancestor::pageViewport/@nr"/>
+ <eval expected="5" xpath="//block[@prod-id='boxB2']/ancestor::pageViewport/@nr"/>
+ <eval expected="5" xpath="//block[@prod-id='boxB3']/ancestor::pageViewport/@nr"/>
+ <eval expected="5" xpath="//block[@prod-id='boxB4']/ancestor::pageViewport/@nr"/>
+ <eval expected="6" xpath="//block[@prod-id='boxB5']/ancestor::pageViewport/@nr"/>
+ <eval expected="6" xpath="//block[@prod-id='boxB6']/ancestor::pageViewport/@nr"/>
+ <eval expected="6" xpath="//block[@prod-id='boxB7']/ancestor::pageViewport/@nr"/>
+ <eval expected="6" xpath="//block[@prod-id='boxB8']/ancestor::pageViewport/@nr"/>
+ <eval expected="7" xpath="//block[@prod-id='boxB9']/ancestor::pageViewport/@nr"/>
+ <eval expected="7" xpath="//block[@prod-id='boxB10']/ancestor::pageViewport/@nr"/>
+ <eval expected="7" xpath="//block[@prod-id='boxB11']/ancestor::pageViewport/@nr"/>
+ <eval expected="288000" xpath="//pageViewport[@nr='5']/descendant::regionBody/@bpd"/>
+ <eval expected="288000" xpath="//pageViewport[@nr='6']/descendant::regionBody/@bpd"/>
+ <eval expected="288000" xpath="//pageViewport[@nr='7']/descendant::regionBody/@bpd"/>
+ <eval expected="144000" xpath="//pageViewport[@nr='8']/descendant::regionBody/@bpd"/>
+ <eval expected="normal" xpath="//pageViewport[@nr='5']/@simple-page-master-name"/>
+ <eval expected="normal" xpath="//pageViewport[@nr='6']/@simple-page-master-name"/>
+ <eval expected="normal" xpath="//pageViewport[@nr='7']/@simple-page-master-name"/>
+ <eval expected="last" xpath="//pageViewport[@nr='8']/@simple-page-master-name"/>
+ </checks>
+</testcase>