From 97564f21e19e5a726a2f011b1a03ea73444626f4 Mon Sep 17 00:00:00 2001 From: Jeremias Maerki Date: Thu, 23 Mar 2006 14:45:17 +0000 Subject: [PATCH] Initial support for page-position="last". Feedback requested! 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-ffa450edef68 --- .../ConditionalPageMasterReference.java | 29 ++- .../fop/fo/pagination/PageSequence.java | 27 ++- .../fop/fo/pagination/PageSequenceMaster.java | 17 +- .../RepeatablePageMasterAlternatives.java | 27 ++- .../RepeatablePageMasterReference.java | 9 +- .../pagination/SinglePageMasterReference.java | 8 +- .../fo/pagination/SubSequenceSpecifier.java | 7 +- .../apache/fop/layoutmgr/AbstractBreaker.java | 18 +- .../fop/layoutmgr/BreakingAlgorithm.java | 11 + .../layoutmgr/PageSequenceLayoutManager.java | 229 +++++++++++++----- .../org/apache/fop/render/rtf/RTFHandler.java | 2 +- status.xml | 3 + .../page-position_last_1.xml | 168 +++++++++++++ 13 files changed, 458 insertions(+), 97 deletions(-) create mode 100644 test/layoutengine/standard-testcases/page-position_last_1.xml 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; @@ -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 ? " " : "") + ")"); } 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 @@ -27,6 +27,9 @@ + + Initial support for page-position="last" added. + Reenabled loading of user-supplied hyphenation patterns that was available in FOP 0.20.5. (See "hyphenation-base" option in the user configuration) 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 @@ + + + + + +

+ This test checks page-position="last". +

+
+ + + + + + + + + + + + + + + + + + + + + page of + + + + box0 + + + box1 + + + box2 + + + box3 + + + box4 + + + box5 + + + box6 + + + box7 + + + box8 + + + box9 + + + box10 + + + + + + + page of + + + + box1 + + + box2 + + + box3 + + + box4 + + + box5 + + + box6 + + + box7 + + + box8 + + + box9 + + + box10 + + + box11 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-- 2.39.5