From: Jeremias Maerki Date: Wed, 15 Jun 2005 09:08:35 +0000 (+0000) Subject: Page breaking process now respects changing available BPD (not IPD) over multiple... X-Git-Tag: fop-0_90-alpha1~561 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=84e1ae6512f685ad1850322d9a40250666d6f8f2;p=xmlgraphics-fop.git Page breaking process now respects changing available BPD (not IPD) over multiple pages. PageSequenceMaster allows backtracking. PageViewports are delivered through a PageViewportProvider class which caches PVs and handles cases where the breaking algorithm allocates PVs that turn out to be unused later (because of hard breaks which may cause blank pages, for example). git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@198746 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/fop/area/PageViewport.java b/src/java/org/apache/fop/area/PageViewport.java index cb7b524cf..4fedd05c6 100644 --- a/src/java/org/apache/fop/area/PageViewport.java +++ b/src/java/org/apache/fop/area/PageViewport.java @@ -49,6 +49,7 @@ public class PageViewport implements Resolvable, Cloneable { private boolean clip = false; private String pageNumberString = null; private SimplePageMaster spm = null; + private boolean blank; // list of id references and the rectangle on the page private Map idReferences = null; @@ -76,9 +77,12 @@ public class PageViewport implements Resolvable, Cloneable { /** * Create a page viewport. * @param spm SimplePageMaster indicating the page and region dimensions + * @param pageStr String representation of the page number + * @param blank true if this is a blank page */ - public PageViewport(SimplePageMaster spm, String pageStr) { + public PageViewport(SimplePageMaster spm, String pageStr, boolean blank) { this.spm = spm; + this.blank = blank; int pageWidth = spm.getPageWidth().getValue(); int pageHeight = spm.getPageHeight().getValue(); pageNumberString = pageStr; @@ -237,6 +241,12 @@ public class PageViewport implements Resolvable, Cloneable { if (marks == null) { return; } + if (log.isDebugEnabled()) { + log.debug("--" + marks.keySet() + ": " + + (starting ? "starting" : "ending") + + (isfirst ? ", first" : "") + + (islast ? ", last" : "")); + } // at the start of the area, register is-first and any areas if (starting) { @@ -348,10 +358,24 @@ public class PageViewport implements Resolvable, Cloneable { } break; } - log.trace("page " + pageNumberString + ": " + "Retrieving marker " + name + "at position " + posName); + if (log.isTraceEnabled()) { + log.trace("page " + pageNumberString + ": " + "Retrieving marker " + name + + " at position " + posName); + } return mark; } + /** Dumps the current marker data to the logger. */ + public void dumpMarkers() { + if (log.isTraceEnabled()) { + log.trace("FirstAny: " + this.markerFirstAny); + log.trace("FirstStart: " + this.markerFirstStart); + log.trace("LastAny: " + this.markerLastAny); + log.trace("LastEnd: " + this.markerLastEnd); + log.trace("LastStart: " + this.markerLastStart); + } + } + /** * Save the page contents to an object stream. * The map of unresolved references are set on the page so that @@ -425,6 +449,11 @@ public class PageViewport implements Resolvable, Cloneable { return spm; } + /** @return True if this is a blank page. */ + public boolean isBlank() { + return this.blank; + } + /** * Convenience method to get BodyRegion of this PageViewport * @return BodyRegion object diff --git a/src/java/org/apache/fop/fo/pagination/ConditionalPageMasterReference.java b/src/java/org/apache/fop/fo/pagination/ConditionalPageMasterReference.java index 3d737065a..dd2e8ee35 100644 --- a/src/java/org/apache/fop/fo/pagination/ConditionalPageMasterReference.java +++ b/src/java/org/apache/fop/fo/pagination/ConditionalPageMasterReference.java @@ -43,8 +43,6 @@ public class ConditionalPageMasterReference extends FObj { private int blankOrNotBlank; // End of property values - private RepeatablePageMasterAlternatives repeatablePageMasterAlternatives; - /** * @see org.apache.fop.fo.FONode#FONode(FONode) */ @@ -70,17 +68,19 @@ public class ConditionalPageMasterReference extends FObj { * @see org.apache.fop.fo.FONode#startOfNode */ protected void startOfNode() throws FOPException { - this.repeatablePageMasterAlternatives = - (RepeatablePageMasterAlternatives) parent; - this.repeatablePageMasterAlternatives.addConditionalPageMasterReference(this); + getConcreteParent().addConditionalPageMasterReference(this); } + private RepeatablePageMasterAlternatives getConcreteParent() { + return (RepeatablePageMasterAlternatives) parent; + } + /** * @see org.apache.fop.fo.FONode#validateChildNode(Locator, String, String) * XSL Content Model: empty */ protected void validateChildNode(Locator loc, String nsURI, String localName) - throws ValidationException { + throws ValidationException { invalidChildError(loc, nsURI, localName); } diff --git a/src/java/org/apache/fop/fo/pagination/PageSequence.java b/src/java/org/apache/fop/fo/pagination/PageSequence.java index dec8676d2..27a3b5b7a 100644 --- a/src/java/org/apache/fop/fo/pagination/PageSequence.java +++ b/src/java/org/apache/fop/fo/pagination/PageSequence.java @@ -433,25 +433,44 @@ public class PageSequence extends FObj { } /** - * Public accessor for determining the page master to use for any given - * page within this page sequence - * @param pageCount = the page number of the page to be created - * @param bIsFirstPage = indicator whether this page is the first page of the + * 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 * page sequence - * @param bIsBlank = indicator whether the page will be blank + * @param bIsBlank 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 getSimplePageMasterToUse(int pageCount, boolean bIsFirstPage, - boolean bIsBlank) throws FOPException { + public SimplePageMaster getNextSimplePageMaster(int page, + boolean bIsFirstPage, + boolean bIsBlank) throws FOPException { if (pageSequenceMaster == null) { return simplePageMaster; } - boolean isOddPage = ((pageCount % 2) == 1); + boolean isOddPage = ((page % 2) == 1); + if (getLogger().isDebugEnabled()) { + getLogger().debug("getNextSimplePageMaster(page=" + page + + " isOdd=" + isOddPage + + " isFirst=" + bIsFirstPage + + " isBlank=" + bIsBlank + ")"); + } return pageSequenceMaster.getNextSimplePageMaster(isOddPage, bIsFirstPage, bIsBlank); } + /** + * Used to set the "cursor position" for the page masters to the previous item. + * @return true if there is a previous item, false if the current one was the first one. + */ + public boolean goToPreviousSimplePageMaster() { + if (pageSequenceMaster == null) { + return true; + } else { + return pageSequenceMaster.goToPreviousSimplePageMaster(); + } + } + /** * 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 2aad43894..32e865622 100644 --- a/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java +++ b/src/java/org/apache/fop/fo/pagination/PageSequenceMaster.java @@ -83,8 +83,8 @@ public class PageSequenceMaster extends FObj { */ protected void endOfNode() throws FOPException { if (childNodes == null) { - missingChildElementError("(single-page-master-reference|" + - "repeatable-page-master-reference|repeatable-page-master-alternatives)+"); + missingChildElementError("(single-page-master-reference|" + + "repeatable-page-master-reference|repeatable-page-master-alternatives)+"); } } @@ -121,7 +121,7 @@ public class PageSequenceMaster extends FObj { private SubSequenceSpecifier getNextSubSequence() { currentSubSequenceNumber++; if (currentSubSequenceNumber >= 0 - && currentSubSequenceNumber < subSequenceSpecifiers.size()) { + && currentSubSequenceNumber < subSequenceSpecifiers.size()) { return (SubSequenceSpecifier)subSequenceSpecifiers .get(currentSubSequenceNumber); } @@ -139,6 +139,26 @@ public class PageSequenceMaster extends FObj { } } + /** + * Used to set the "cursor position" for the page masters to the previous item. + * @return true if there is a previous item, false if the current one was the first one. + */ + public boolean goToPreviousSimplePageMaster() { + if (currentSubSequence != null) { + boolean success = currentSubSequence.goToPrevious(); + if (!success) { + if (currentSubSequenceNumber > 0) { + currentSubSequenceNumber--; + currentSubSequence = (SubSequenceSpecifier)subSequenceSpecifiers + .get(currentSubSequenceNumber); + } else { + currentSubSequence = null; + } + } + } + return (currentSubSequence != null); + } + /** * Returns the next simple-page-master. * @param isOddPage True if the next page number is odd diff --git a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterAlternatives.java index 546107929..64288457a 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-2004 The Apache Software Foundation. + * Copyright 1999-2005 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. @@ -19,7 +19,7 @@ package org.apache.fop.fo.pagination; // Java -import java.util.ArrayList; +import java.util.List; import org.xml.sax.Locator; @@ -46,7 +46,7 @@ public class RepeatablePageMasterAlternatives extends FObj private int numberConsumed = 0; - private ArrayList conditionalPageMasterRefs; + private List conditionalPageMasterRefs; /** * @see org.apache.fop.fo.FONode#FONode(FONode) @@ -66,7 +66,7 @@ public class RepeatablePageMasterAlternatives extends FObj * @see org.apache.fop.fo.FONode#startOfNode */ protected void startOfNode() throws FOPException { - conditionalPageMasterRefs = new ArrayList(); + conditionalPageMasterRefs = new java.util.ArrayList(); if (parent.getName().equals("fo:page-sequence-master")) { PageSequenceMaster pageSequenceMaster = (PageSequenceMaster)parent; @@ -99,9 +99,7 @@ public class RepeatablePageMasterAlternatives extends FObj } } - /** - * Return the "maximum-repeats" property. - */ + /** @return the "maximum-repeats" property. */ public int getMaximumRepeats() { if (maximumRepeats.getEnum() == EN_NO_LIMIT) { return INFINITE; @@ -151,20 +149,27 @@ public class RepeatablePageMasterAlternatives extends FObj this.conditionalPageMasterRefs.add(cpmr); } - /** - * @see org.apache.fop.fo.pagination.SubSequenceSpecifier#reset() - */ + /** @see org.apache.fop.fo.pagination.SubSequenceSpecifier#reset() */ public void reset() { this.numberConsumed = 0; } + /** @see org.apache.fop.fo.pagination.SubSequenceSpecifier#goToPrevious() */ + public boolean goToPrevious() { + if (numberConsumed == 0) { + return false; + } else { + numberConsumed--; + return true; + } + } + + /** @see org.apache.fop.fo.FONode#getName() */ public String getName() { return "fo:repeatable-page-master-alternatives"; } - /** - * @see org.apache.fop.fo.FObj#getNameId() - */ + /** @see org.apache.fop.fo.FObj#getNameId() */ 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 efe3b16e1..8f714aac9 100644 --- a/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java +++ b/src/java/org/apache/fop/fo/pagination/RepeatablePageMasterReference.java @@ -43,7 +43,6 @@ public class RepeatablePageMasterReference extends FObj private static final int INFINITE = -1; - private PageSequenceMaster pageSequenceMaster; private int numberConsumed = 0; /** @@ -103,9 +102,7 @@ public class RepeatablePageMasterReference extends FObj return masterReference; } - /** - * Return the "maximum-repeats" property. - */ + /** @return the "maximum-repeats" property. */ public int getMaximumRepeats() { if (maximumRepeats.getEnum() == EN_NO_LIMIT) { return INFINITE; @@ -120,23 +117,28 @@ public class RepeatablePageMasterReference extends FObj } } - /** - * @see org.apache.fop.fo.pagination.SubSequenceSpecifier#reset() - */ + /** @see org.apache.fop.fo.pagination.SubSequenceSpecifier#reset() */ public void reset() { this.numberConsumed = 0; } - /** - * @see org.apache.fop.fo.FObj#getName() - */ + + /** @see org.apache.fop.fo.pagination.SubSequenceSpecifier#goToPrevious() */ + public boolean goToPrevious() { + if (numberConsumed == 0) { + return false; + } else { + numberConsumed--; + return true; + } + } + + /** @see org.apache.fop.fo.FObj#getName() */ public String getName() { return "fo:repeatable-page-master-reference"; } - /** - * @see org.apache.fop.fo.FObj#getNameId() - */ + /** @see org.apache.fop.fo.FObj#getNameId() */ 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 68a08259a..1d2a7cd6a 100644 --- a/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java +++ b/src/java/org/apache/fop/fo/pagination/SinglePageMasterReference.java @@ -80,9 +80,7 @@ public class SinglePageMasterReference extends FObj invalidChildError(loc, nsURI, localName); } - /** - * @see org.apache.fop.fo.pagination.SubSequenceSpecifier - */ + /** @see org.apache.fop.fo.pagination.SubSequenceSpecifier */ public String getNextPageMasterName(boolean isOddPage, boolean isFirstPage, boolean isEmptyPage) { @@ -94,20 +92,29 @@ public class SinglePageMasterReference extends FObj } } - /** - * @see org.apache.fop.fo.pagination.SubSequenceSpecifier#reset() - */ + /** @see org.apache.fop.fo.pagination.SubSequenceSpecifier#reset() */ public void reset() { this.state = FIRST; } + + + /** @see org.apache.fop.fo.pagination.SubSequenceSpecifier#goToPrevious() */ + public boolean goToPrevious() { + if (state == FIRST) { + return false; + } else { + this.state = FIRST; + return true; + } + } + + /** @see org.apache.fop.fo.FONode#getName() */ public String getName() { return "fo:single-page-master-reference"; } - /** - * @see org.apache.fop.fo.FObj#getNameId() - */ + /** @see org.apache.fop.fo.FObj#getNameId() */ public int getNameId() { return FO_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 67b5cc303..39609bb05 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-2004 The Apache Software Foundation. + * Copyright 1999-2005 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. @@ -45,5 +45,11 @@ public interface SubSequenceSpecifier { */ void reset(); + /** + * Used to set the "cursor position" to the previous item. + * @return true if there is a previous item, false if the current one was the first one. + */ + boolean goToPrevious(); + } diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java index 0a0c1eeb2..99dff32ba 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java @@ -105,6 +105,16 @@ public abstract class AbstractBreaker { protected abstract LayoutManager getTopLevelLM(); protected abstract LayoutManager getCurrentChildLM(); + /** + * Returns the PageViewportProvider if any. PageBreaker overrides this method because each + * page may have a different available BPD which needs to be accessible to the breaking + * algorithm. + * @return the applicable PageViewportProvider, or null if not applicable + */ + protected PageSequenceLayoutManager.PageViewportProvider getPageViewportProvider() { + return null; + } + /* * This method is to contain the logic to determine the LM's * getNextKnuthElements() implementation(s) that are to be called. @@ -171,6 +181,7 @@ public abstract class AbstractBreaker { log.debug("PLM> start of algorithm (" + this.getClass().getName() + "), flow BPD =" + flowBPD); PageBreakingAlgorithm alg = new PageBreakingAlgorithm(getTopLevelLM(), + getPageViewportProvider(), alignment, alignmentLast, footnoteSeparatorLength); int iOptPageNumber; @@ -184,8 +195,9 @@ public abstract class AbstractBreaker { } //iOptPageNumber = alg.firstFit(effectiveList, flowBPD, 1, true); - iOptPageNumber = alg.findBreakingPoints(effectiveList, flowBPD, 1, - true, true); + alg.setConstantLineWidth(flowBPD); + iOptPageNumber = alg.findBreakingPoints(effectiveList, /*flowBPD,*/ + 1, true, true); log.debug("PLM> iOptPageNumber= " + iOptPageNumber + " pageBreaks.size()= " + alg.getPageBreaks().size()); @@ -413,8 +425,9 @@ public abstract class AbstractBreaker { */ private BlockSequence justifyBoxes(BlockSequence blockList, PageBreakingAlgorithm alg, int availableBPD) { int iOptPageNumber; - iOptPageNumber = alg.findBreakingPoints(blockList, availableBPD, 1, - true, true); + alg.setConstantLineWidth(availableBPD); + iOptPageNumber = alg.findBreakingPoints(blockList, /*availableBPD,*/ + 1, true, true); log.debug("PLM> iOptPageNumber= " + iOptPageNumber); // diff --git a/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java index e7098e330..1da5ee4d7 100644 --- a/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java +++ b/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java @@ -58,9 +58,10 @@ public abstract class BreakingAlgorithm { protected KnuthSequence par; /** - * The width of a line. + * The width of a line (or height of a column in page-breaking mode). + * -1 indicates that the line widths are different for each line. */ - protected int lineWidth = 0; + protected int lineWidth = -1; private boolean force = false; protected KnuthNode lastDeactivatedNode = null; @@ -287,13 +288,17 @@ public abstract class BreakingAlgorithm { KnuthSequence sequence, int total) ; - public int findBreakingPoints(KnuthSequence par, int lineWidth, + public void setConstantLineWidth(int lineWidth) { + this.lineWidth = lineWidth; + } + + public int findBreakingPoints(KnuthSequence par, /*int lineWidth,*/ double threshold, boolean force, boolean hyphenationAllowed) { this.par = par; this.threshold = threshold; this.force = force; - this.lineWidth = lineWidth; + //this.lineWidth = lineWidth; initialize(); activeLines = new KnuthNode[20]; @@ -567,7 +572,7 @@ public abstract class BreakingAlgorithm { if (element.isPenalty()) { actualWidth += element.getW(); } - return lineWidth - actualWidth; + return getLineWidth() - actualWidth; } /** @@ -768,6 +773,18 @@ public abstract class BreakingAlgorithm { return positions[line].position; } + protected int getLineWidth(int line) { + if (this.lineWidth < 0) { + throw new IllegalStateException("lineWidth must be set"); + } else { + return this.lineWidth; + } + } + + protected int getLineWidth() { + return this.lineWidth; + } + /** * Return a string representation of a MinOptMax in the form of a * "width+stretch-shrink". Useful only for debugging. diff --git a/src/java/org/apache/fop/layoutmgr/LineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/LineLayoutManager.java index 13befec2c..7fe0c3e29 100644 --- a/src/java/org/apache/fop/layoutmgr/LineLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/LineLayoutManager.java @@ -418,10 +418,11 @@ public class LineLayoutManager extends InlineStackingLayoutManager } } - public int findBreakingPoints(Paragraph par, int lineWidth, + public int findBreakingPoints(Paragraph par, /*int lineWidth,*/ double threshold, boolean force, boolean hyphenationAllowed) { - return super.findBreakingPoints(par, lineWidth, threshold, force, hyphenationAllowed); + return super.findBreakingPoints(par, /*lineWidth,*/ + threshold, force, hyphenationAllowed); } protected int filterActiveNodes() { @@ -832,8 +833,8 @@ public class LineLayoutManager extends InlineStackingLayoutManager // first try boolean bHyphenationAllowed = false; + alg.setConstantLineWidth(iLineWidth); iBPcount = alg.findBreakingPoints(currPar, - iLineWidth, maxAdjustment, false, bHyphenationAllowed); if (iBPcount == 0 || alignment == EN_JUSTIFY) { // if the first try found a set of breaking points, save them @@ -857,7 +858,6 @@ public class LineLayoutManager extends InlineStackingLayoutManager if ((iBPcount = alg.findBreakingPoints(currPar, - iLineWidth, maxAdjustment, false, bHyphenationAllowed)) == 0) { // the second try failed too, try with a huge threshold // and force the algorithm to find @@ -867,7 +867,6 @@ public class LineLayoutManager extends InlineStackingLayoutManager maxAdjustment = 20; iBPcount = alg.findBreakingPoints(currPar, - iLineWidth, maxAdjustment, true, bHyphenationAllowed); } @@ -887,13 +886,11 @@ public class LineLayoutManager extends InlineStackingLayoutManager // try with shorter lines int savedLineWidth = iLineWidth; iLineWidth = (int) (iLineWidth * 0.95); - iBPcount - = alg.findBreakingPoints(currPar, - iLineWidth, - maxAdjustment, true, bHyphenationAllowed); - // use normal lines, when possible - lineLayouts.restorePossibilities(); - iLineWidth = savedLineWidth; + iBPcount = alg.findBreakingPoints(currPar, + maxAdjustment, true, bHyphenationAllowed); + // use normal lines, when possible + lineLayouts.restorePossibilities(); + iLineWidth = savedLineWidth; } if (!lineLayouts.canUseLessLines()) { alg.resetAlgorithm(); @@ -901,13 +898,12 @@ public class LineLayoutManager extends InlineStackingLayoutManager // try with longer lines int savedLineWidth = iLineWidth; iLineWidth = (int) (iLineWidth * 1.05); - iBPcount - = alg.findBreakingPoints(currPar, - iLineWidth, - maxAdjustment, true, bHyphenationAllowed); - // use normal lines, when possible - lineLayouts.restorePossibilities(); - iLineWidth = savedLineWidth; + alg.setConstantLineWidth(iLineWidth); + iBPcount = alg.findBreakingPoints(currPar, + maxAdjustment, true, bHyphenationAllowed); + // use normal lines, when possible + lineLayouts.restorePossibilities(); + iLineWidth = savedLineWidth; } //System.out.println("LLM.getNextKnuthElements> now, layouts with more lines? " + lineLayouts.canUseMoreLines()); //System.out.println(" now, layouts with fewer lines? " + lineLayouts.canUseLessLines()); diff --git a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java index e48675c95..0f00a9685 100644 --- a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java +++ b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java @@ -22,12 +22,14 @@ import java.util.ArrayList; import java.util.LinkedList; import java.util.ListIterator; +import org.apache.fop.area.PageViewport; import org.apache.fop.layoutmgr.AbstractBreaker.PageBreakPosition; import org.apache.fop.traits.MinOptMax; class PageBreakingAlgorithm extends BreakingAlgorithm { private LayoutManager topLevelLM; + private PageSequenceLayoutManager.PageViewportProvider pvProvider; private LinkedList pageBreaks = null; private ArrayList footnotesList = null; @@ -62,10 +64,12 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { private boolean storedValue = false; public PageBreakingAlgorithm(LayoutManager topLevelLM, + PageSequenceLayoutManager.PageViewportProvider pvProvider, int alignment, int alignmentLast, MinOptMax fnSeparatorLength) { super(alignment, alignmentLast, true); this.topLevelLM = topLevelLM; + this.pvProvider = pvProvider; best = new BestPageRecords(); footnoteSeparatorLength = (MinOptMax) fnSeparatorLength.clone(); // add some stretch, to avoid a restart for every page containing footnotes @@ -274,7 +278,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { // this page contains some footnote citations // add the footnote separator width actualWidth += footnoteSeparatorLength.opt; - if (actualWidth + allFootnotes <= lineWidth) { + if (actualWidth + allFootnotes <= getLineWidth()) { // there is enough space to insert all footnotes: // add the whole allFootnotes length actualWidth += allFootnotes; @@ -284,7 +288,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { } else if (((bCanDeferOldFootnotes = canDeferOldFootnotes((KnuthPageNode) activeNode, elementIndex)) || bNewFootnotes) && (footnoteSplit = getFootnoteSplit((KnuthPageNode) activeNode, - lineWidth - actualWidth, + getLineWidth() - actualWidth, bCanDeferOldFootnotes)) > 0) { // it is allowed to break or even defer footnotes if either: // - there are new footnotes in the last piece of content, and @@ -313,7 +317,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { } else { // there are no footnotes } - return lineWidth - actualWidth; + return getLineWidth(activeNode.line) - actualWidth; } private boolean canDeferOldFootnotes(KnuthPageNode node, int contentElementIndex) { @@ -594,7 +598,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { insertedFootnotesLength = lastNode.totalFootnotes; footnoteListIndex = lastNode.footnoteListIndex; footnoteElementIndex = lastNode.footnoteElementIndex; - int availableBPD = lineWidth; + int availableBPD = getLineWidth(); int split = 0; KnuthPageNode prevNode = lastNode; @@ -627,7 +631,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { removeNode(prevNode.line, prevNode); prevNode = node; - availableBPD = lineWidth; + availableBPD = getLineWidth(); } } // create the last node @@ -741,4 +745,19 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { public LinkedList getFootnoteList(int index) { return (LinkedList) footnotesList.get(index); } + + /** @see org.apache.fop.layoutmgr.BreakingAlgorithm#getLineWidth(int) */ + protected int getLineWidth(int line) { + int bpd; + if (pvProvider != null) { + PageViewport pv = pvProvider.getPageViewport( + false, line, + PageSequenceLayoutManager.PageViewportProvider.RELTO_CURRENT_ELEMENT_LIST); + bpd = pv.getBodyRegion().getBPD(); + } else { + bpd = super.getLineWidth(line); + } + return bpd; + } + } diff --git a/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java b/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java index 843d13e11..07e6dfdd8 100644 --- a/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java @@ -63,6 +63,8 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { */ private PageSequence pageSeq; + private PageViewportProvider pvProvider; + /** * Current page-viewport-area being filled by * the PSLM. @@ -90,6 +92,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { super(pseq); this.areaTreeHandler = ath; this.pageSeq = pseq; + this.pvProvider = new PageViewportProvider(this.pageSeq); } /** @@ -121,7 +124,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { areaTreeHandler.getAreaTreeModel().startPageSequence(title); log.debug("Starting layout"); - curPV = makeNewPage(false, true, false); + curPV = makeNewPage(false, false); Flow mainFlow = pageSeq.getMainFlow(); childFLM = getLayoutManagerMaker(). @@ -160,6 +163,11 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { return null; // unneeded for PSLM } + /** @see org.apache.fop.layoutmgr.AbstractBreaker#getPageViewportProvider() */ + protected PageSequenceLayoutManager.PageViewportProvider getPageViewportProvider() { + return pvProvider; + } + protected LinkedList getNextKnuthElements(LayoutContext context, int alignment) { LinkedList contentList = null; @@ -263,6 +271,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { handleBreakTrait(bIsFirstPage ? list.getStartOn() : Constants.EN_PAGE); } } + pvProvider.setStartPageOfNextElementList(currentPageNum); } // add static areas and resolve any new id areas // finish page and add to area tree @@ -425,32 +434,15 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { } } - private PageViewport makeNewPage(boolean bIsBlank, boolean bIsFirst, boolean bIsLast) { + private PageViewport makeNewPage(boolean bIsBlank, boolean bIsLast) { if (curPV != null) { finishPage(); } currentPageNum++; - String pageNumberString = pageSeq.makeFormattedPageNumber(currentPageNum); - try { - // create a new page - SimplePageMaster spm = pageSeq.getSimplePageMasterToUse( - currentPageNum, bIsFirst, bIsBlank); - - Region body = spm.getRegion(FO_REGION_BODY); - if (!pageSeq.getMainFlow().getFlowName().equals(body.getRegionName())) { - // this is fine by the XSL Rec (fo:flow's flow-name can be mapped to - // any region), but we don't support it yet. - throw new FOPException("Flow '" + pageSeq.getMainFlow().getFlowName() - + "' does not map to the region-body in page-master '" - + spm.getMasterName() + "'. FOP presently " - + "does not support this."); - } - curPV = new PageViewport(spm, pageNumberString); - } catch (FOPException fopex) { - throw new IllegalArgumentException("Cannot create page: " + fopex.getMessage()); - } + curPV = pvProvider.getPageViewport(bIsBlank, + currentPageNum, PageViewportProvider.RELTO_PAGE_SEQUENCE); if (log.isDebugEnabled()) { log.debug("[" + curPV.getPageNumberString() + (bIsBlank ? "*" : "") + "]"); @@ -475,6 +467,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { } private void finishPage() { + curPV.dumpMarkers(); // Layout side regions layoutSideRegion(FO_REGION_BEFORE); layoutSideRegion(FO_REGION_AFTER); @@ -504,17 +497,17 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { if (curPV.getCurrentSpan().hasMoreFlows()) { curPV.getCurrentSpan().moveToNextFlow(); } else { - curPV = makeNewPage(false, false, false); + curPV = makeNewPage(false, false); } return; } log.debug("handling break-before after page " + currentPageNum + " breakVal=" + breakVal); if (needBlankPageBeforeNew(breakVal)) { - curPV = makeNewPage(true, false, false); + curPV = makeNewPage(true, false); } if (needNewPage(breakVal)) { - curPV = makeNewPage(false, false, false); + curPV = makeNewPage(false, false); } } @@ -556,4 +549,89 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { return true; } } + + + public class PageViewportProvider { + + public static final int RELTO_PAGE_SEQUENCE = 0; + public static final int RELTO_CURRENT_ELEMENT_LIST = 1; + + private int startPageOfPageSequence; + private int startPageOfCurrentElementList; + private List cachedPageViewports = new java.util.ArrayList(); + + public PageViewportProvider(PageSequence ps) { + this.startPageOfPageSequence = ps.getStartingPageNumber(); + } + + public void setStartPageOfNextElementList(int startPage) { + log.debug("start page of the next element list is: " + startPage); + this.startPageOfCurrentElementList = startPage; + } + + public PageViewport getPageViewport(boolean bIsBlank, int index, int relativeTo) { + if (relativeTo == RELTO_PAGE_SEQUENCE) { + return getPageViewport(bIsBlank, index); + } else if (relativeTo == RELTO_CURRENT_ELEMENT_LIST) { + int effIndex = startPageOfCurrentElementList + index; + effIndex += startPageOfPageSequence; + return getPageViewport(bIsBlank, effIndex); + } else { + throw new IllegalArgumentException( + "Illegal value for relativeTo: " + relativeTo); + } + } + + private PageViewport getPageViewport(boolean bIsBlank, int index) { + //System.out.println("getPageViewport(" + index + " " + bIsBlank); + log.debug("getPageViewport(" + index + " " + bIsBlank); + int intIndex = index - startPageOfPageSequence; + if (bIsBlank) { + log.debug("blank page requested: " + index); + } + while (intIndex >= cachedPageViewports.size()) { + //System.out.println("Caching " + index); + log.debug("Caching " + index); + cacheNextPageViewport(index, bIsBlank); + } + PageViewport pv = (PageViewport)cachedPageViewports.get(intIndex); + if (pv.isBlank() != bIsBlank) { + log.debug("blank condition doesn't match. Replacing PageViewport."); + while (intIndex < cachedPageViewports.size()) { + this.cachedPageViewports.remove(cachedPageViewports.size() - 1); + if (!pageSeq.goToPreviousSimplePageMaster()) { + log.warn("goToPreviousSimplePageMaster() on the first page called!"); + } + } + cacheNextPageViewport(index, bIsBlank); + //throw new IllegalStateException("blank condition doesn't match. Check code!"); + } + return pv; + } + + private void cacheNextPageViewport(int index, boolean bIsBlank) { + try { + String pageNumberString = pageSeq.makeFormattedPageNumber(index); + SimplePageMaster spm = pageSeq.getNextSimplePageMaster( + index, (startPageOfPageSequence == index), bIsBlank); + + Region body = spm.getRegion(FO_REGION_BODY); + if (!pageSeq.getMainFlow().getFlowName().equals(body.getRegionName())) { + // this is fine by the XSL Rec (fo:flow's flow-name can be mapped to + // any region), but we don't support it yet. + throw new FOPException("Flow '" + pageSeq.getMainFlow().getFlowName() + + "' does not map to the region-body in page-master '" + + spm.getMasterName() + "'. FOP presently " + + "does not support this."); + } + PageViewport pv = new PageViewport(spm, pageNumberString, bIsBlank); + cachedPageViewports.add(pv); + } catch (FOPException e) { + //TODO Maybe improve. It'll mean to propagate this exception up several + //methods calls. + throw new IllegalStateException(e.getMessage()); + } + } + + } }