sw/qa/core/layout/data/floattable-nested-rowspan.docx |binary
 sw/qa/core/layout/tabfrm.cxx                          |   11 +++++
 sw/source/core/layout/tabfrm.cxx                      |   37 ++++++++++++++++++
 3 files changed, 48 insertions(+)

New commits:
commit 3be73099197aa8892ad34f267e3908734464dddf
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Fri Oct 20 08:57:08 2023 +0200
Commit:     Caolán McNamara <caolan.mcnam...@collabora.com>
CommitDate: Tue Oct 24 16:41:40 2023 +0200

    tdf#157590 sw floattable: avoid hang in the nested + row span case
    
    Regression from commit 905962db870e9d1cf1dcf3bd1be44c347cddafe1 (sw
    floattable: handle AllowOverlap==false in the layout, 2023-08-10), the
    document load resulted in a hang due to a layout loop.
    
    What happens is that SwTabFrame::MakeAll() first does a Split(), but the
    problematic row has cells with rowspans, and once this is combined with
    multi-page nested floating tables, we move all the content to the next
    page (we only leave a stub table frame on the old page), so the next
    time SwTabFrame::MakeAll() is called, we do a Join(), and this leads to
    a loop.
    
    The traditional Writer way here would be to add a loop control, but we
    can do a little bit better: nobody really asked for row span handling
    with nested floating tables, so just don't split rows with row span in
    this case, move the entire row forward instead. This is enough to avoid
    the layout loop, and a next iteration can still use
    SwFlowFrame::MoveBwd() / SwFlowFrame::MoveFwd() to split the complex
    row.
    
    The bug is fairly hard to hit, any naive simplification to the original
    bugdoc leads to a working layout. Carefully keeping the size of the
    document, it's possible to at least simplify the content of the table
    cells (while keeping their size unchanged), so we avoid half of the
    tables and half of the shapes for a faster test case.
    
    (cherry picked from commit 8e03dfd6a4bff4eabf779ace9b758b49cf80f8ba)
    
    Change-Id: Ib14154200c45ecb7f59e85f9f4f1fe0124c4256e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/158379
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Tested-by: Caolán McNamara <caolan.mcnam...@collabora.com>
    Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com>

diff --git a/sw/qa/core/layout/data/floattable-nested-rowspan.docx 
b/sw/qa/core/layout/data/floattable-nested-rowspan.docx
new file mode 100644
index 000000000000..48cbdbfe59c3
Binary files /dev/null and 
b/sw/qa/core/layout/data/floattable-nested-rowspan.docx differ
diff --git a/sw/qa/core/layout/tabfrm.cxx b/sw/qa/core/layout/tabfrm.cxx
index 64a30e8368db..44ee7c2a6966 100644
--- a/sw/qa/core/layout/tabfrm.cxx
+++ b/sw/qa/core/layout/tabfrm.cxx
@@ -82,6 +82,17 @@ CPPUNIT_TEST_FIXTURE(Test, testSplitFlyInInlineTable)
         CPPUNIT_ASSERT(!pTab->GetFollow());
     }
 }
+
+CPPUNIT_TEST_FIXTURE(Test, testSplitFlyNestedRowSpan)
+{
+    // Given a document with nested floating tables and a row with rowspan 
cells at page boundary:
+    // When loading that document:
+    // Without the accompanying fix in place, this test would have resulted in 
a layout loop.
+    createSwDoc("floattable-nested-rowspan.docx");
+
+    // Then make sure the resulting page count matches Word:
+    CPPUNIT_ASSERT_EQUAL(6, getPages());
+}
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx
index 344039b1bffe..b97536d015c6 100644
--- a/sw/source/core/layout/tabfrm.cxx
+++ b/sw/source/core/layout/tabfrm.cxx
@@ -2608,6 +2608,43 @@ void SwTabFrame::MakeAll(vcl::RenderContext* 
pRenderContext)
                     // See if this is a split fly that can also grow.
                     auto pUpperFly = static_cast<SwFlyFrame*>(GetUpper());
                     bFlySplit = pUpperFly->IsFlySplitAllowed();
+
+                    if (bFlySplit)
+                    {
+                        // See if this is a nested split fly where the inner 
table also has
+                        // rowspans.
+                        SwTextFrame* pAnchorCharFrame = 
pUpperFly->FindAnchorCharFrame();
+                        if (pAnchorCharFrame && pAnchorCharFrame->IsInFly())
+                        {
+                            // Find the row we'll split.
+                            SwTwips nRemaining
+                                = aRectFnSet.YDiff(nDeadLine, 
aRectFnSet.GetTop(getFrameArea()));
+                            nRemaining -= aRectFnSet.GetTopMargin(*this);
+                            const SwFrame* pRow = Lower();
+                            for (; pRow->GetNext(); pRow = pRow->GetNext())
+                            {
+                                if (nRemaining < 
aRectFnSet.GetHeight(pRow->getFrameArea()))
+                                {
+                                    break;
+                                }
+
+                                nRemaining -= 
aRectFnSet.GetHeight(pRow->getFrameArea());
+                            }
+                            // See if any cells have rowspans.
+                            for (const SwFrame* pLower = pRow->GetLower(); 
pLower;
+                                 pLower = pLower->GetNext())
+                            {
+                                auto pCellFrame = static_cast<const 
SwCellFrame*>(pLower);
+                                if (pCellFrame->GetTabBox()->getRowSpan() != 1)
+                                {
+                                    // The cell has a rowspan, don't split the 
row itself in this
+                                    // case (but just move it forward, i.e. 
split between the rows).
+                                    bTryToSplit = false;
+                                    break;
+                                }
+                            }
+                        }
+                    }
                 }
                 if( IsInSct() || GetUpper()->IsInTab() || bFlySplit )
                     nDeadLine = aRectFnSet.YInc( nDeadLine,

Reply via email to