]> source.dussan.org Git - xmlgraphics-fop.git/commitdiff
Bugzilla #39840:
authorJeremias Maerki <jeremias@apache.org>
Fri, 4 Aug 2006 15:13:53 +0000 (15:13 +0000)
committerJeremias Maerki <jeremias@apache.org>
Fri, 4 Aug 2006 15:13:53 +0000 (15:13 +0000)
Changed the way overflowing pages are handled. The overflow property on region-body is now used to define the behaviour. It removes the "Some content could not fit..." exception that bugged so many. However, the change does not yet change any keep behaviour.

git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@428750 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/fop/layoutmgr/AbstractBreaker.java
src/java/org/apache/fop/layoutmgr/BalancingColumnBreakingAlgorithm.java
src/java/org/apache/fop/layoutmgr/BreakingAlgorithm.java
src/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
src/java/org/apache/fop/layoutmgr/PageSequenceLayoutManager.java
status.xml
test/layoutengine/standard-testcases/block_keep-together_overflow_1.xml [new file with mode: 0644]

index c549be3ee8d6bfc429c76d3d21e3d6f7f0dd0d53..24ee2e4dad6f1b5da695b01a73f2e195b8eb0a5c 100644 (file)
@@ -196,6 +196,15 @@ public abstract class AbstractBreaker {
         return null;
     }
     
+    /**
+     * Returns a PageBreakingLayoutListener for the PageBreakingAlgorithm to notify about layout
+     * problems.
+     * @return the listener instance or null if no notifications are needed
+     */
+    protected PageBreakingAlgorithm.PageBreakingLayoutListener getLayoutListener() {
+        return null;
+    }
+    
     /*
      * This method is to contain the logic to determine the LM's
      * getNextKnuthElements() implementation(s) that are to be called. 
@@ -310,7 +319,7 @@ public abstract class AbstractBreaker {
                 log.debug("PLM> start of algorithm (" + this.getClass().getName() 
                         + "), flow BPD =" + flowBPD);
                 PageBreakingAlgorithm alg = new PageBreakingAlgorithm(getTopLevelLM(),
-                        getPageProvider(),
+                        getPageProvider(), getLayoutListener(),
                         alignment, alignmentLast, footnoteSeparatorLength,
                         isPartOverflowRecoveryActivated(), autoHeight, isSinglePartFavored());
                 int iOptPageCount;
index c917223608360c029934a051569797488e42537f..57e42aaf61e1520249f5e280fc845aeee5243740 100644 (file)
@@ -21,6 +21,7 @@ package org.apache.fop.layoutmgr;
 \r
 import org.apache.commons.logging.Log;\r
 import org.apache.commons.logging.LogFactory;\r
+import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener;\r
 import org.apache.fop.traits.MinOptMax;\r
 \r
 /**\r
@@ -37,11 +38,13 @@ public class BalancingColumnBreakingAlgorithm extends PageBreakingAlgorithm {
     \r
     public BalancingColumnBreakingAlgorithm(LayoutManager topLevelLM,\r
             PageSequenceLayoutManager.PageProvider pageProvider,\r
+            PageBreakingLayoutListener layoutListener,\r
             int alignment, int alignmentLast,\r
             MinOptMax footnoteSeparatorLength,\r
             boolean partOverflowRecovery,\r
             int columnCount) {\r
-        super(topLevelLM, pageProvider, alignment, alignmentLast, \r
+        super(topLevelLM, pageProvider, layoutListener,\r
+                alignment, alignmentLast, \r
                 footnoteSeparatorLength, partOverflowRecovery, false, false);\r
         this.columnCount = columnCount;\r
         this.considerTooShort = true; //This is important!\r
index 33f5f96ad91afbb742d256bd364e14f69009d771..5720e3095dff33573caa17112d5b2737e0a53acd 100644 (file)
@@ -50,7 +50,7 @@ public abstract class BreakingAlgorithm {
     /** Maximum adjustment ration */
     protected static final int INFINITE_RATIO = 1000;
 
-    private static final int MAX_RECOVERY_ATTEMPTS = 50;
+    private static final int MAX_RECOVERY_ATTEMPTS = 5;
 
     // constants identifying a subset of the feasible breaks
     /** All feasible breaks are ok. */
