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 8e03dfd6a4bff4eabf779ace9b758b49cf80f8ba
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Fri Oct 20 08:57:08 2023 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Fri Oct 20 11:53:29 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.
    
    Change-Id: Ib14154200c45ecb7f59e85f9f4f1fe0124c4256e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/158228
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

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 a5e448081b0f..1659cf0df6fd 100644
--- a/sw/qa/core/layout/tabfrm.cxx
+++ b/sw/qa/core/layout/tabfrm.cxx
@@ -98,6 +98,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 183c5114178a..688f1e9770a2 100644
--- a/sw/source/core/layout/tabfrm.cxx
+++ b/sw/source/core/layout/tabfrm.cxx
@@ -2644,6 +2644,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