From 1b0fdf72a4abad2c9e8f8878bf257313321855a9 Mon Sep 17 00:00:00 2001 From: Jeremias Maerki Date: Thu, 23 Jun 2005 15:01:14 +0000 Subject: [PATCH] First parts on a page which don't fit are moved to the next page. A counter avoids endless loops. Fixes normal-breaking5.xml. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@198768 13f79535-47bb-0310-9956-ffa450edef68 --- src/java/org/apache/fop/area/Page.java | 17 +++- .../apache/fop/layoutmgr/AbstractBreaker.java | 22 ++++- .../BlockContainerLayoutManager.java | 6 ++ .../fop/layoutmgr/BreakingAlgorithm.java | 81 ++++++++++++++----- .../fop/layoutmgr/LineLayoutManager.java | 2 +- .../fop/layoutmgr/PageBreakingAlgorithm.java | 5 +- .../layoutmgr/PageSequenceLayoutManager.java | 5 ++ .../layoutmgr/StaticContentLayoutManager.java | 6 ++ 8 files changed, 119 insertions(+), 25 deletions(-) diff --git a/src/java/org/apache/fop/area/Page.java b/src/java/org/apache/fop/area/Page.java index 1733f4879..2f327b55a 100644 --- a/src/java/org/apache/fop/area/Page.java +++ b/src/java/org/apache/fop/area/Page.java @@ -55,6 +55,9 @@ public class Page implements Serializable, Cloneable { // temporary map of unresolved objects used when serializing the page private HashMap unresolved = null; + /** Set to true to make this page behave as if it were not empty. */ + private boolean fakeNonEmpty = false; + /** * Empty constructor, for cloning */ @@ -109,6 +112,13 @@ public class Page implements Serializable, Cloneable { } } + /** + * Call this method to force this page to pretend not to be empty. + */ + public void fakeNonEmpty() { + this.fakeNonEmpty = true; + } + /** * Creates a RegionViewport Area object for this pagination Region. * @param reldims relative dimensions @@ -197,10 +207,11 @@ public class Page implements Serializable, Cloneable { * @return whether any FOs have been added to the body region */ public boolean isEmpty() { - if (regionBody == null) { + if (fakeNonEmpty) { + return false; + } else if (regionBody == null) { return true; - } - else { + } else { BodyRegion body = (BodyRegion)regionBody.getRegionReference(); return body.isEmpty(); } diff --git a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java index b6f2eda24..7b5a2eb5a 100644 --- a/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java +++ b/src/java/org/apache/fop/layoutmgr/AbstractBreaker.java @@ -105,6 +105,15 @@ public abstract class AbstractBreaker { protected abstract LayoutManager getTopLevelLM(); protected abstract LayoutManager getCurrentChildLM(); + /** + * Controls the behaviour of the algorithm in cases where the first element of a part + * overflows a line/page. + * @return true if the algorithm should try to send the element to the next line/page. + */ + protected boolean isPartOverflowRecoveryActivated() { + return true; + } + /** * 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 @@ -131,6 +140,13 @@ public abstract class AbstractBreaker { //nop } + /** + * This method is called when no content is available for a part. Used to force empty pages. + */ + protected void handleEmptyContent() { + //nop + } + protected abstract void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp); protected LayoutContext createLayoutContext() { @@ -182,7 +198,8 @@ public abstract class AbstractBreaker { + "), flow BPD =" + flowBPD); PageBreakingAlgorithm alg = new PageBreakingAlgorithm(getTopLevelLM(), getPageViewportProvider(), - alignment, alignmentLast, footnoteSeparatorLength); + alignment, alignmentLast, footnoteSeparatorLength, + isPartOverflowRecoveryActivated()); int iOptPageCount; BlockSequence effectiveList; @@ -328,6 +345,9 @@ public abstract class AbstractBreaker { addAreas(new KnuthPossPosIter(effectiveList, startElementIndex, endElementIndex + 1), childLC); + } else { + //no content for this part + handleEmptyContent(); } finishPart(alg, pbp); diff --git a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java index ed855afe4..001fdfbb6 100644 --- a/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/BlockContainerLayoutManager.java @@ -447,6 +447,12 @@ public class BlockContainerLayoutManager extends BlockStackingLayoutManager { this.ipd = ipd; } + /** @see org.apache.fop.layoutmgr.AbstractBreaker#isPartOverflowRecoveryActivated() */ + protected boolean isPartOverflowRecoveryActivated() { + //For block-containers, this must be disabled because of wanted overflow. + return false; + } + public int getDifferenceOfFirstPart() { PageBreakPosition pbp = (PageBreakPosition)this.deferredAlg.getPageBreaks().getFirst(); return pbp.difference; diff --git a/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java index 30deec540..a09680821 100644 --- a/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java +++ b/src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java @@ -21,6 +21,7 @@ package org.apache.fop.layoutmgr; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.fop.layoutmgr.PageBreakingAlgorithm.KnuthPageNode; import org.apache.fop.traits.MinOptMax; /** @@ -45,6 +46,8 @@ public abstract class BreakingAlgorithm { protected static final int INFINITE_RATIO = 1000; + private static final int MAX_RECOVERY_ATTEMPTS = 50; + // parameters of Knuth's algorithm: // penalty value for flagged penalties private int flaggedPenalty = 50; @@ -117,57 +120,66 @@ public abstract class BreakingAlgorithm { protected BestRecords best; private KnuthNode[] positions; + /** @see isPartOverflowRecoveryActivated() */ + private boolean partOverflowRecoveryActivated = true; + public BreakingAlgorithm(int align, int alignLast, - boolean first) { + boolean first, boolean partOverflowRecovery) { alignment = align; alignmentLast = alignLast; bFirst = first; + this.partOverflowRecoveryActivated = partOverflowRecovery; this.best = new BestRecords(); } // this class represent a feasible breaking point protected class KnuthNode { - // index of the breakpoint represented by this node + /** index of the breakpoint represented by this node */ public int position; - // number of the line ending at this breakpoint + /** number of the line ending at this breakpoint */ public int line; - // fitness class of the line ending at his breakpoint + /** fitness class of the line ending at his breakpoint */ public int fitness; - // accumulated width of the KnuthElements + /** accumulated width of the KnuthElements */ public int totalWidth; - // accumulated stretchability of the KnuthElements + /** accumulated stretchability of the KnuthElements */ public int totalStretch; - // accumulated shrinkability of the KnuthElements + /** accumulated shrinkability of the KnuthElements */ public int totalShrink; - // adjustment ratio if the line ends at this breakpoint + /** adjustment ratio if the line ends at this breakpoint */ public double adjustRatio; - // available stretch of the line ending at this breakpoint + /** available stretch of the line ending at this breakpoint */ public int availableShrink; - // available shrink of the line ending at this breakpoint + /** available shrink of the line ending at this breakpoint */ public int availableStretch; - // difference between target and actual line width + /** difference between target and actual line width */ public int difference; - // minimum total demerits up to this breakpoint + /** minimum total demerits up to this breakpoint */ public double totalDemerits; - // best node for the preceding breakpoint + /** best node for the preceding breakpoint */ public KnuthNode previous; - // next possible node in the same line + /** next possible node in the same line */ public KnuthNode next; - + /** + * Holds the number of subsequent recovery attempty that are made to get content fit + * into a line. + */ + public int fitRecoveryCounter = 0; + public KnuthNode(int position, int line, int fitness, int totalWidth, int totalStretch, int totalShrink, double adjustRatio, int availableShrink, int availableStretch, int difference, @@ -219,7 +231,7 @@ public abstract class BreakingAlgorithm { int availableShrink, int availableStretch, int difference, int fitness) { if (demerits > bestDemerits[fitness]) { - log.error("New demerits value greter than the old one"); + log.error("New demerits value greater than the old one"); } bestDemerits[fitness] = demerits; bestNode[fitness] = node; @@ -282,7 +294,22 @@ public abstract class BreakingAlgorithm { } } - + /** + * @return the number of times the algorithm should try to move overflowing content to the + * next line/page. + */ + protected int getMaxRecoveryAttempts() { + return MAX_RECOVERY_ATTEMPTS; + } + + /** + * Controls the behaviour of the algorithm in cases where the first element of a part + * overflows a line/page. + * @return true if the algorithm should try to send the element to the next line/page. + */ + protected boolean isPartOverflowRecoveryActivated() { + return this.partOverflowRecoveryActivated; + } public abstract void updateData1(int total, double demerits) ; @@ -367,7 +394,25 @@ public abstract class BreakingAlgorithm { return 0; } if (lastTooShort == null || lastForced.position == lastTooShort.position) { - lastForced = lastTooLong; + if (isPartOverflowRecoveryActivated()) { + // content would overflow, insert empty line/page and try again + KnuthNode node = createNode( + lastTooLong.previous.position, lastTooLong.previous.line + 1, 1, + 0, 0, 0, + 0, 0, 0, + 0, 0, lastTooLong.previous); + lastForced = node; + node.fitRecoveryCounter = lastTooLong.previous.fitRecoveryCounter + 1; + log.debug("first part doesn't fit into line, recovering: " + + node.fitRecoveryCounter); + if (node.fitRecoveryCounter > getMaxRecoveryAttempts()) { + throw new RuntimeException("Some content could not fit " + + "into a line/page after " + getMaxRecoveryAttempts() + + " attempts. Giving up to avoid an endless loop."); + } + } else { + lastForced = lastTooLong; + } } else { lastForced = lastTooShort; } diff --git a/src/java/org/apache/fop/layoutmgr/LineLayoutManager.java b/src/java/org/apache/fop/layoutmgr/LineLayoutManager.java index 7fe0c3e29..14a9709a0 100644 --- a/src/java/org/apache/fop/layoutmgr/LineLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/LineLayoutManager.java @@ -273,7 +273,7 @@ public class LineLayoutManager extends InlineStackingLayoutManager int indent, int fillerWidth, int lh, int ld, int fl, int ms, boolean first, LineLayoutManager llm) { - super(textAlign, textAlignLast, first); + super(textAlign, textAlignLast, first, false); pageAlignment = pageAlign; textIndent = indent; fillerMinWidth = fillerWidth; diff --git a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java index ec1f8860a..b96d2ad78 100644 --- a/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java +++ b/src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java @@ -67,8 +67,9 @@ class PageBreakingAlgorithm extends BreakingAlgorithm { public PageBreakingAlgorithm(LayoutManager topLevelLM, PageSequenceLayoutManager.PageViewportProvider pvProvider, int alignment, int alignmentLast, - MinOptMax fnSeparatorLength) { - super(alignment, alignmentLast, true); + MinOptMax fnSeparatorLength, + boolean partOverflowRecovery) { + super(alignment, alignmentLast, true, partOverflowRecovery); this.topLevelLM = topLevelLM; this.pvProvider = pvProvider; best = new BestPageRecords(); diff --git a/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java b/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java index 59f07bf93..2a097cbf8 100644 --- a/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java @@ -286,6 +286,11 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager { firstPart = false; } + /** @see org.apache.fop.layoutmgr.AbstractBreaker#handleEmptyContent() */ + protected void handleEmptyContent() { + curPV.getPage().fakeNonEmpty(); + } + protected void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp) { // add footnote areas if (pbp.footnoteFirstListIndex < pbp.footnoteLastListIndex diff --git a/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java b/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java index 417ad467e..841634ef4 100644 --- a/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java +++ b/src/java/org/apache/fop/layoutmgr/StaticContentLayoutManager.java @@ -236,6 +236,12 @@ public class StaticContentLayoutManager extends BlockStackingLayoutManager { this.displayAlign = displayAlign; } + /** @see org.apache.fop.layoutmgr.AbstractBreaker#isPartOverflowRecoveryActivated() */ + protected boolean isPartOverflowRecoveryActivated() { + //For side regions, this must be disabled because of wanted overflow. + return false; + } + public boolean isOverflow() { return this.overflow; } -- 2.39.5