@@ -156,6 +156,7 @@ public abstract class BreakingAlgorithm {
 
     /** @see #isPartOverflowRecoveryActivated() */
     private boolean partOverflowRecoveryActivated = true;
+    private KnuthNode lastRecovered;
 
     /**
      * Create a new instance.
@@ -495,6 +496,12 @@ public abstract class BreakingAlgorithm {
                 }
                 if (lastTooShort == null || lastForced.position == lastTooShort.position) {
                     if (isPartOverflowRecoveryActivated()) {
+                        if (this.lastRecovered == null) {
+                            this.lastRecovered = lastTooLong;
+                            if (log.isDebugEnabled()) {
+                                log.debug("Recovery point: " + lastRecovered);
+                            }
+                        }
                         // content would overflow, insert empty line/page and try again
                         KnuthNode node = createNode(
                                 lastTooLong.previous.position, lastTooLong.previous.line + 1, 1,
@@ -503,23 +510,34 @@ public abstract class BreakingAlgorithm {
                                 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 (log.isDebugEnabled()) {
+                            log.debug("first part doesn't fit into line, recovering: " 
+                                    + node.fitRecoveryCounter);
+                        }
                         if (node.fitRecoveryCounter > getMaxRecoveryAttempts()) {
-                            FONode contextFO = findContextFO(par, node.position + 1);
-                            throw new RuntimeException(FONode.decorateWithContextInfo(
-                                    "Some content could not fit "
-                                    + "into a line/page after " + getMaxRecoveryAttempts() 
-                                    + " attempts. Giving up to avoid an endless loop.", contextFO));
+                            while (lastForced.fitRecoveryCounter > 0) {
+                                lastForced = lastForced.previous;
+                                lastDeactivated = lastForced.previous;
+                                startLine--;
+                                endLine--;
+                            }
+                            lastForced = this.lastRecovered;
+                            this.lastRecovered = null;
+                            startLine = lastForced.line;
+                            endLine = lastForced.line;
+                            log.debug("rolled back...");
                         }
                     } else {
                         lastForced = lastTooLong;
                     }
                 } else {
                     lastForced = lastTooShort;
+                    this.lastRecovered = null;
                 }
 
-                log.debug("Restarting at node " + lastForced);
+                if (log.isDebugEnabled()) {
+                    log.debug("Restarting at node " + lastForced);
+                }
                 i = restartFrom(lastForced, i);
             }
         }
index 3910fa0234ab37f1f6a918e00f91a90c52617c41..cb28223d04b5e7caf29fbddca9779e0b9bad3a83 100644 (file)
@@ -39,6 +39,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
 
     private LayoutManager topLevelLM;
     private PageSequenceLayoutManager.PageProvider pageProvider;
+    private PageBreakingLayoutListener layoutListener;
     /** List of PageBreakPosition elements. */
     private LinkedList pageBreaks = null;
 
@@ -94,6 +95,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
     
     public PageBreakingAlgorithm(LayoutManager topLevelLM,
                                  PageSequenceLayoutManager.PageProvider pageProvider,
+                                 PageBreakingLayoutListener layoutListener,
                                  int alignment, int alignmentLast,
                                  MinOptMax footnoteSeparatorLength,
                                  boolean partOverflowRecovery, boolean autoHeight,
@@ -102,6 +104,7 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
         this.log = classLog;
         this.topLevelLM = topLevelLM;
         this.pageProvider = pageProvider;
+        this.layoutListener = layoutListener;
         best = new BestPageRecords();
         this.footnoteSeparatorLength = (MinOptMax) footnoteSeparatorLength.clone();
         // add some stretch, to avoid a restart for every page containing footnotes
@@ -746,11 +749,15 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
         //      ? bestActiveNode.difference : bestActiveNode.difference + fillerMinWidth;
         int difference = bestActiveNode.difference;
         if (difference + bestActiveNode.availableShrink < 0) {
-            if (!autoHeight && log.isWarnEnabled()) {
-                log.warn(FONode.decorateWithContextInfo(
-                        "Part/page " + (getPartCount() + 1) 
-                        + " overflows the available area in block-progression dimension.", 
-                        getFObj()));
+            if (!autoHeight) {
+                if (layoutListener != null) {
+                    layoutListener.notifyOverflow(bestActiveNode.line - 1, getFObj());
+                } else if (log.isWarnEnabled()) {
+                    log.warn(FONode.decorateWithContextInfo(
+                            "Part/page " + (bestActiveNode.line - 1) 
+                            + " overflows the available area in block-progression dimension.", 
+                            getFObj()));
+                }
             }
         }
         boolean isNonLastPage = (bestActiveNode.line < total);
@@ -852,4 +859,18 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
         return bpd;
     }
     
+    /**
+     * Interface to notify about layout events during page breaking.
+     */
+    public interface PageBreakingLayoutListener {
+
+        /**
+         * Issued when an overflow is detected
+         * @param part the number of the part (page) this happens on
+         * @param obj the root FO object where this happens
+         */
+        void notifyOverflow(int part, FObj obj);
+        
+    }
+    
 }
index 9a06896f35da1b4e6b3a45180ca2188e49ddfa03..4496d11da2f93c0924b548ffac2c19ca16e9f24c 100644 (file)
@@ -33,6 +33,8 @@ import org.apache.fop.area.LineArea;
 import org.apache.fop.area.Resolvable;
 
 import org.apache.fop.fo.Constants;
+import org.apache.fop.fo.FONode;
+import org.apache.fop.fo.FObj;
 import org.apache.fop.fo.flow.Marker;
 import org.apache.fop.fo.flow.RetrieveMarker;
 
@@ -43,6 +45,7 @@ import org.apache.fop.fo.pagination.RegionBody;
 import org.apache.fop.fo.pagination.SideRegion;
 import org.apache.fop.fo.pagination.SimplePageMaster;
 import org.apache.fop.fo.pagination.StaticContent;
+import org.apache.fop.layoutmgr.PageBreakingAlgorithm.PageBreakingLayoutListener;
 import org.apache.fop.layoutmgr.inline.ContentLayoutManager;
 
 import org.apache.fop.traits.MinOptMax;
@@ -192,8 +195,9 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
             context.setRefIPD(flowIPD);
         }
         
