sw/CppunitTest_sw_core_layout.mk                       |    1 
 sw/qa/core/layout/data/floattable-negative-height.docx |binary
 sw/qa/core/layout/fly.cxx                              |   91 +++++++++++++++++
 sw/source/core/layout/fly.cxx                          |   14 ++
 4 files changed, 104 insertions(+), 2 deletions(-)

New commits:
commit ce2fc5eb29b4e252993b549dee002fa8948c8386
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Wed Nov 29 08:35:35 2023 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Wed Nov 29 09:57:41 2023 +0100

    tdf#158341 sw floattable: fix layout loop when fly is below the body frame
    
    Regression from commit 25b8fdd3b939a221ba00ca37fbf89adaf893aab7 (sw
    floattable: maintain the invariant that fly height is at least MINFLY,
    2023-09-28), the document started to layout-loop on load.
    
    What happens is we have a fly frame where the bottom of the body frame
    is above both the top and bottom of the fly. We used to make sure these
    flys don't "flip" (with a negative height) and ensure that their height
    is still MINFLY. But that causes a new problem, because the layout will
    try to make sure they fit, but they can't have enough space.
    
    Fix the problem by improving the correction of the fly height, so in
    case even the top is below the deadline, then we set the height to 0 and
    explicitly mark the frame as clipped. That keeps the unwanted warnings
    about violated invariants fixed and fixes the layout loop.
    
    The test just ensures that all pages but the last one has a single
    multi-page floating table, chained over several pages.
    
    Change-Id: Ibac0a465839a59abe5ae49809c0d76c955aa39b9
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160061
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/sw/CppunitTest_sw_core_layout.mk b/sw/CppunitTest_sw_core_layout.mk
index a499f537acc2..b77cf51e2799 100644
--- a/sw/CppunitTest_sw_core_layout.mk
+++ b/sw/CppunitTest_sw_core_layout.mk
@@ -14,6 +14,7 @@ $(eval $(call gb_CppunitTest_CppunitTest,sw_core_layout))
 $(eval $(call gb_CppunitTest_use_common_precompiled_header,sw_core_layout))
 
 $(eval $(call gb_CppunitTest_add_exception_objects,sw_core_layout, \
+    sw/qa/core/layout/fly \
     sw/qa/core/layout/flycnt \
     sw/qa/core/layout/frmtool \
     sw/qa/core/layout/ftnfrm \
diff --git a/sw/qa/core/layout/data/floattable-negative-height.docx 
b/sw/qa/core/layout/data/floattable-negative-height.docx
new file mode 100644
index 000000000000..e5b6cd510300
Binary files /dev/null and 
b/sw/qa/core/layout/data/floattable-negative-height.docx differ
diff --git a/sw/qa/core/layout/fly.cxx b/sw/qa/core/layout/fly.cxx
new file mode 100644
index 000000000000..c1d54119a773
--- /dev/null
+++ b/sw/qa/core/layout/fly.cxx
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <swmodeltestbase.hxx>
+
+#include <IDocumentLayoutAccess.hxx>
+#include <anchoredobject.hxx>
+#include <flyfrms.hxx>
+#include <pagefrm.hxx>
+#include <rootfrm.hxx>
+#include <sortedobjs.hxx>
+#include <docsh.hxx>
+#include <wrtsh.hxx>
+
+namespace
+{
+/// Covers sw/source/core/layout/fly.cxx fixes, i.e. mostly SwFlyFrame.
+class Test : public SwModelTestBase
+{
+public:
+    Test()
+        : SwModelTestBase("/sw/qa/core/layout/data/")
+    {
+    }
+};
+
+CPPUNIT_TEST_FIXTURE(Test, testSplitFlyNegativeHeight)
+{
+    // Given a document with complex enough content that a split fly frame 
temporarily moves below
+    // the bottom of the body frame on a page:
+    // When laying out the document, SwEditShell::CalcLayout() never returned:
+    createSwDoc("floattable-negative-height.docx");
+
+    SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+    pWrtShell->Reformat();
+
+    // Make sure that all the pages have the expected content:
+    SwDoc* pDoc = getSwDoc();
+    SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+    for (SwFrame* pFrame = pLayout->Lower(); pFrame; pFrame = 
pFrame->GetNext())
+    {
+        auto pPage = pFrame->DynCastPageFrame();
+        if (!pPage->GetPrev())
+        {
+            // First page: start of the split fly chain:
+            CPPUNIT_ASSERT(pPage->GetSortedObjs());
+            SwSortedObjs& rPageObjs = *pPage->GetSortedObjs();
+            CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rPageObjs.size());
+            auto pFly = 
rPageObjs[0]->DynCastFlyFrame()->DynCastFlyAtContentFrame();
+            CPPUNIT_ASSERT(pFly);
+            CPPUNIT_ASSERT(!pFly->GetPrecede());
+            CPPUNIT_ASSERT(pFly->GetFollow());
+        }
+        else if (pPage->GetPrev() && pPage->GetNext() && 
pPage->GetNext()->GetNext())
+        {
+            // Middle pages: have a prevous and a next fly:
+            CPPUNIT_ASSERT(pPage->GetSortedObjs());
+            SwSortedObjs& rPageObjs = *pPage->GetSortedObjs();
+            CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rPageObjs.size());
+            auto pFly = 
rPageObjs[0]->DynCastFlyFrame()->DynCastFlyAtContentFrame();
+            CPPUNIT_ASSERT(pFly);
+            CPPUNIT_ASSERT(pFly->GetPrecede());
+            CPPUNIT_ASSERT(pFly->GetFollow());
+        }
+        else if (pPage->GetPrev() && pPage->GetNext() && 
!pPage->GetNext()->GetNext())
+        {
+            // Page last but one: end of the fly chain:
+            CPPUNIT_ASSERT(pPage->GetSortedObjs());
+            SwSortedObjs& rPageObjs = *pPage->GetSortedObjs();
+            CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rPageObjs.size());
+            auto pFly = 
rPageObjs[0]->DynCastFlyFrame()->DynCastFlyAtContentFrame();
+            CPPUNIT_ASSERT(pFly);
+            CPPUNIT_ASSERT(pFly->GetPrecede());
+            CPPUNIT_ASSERT(!pFly->GetFollow());
+        }
+        else if (pPage->GetPrev() && !pPage->GetNext())
+        {
+            // Last page: no flys.
+            CPPUNIT_ASSERT(!pPage->GetSortedObjs());
+        }
+    }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/fly.cxx b/sw/source/core/layout/fly.cxx
index 8dad5a5ad047..d23c30a0aaf1 100644
--- a/sw/source/core/layout/fly.cxx
+++ b/sw/source/core/layout/fly.cxx
@@ -1441,9 +1441,19 @@ void SwFlyFrame::Format( vcl::RenderContext* 
/*pRenderContext*/, const SwBorderA
                 SwTwips nDeadline = GetFlyAnchorBottom(this, *pAnchor);
                 SwTwips nTop = aRectFnSet.GetTop(getFrameArea());
                 SwTwips nBottom = aRectFnSet.GetTop(getFrameArea()) + 
nRemaining;
-                if (nBottom > nDeadline && nDeadline > nTop)
+                if (nBottom > nDeadline)
                 {
-                    nRemaining = nDeadline - nTop;
+                    if (nDeadline > nTop)
+                    {
+                        nRemaining = nDeadline - nTop;
+                    }
+                    else
+                    {
+                        // Even the top is below the deadline, set size to 
empty and mark it as
+                        // clipped so we re-format later.
+                        nRemaining = 0;
+                        m_bHeightClipped = true;
+                    }
                 }
             }
 

Reply via email to