See also: http://wiki.apache.org/xmlgraphics-fop/PagePositionLast git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@388182 13f79535-47bb-0310-9956-ffa450edef68tags/fop-0_92-beta
@@ -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; | |||
} |
@@ -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 |
@@ -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); |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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"; |
@@ -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(); | |||
} | |||
@@ -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)); | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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; | |||
@@ -756,6 +832,15 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { | |||
this.lastReportedBPD = -1; | |||
} | |||
/** | |||
* 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. | |||
@@ -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. |
@@ -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); | |||
} | |||
} | |||
@@ -27,6 +27,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) |
@@ -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> |