+        /** @see org.apache.fop.layoutmgr.AbstractBreaker#getTopLevelLM() */
         protected LayoutManager getTopLevelLM() {
-            return null;  // unneeded for PSLM
+            return pslm;
         }
         
         /** @see org.apache.fop.layoutmgr.AbstractBreaker#getPageProvider() */
@@ -201,6 +205,32 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
             return pageProvider;
         }
         
+        /**
+         * @see org.apache.fop.layoutmgr.AbstractBreaker#getLayoutListener()
+         */
+        protected PageBreakingLayoutListener getLayoutListener() {
+            return new PageBreakingLayoutListener() {
+
+                public void notifyOverflow(int part, FObj obj) {
+                    Page p = pageProvider.getPage(
+                                false, part, PageProvider.RELTO_CURRENT_ELEMENT_LIST);
+                    RegionBody body = (RegionBody)p.getSimplePageMaster().getRegion(
+                            Region.FO_REGION_BODY);
+                    String err = FONode.decorateWithContextInfo(
+                            "Content of the region-body on page " 
+                            + p.getPageViewport().getPageNumberString() 
+                            + " overflows the available area in block-progression dimension.", 
+                            obj);
+                    if (body.getOverflow() == Constants.EN_ERROR_IF_OVERFLOW) {
+                        throw new RuntimeException(err);
+                    } else {
+                        PageSequenceLayoutManager.log.warn(err);
+                    }
+                }
+                
+            };
+        }
+
         /** @see org.apache.fop.layoutmgr.AbstractBreaker */
         protected int handleSpanChange(LayoutContext childLC, int nextSequenceStartsOn) {
             needColumnBalancing = false;
@@ -373,7 +403,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
             //Restart last page
             PageBreakingAlgorithm algRestart = new PageBreakingAlgorithm(
                     getTopLevelLM(),
-                    getPageProvider(),
+                    getPageProvider(), getLayoutListener(),
                     alg.getAlignment(), alg.getAlignmentLast(), 
                     footnoteSeparatorLength,
                     isPartOverflowRecoveryActivated(), false, false);
@@ -433,7 +463,7 @@ public class PageSequenceLayoutManager extends AbstractLayoutManager {
             //Restart last page
             PageBreakingAlgorithm algRestart = new BalancingColumnBreakingAlgorithm(
                     getTopLevelLM(),
-                    getPageProvider(),
+                    getPageProvider(), getLayoutListener(),
                     alignment, Constants.EN_START, footnoteSeparatorLength,
                     isPartOverflowRecoveryActivated(),
                     getCurrentPV().getBodyRegion().getColumnCount());
index 408dba61708fbf80340b1284462bdec1125e1941..b71af09d4a0d3257fe26a46651cc938c3a4439d0 100644 (file)
 
   <changes>
     <release version="FOP Trunk">
+      <action context="Code" dev="JM" type="update">
+        Changed the way overflowing pages are handled. The overflow property on region-body
+        is now used to define the behaviour.
+      </action>
       <action context="Code" dev="JM" type="fix">
         Fixed a memory-leak: The FO tree part of a page-sequence was not released when a
         page-sequence was finished.
diff --git a/test/layoutengine/standard-testcases/block_keep-together_overflow_1.xml b/test/layoutengine/standard-testcases/block_keep-together_overflow_1.xml
new file mode 100644 (file)
index 0000000..a4a2de6
--- /dev/null
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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 keep-together with overflow conditions.
+    </p>
+    <p>
+      Widows and Orphans are disabled in this test to avoid side-effects.
+    </p>
+  </info>
+  <fo>
+    <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" widows="0" orphans="0">
+      <fo:layout-master-set>
+        <fo:simple-page-master master-name="normal" page-width="5in" page-height="4.5 * 14.4pt">
+          <fo:region-body overflow="hidden"/>
+        </fo:simple-page-master>
+        <fo:simple-page-master master-name="longer" page-width="5in" page-height="5.5 * 14.4pt">
+          <fo:region-body overflow="hidden"/>
+        </fo:simple-page-master>
+        <fo:page-sequence-master master-name="mix">
+          <fo:repeatable-page-master-reference master-reference="normal" maximum-repeats="2"/>
+          <fo:repeatable-page-master-reference master-reference="longer" maximum-repeats="1"/>
+          <fo:repeatable-page-master-reference master-reference="normal"/>
+        </fo:page-sequence-master>
+      </fo:layout-master-set>
+      <fo:page-sequence master-reference="mix">
+        <fo:flow flow-name="xsl-region-body">
+          <fo:block>block1</fo:block>
+          <fo:block>block2</fo:block>
+          <fo:block keep-together.within-page="10" color="blue">
+            <fo:block>block3</fo:block>
+            <fo:block>block4</fo:block>
+            <fo:block>block5</fo:block>
+            <fo:block>block6</fo:block>
+            <fo:block>block7</fo:block>
+          </fo:block>
+          <fo:block>block8</fo:block>
+          <fo:block>block9</fo:block>
+          <fo:block keep-together.within-page="10" color="green">
+            <fo:block>block10</fo:block>
+            <fo:block>block11</fo:block>
+            <fo:block>block12</fo:block>
+            <fo:block>block13</fo:block>
+            <fo:block>block14</fo:block>
+          </fo:block>
+          <fo:block>block15</fo:block>
+        </fo:flow>
+      </fo:page-sequence>
+    </fo:root>
+  </fo>
+  <checks>
+    <eval expected="1" xpath="//lineArea[starts-with(., 'block1')]/ancestor::pageViewport/@nr"/>
+    <eval expected="1" xpath="//lineArea[starts-with(., 'block2')]/ancestor::pageViewport/@nr"/>
+    <eval expected="3" xpath="//lineArea[starts-with(., 'block3')]/ancestor::pageViewport/@nr"/>
+    <eval expected="3" xpath="//lineArea[starts-with(., 'block4')]/ancestor::pageViewport/@nr"/>
+    <eval expected="3" xpath="//lineArea[starts-with(., 'block5')]/ancestor::pageViewport/@nr"/>
+    <eval expected="3" xpath="//lineArea[starts-with(., 'block6')]/ancestor::pageViewport/@nr"/>
+    <eval expected="3" xpath="//lineArea[starts-with(., 'block7')]/ancestor::pageViewport/@nr"/>
+    <eval expected="4" xpath="//lineArea[starts-with(., 'block8')]/ancestor::pageViewport/@nr"/>
+    <eval expected="4" xpath="//lineArea[starts-with(., 'block9')]/ancestor::pageViewport/@nr"/>
+    <eval expected="5" xpath="//lineArea[starts-with(., 'block10')]/ancestor::pageViewport/@nr"/>
+    <eval expected="5" xpath="//lineArea[starts-with(., 'block11')]/ancestor::pageViewport/@nr"/>
+    <eval expected="5" xpath="//lineArea[starts-with(., 'block12')]/ancestor::pageViewport/@nr"/>
+    <eval expected="5" xpath="//lineArea[starts-with(., 'block13')]/ancestor::pageViewport/@nr"/>
+    <eval expected="5" xpath="//lineArea[starts-with(., 'block14')]/ancestor::pageViewport/@nr"/>
+    <eval expected="6" xpath="//lineArea[starts-with(., 'block15')]/ancestor::pageViewport/@nr"/>
+  </checks>
+</testcase>