You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xmlgraphics.apache.org by jg...@apache.org on 2026/10/14 17:28:33 UTC

(xmlgraphics-fop) branch main updated: FOP-3354 Fix NoSuchElementException when a side float whose end falls inside a table.

This is an automated email from the ASF dual-hosted git repository.

joao12021996 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/xmlgraphics-fop.git


The following commit(s) were added to refs/heads/main by this push:
     new bed50fc76 FOP-3354 Fix NoSuchElementException when a side float whose end falls inside a table.
bed50fc76 is described below

commit bed50fc769d9eb54d77542b1481700d52d600df4
Author: Joao Goncalves <an...@gmail.com>
AuthorDate: Fri Oct 9 14:56:14 2026 -0500

    FOP-3354 Fix NoSuchElementException when a side float whose end falls inside a table.
    
    Co-authored-by: Jason Harrop <pl...@users.noreply.github.com>
---
 .../fop/layoutmgr/PageBreakingAlgorithm.java       | 47 +++++++++++++++++++
 .../standard-testcases/float_edge-inside-table.xml | 53 ++++++++++++++++++++++
 2 files changed, 100 insertions(+)

diff --git a/fop-core/src/main/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java b/fop-core/src/main/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
index 925f69ea8..817f57127 100644
--- a/fop-core/src/main/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
+++ b/fop-core/src/main/java/org/apache/fop/layoutmgr/PageBreakingAlgorithm.java
@@ -514,10 +514,57 @@ class PageBreakingAlgorithm extends BreakingAlgorithm {
                 //nop
             }
         }
+        // A side float's edge is a break after which the content is read again from its layout manager,
+        // which a table cannot do (it is not restartable): a break between two boxes of one such layout
+        // manager is not the edge, which waits for the first legal break after it. The rows below the
+        // float keep the table's width, which the table set once for all its rows, so nothing is drawn
+        // differently.
+        boolean edgeDeferred = handlingEndOfFloat && isInsideNonRestartableLM(elementIdx);
+        if (edgeDeferred) {
+            handlingEndOfFloat = false;
+        }
         super.considerLegalBreak(element, elementIdx);
+        if (edgeDeferred) {
+            handlingEndOfFloat = true;
+        }
         newFootnotes = false;
     }
 
+    /** Whether the boxes either side of a break both belong to one layout manager that cannot be restarted. */
+    private boolean isInsideNonRestartableLM(int elementIdx) {
+        LayoutManager next = null;
+        for (int i = elementIdx; i < par.size() && next == null; i++) {
+            KnuthElement e = getElement(i);
+            if (e.isBox()) {
+                next = nonRestartableLM(e.getPosition());
+                if (next == null) {
+                    return false;
+                }
+            }
+        }
+        if (next == null) {
+            return false;
+        }
+        for (int i = elementIdx - 1; i >= 0; i--) {
+            KnuthElement e = getElement(i);
+            if (e.isBox()) {
+                return next == nonRestartableLM(e.getPosition());
+            }
+        }
+        return false;
+    }
+
+    /** The outermost layout manager in a position's chain that cannot be restarted, or null. */
+    private static LayoutManager nonRestartableLM(Position position) {
+        for (Position p = position; p != null; p = p.getPosition()) {
+            LayoutManager lm = p.getLM();
+            if (lm != null && !lm.isRestartable()) {
+                return lm;
+            }
+        }
+        return null;
+    }
+
     /** {@inheritDoc} */
     @Override
     protected boolean elementCanEndLine(KnuthElement element, int line, int difference) {
diff --git a/fop/test/layoutengine/standard-testcases/float_edge-inside-table.xml b/fop/test/layoutengine/standard-testcases/float_edge-inside-table.xml
new file mode 100644
index 000000000..e1c87e663
--- /dev/null
+++ b/fop/test/layoutengine/standard-testcases/float_edge-inside-table.xml
@@ -0,0 +1,53 @@
+<?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>
+            A side float outlasts the paragraph it is anchored in and its space-after, so its end falls between two
+            rows of the table after it. A table cannot be read again from a row, so the float's edge waits for the
+            first break after the table: the rows keep the table's width, and the block after the table is laid out
+            at full width. Before, this threw NoSuchElementException in LMiter.next.
+        </p>
+    </info>
+    <fo>
+        <fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
+            <fo:layout-master-set>
+                <fo:simple-page-master master-name="page" page-width="595pt" page-height="842pt" margin="72pt">
+                    <fo:region-body/>
+                </fo:simple-page-master>
+            </fo:layout-master-set>
+            <fo:page-sequence master-reference="page">
+                <fo:flow flow-name="xsl-region-body" font-family="Times" font-size="12pt">
+                    <fo:block line-height="14pt" space-after="10pt"><fo:float float="right"><fo:block line-height="40pt" padding-start="200pt">BOX</fo:block></fo:float>Heading</fo:block>
+                    <fo:table table-layout="fixed" width="200pt" border-collapse="collapse">
+                        <fo:table-column column-width="200pt"/>
+                        <fo:table-body><fo:table-row><fo:table-cell border="0.5pt solid"><fo:block>cell 1</fo:block></fo:table-cell></fo:table-row><fo:table-row><fo:table-cell border="0.5pt solid"><fo:block>cell 2</fo:block></fo:table-cell></fo:table-row><fo:table-row><fo:table-cell border="0.5pt solid"><fo:block>cell 3</fo:block></fo:table-cell></fo:table-row><fo:table-row><fo:table-cell border="0.5pt solid"><fo:block>cell 4</fo:block></fo:table-cell></fo:table-row><fo:table-row><fo:tab [...]
+                    </fo:table>
+                    <fo:block>after</fo:block>
+                </fo:flow>
+            </fo:page-sequence>
+        </fo:root>
+    </fo>
+    <checks>
+        <eval expected="5" xpath="count(//word[. = 'cell'])"/>
+        <eval expected="451000" xpath="//lineArea[text/word = 'after']/../@ipd"/>
+        <eval expected="185012" xpath="//lineArea[text/word = 'Heading']/@end-indent"/>
+    </checks>
+</testcase>
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@xmlgraphics.apache.org
For additional commands, e-mail: commits-help@xmlgraphics.apache.org