sw/qa/core/layout/data/floattable-compat14-body.docx |binary
 sw/qa/core/layout/flycnt.cxx                         |   46 +++++++++++++++++++
 sw/source/core/layout/fly.cxx                        |   33 +++++++++----
 3 files changed, 70 insertions(+), 9 deletions(-)

New commits:
commit fd8ae820cb604f8698e5a5f4fe3a5ab15f5b283d
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Fri Mar 17 08:22:26 2023 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Mon Mar 20 11:17:48 2023 +0000

    sw floattable, legacy: also consier fly position when deciding the bottom
    
    The bugdoc has 2 rows: the 2nd row was split and the master row went to
    page 1 and the follow row went to page 2. Word positioned the entire row
    to page 2.
    
    The problem seems to be that the layout code wants a "deadline" (so it
    can grow the frame to that bottom position), but the deadline depends on
    the height: split flys need to fit the body frame, then they can have a
    vertical offset (resulting in an overlap between the bottom margin area
    & the fly frame), in case the fly frame is still inside the page area.
    This means that this deadline depends on the body height, the fly top
    and the fly height at the same time. (Or in other words: the overlap is
    only allowed if the fly would fit the body frame, but an additional
    vertical offset pushes it to the bottom margin area, with an unchanged
    height.)
    
    Solve this fly height <-> deadline circular dependency by first checking
    against the page size, using the current fly height; and then against
    the body height, using the assumed / future fly height (assuming that
    the fly will grow so its new height will match the deadline). This
    results in a construct where usually you can't grow outside the body
    area, but in some limited cases you can (till you only use the bottom
    margin area), but only in case you grow your top position as well, which
    is Word-compatible, but quite unusual at other places in Writer.
    
    With this, the original tdf#61594 bugdoc gets rendered correctly, once
    you opt in with SW_FORCE_FLY_SPLIT=1.
    
    (cherry picked from commit 90523e10ec053347719309403a4d8566da1dfc4a)
    
    Change-Id: I016df1bad79f4b09abc7e2b4fe9ea613355e3794
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/149143
    Tested-by: Miklos Vajna <vmik...@collabora.com>
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/sw/qa/core/layout/data/floattable-compat14-body.docx 
b/sw/qa/core/layout/data/floattable-compat14-body.docx
new file mode 100644
index 000000000000..134128348ce2
Binary files /dev/null and 
b/sw/qa/core/layout/data/floattable-compat14-body.docx differ
diff --git a/sw/qa/core/layout/flycnt.cxx b/sw/qa/core/layout/flycnt.cxx
index 07aaf5deba3b..a9da83353ecd 100644
--- a/sw/qa/core/layout/flycnt.cxx
+++ b/sw/qa/core/layout/flycnt.cxx
@@ -566,6 +566,52 @@ CPPUNIT_TEST_FIXTURE(Test, testSplitFlyCompat14Nosplit)
     SwFrame* pRow2 = pTab2->GetLower();
     CPPUNIT_ASSERT(!pRow2->GetNext());
 }
+
+CPPUNIT_TEST_FIXTURE(Test, testSplitFlyCompat14Body)
+{
+    // Given a Word 2010 document with 2 pages, 1 row on page 1, 1 row on page 
2:
+    std::shared_ptr<comphelper::ConfigurationChanges> pChanges(
+        comphelper::ConfigurationChanges::create());
+    
officecfg::Office::Writer::Filter::Import::DOCX::ImportFloatingTableAsSplitFly::set(true,
+                                                                               
         pChanges);
+    pChanges->commit();
+    comphelper::ScopeGuard g([pChanges] {
+        
officecfg::Office::Writer::Filter::Import::DOCX::ImportFloatingTableAsSplitFly::set(
+            false, pChanges);
+        pChanges->commit();
+    });
+    createSwDoc("floattable-compat14-body.docx");
+
+    // When laying out that document:
+    // (This is legacy mode, but still Word doesn't split row 2 because 1) row 
2 has a minimal
+    // height, so even the first part of row 2 would not fit the body frame 
and 2) Word allows using
+    // the bottom margin area in legacy mode, but only in case the fly height 
<= body height.)
+    calcLayout();
+
+    // Then make sure that the second row is on a page 2:
+    SwDoc* pDoc = getSwDoc();
+    SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+    auto pPage1 = dynamic_cast<SwPageFrame*>(pLayout->Lower());
+    CPPUNIT_ASSERT(pPage1);
+    const SwSortedObjs& rPage1Objs = *pPage1->GetSortedObjs();
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rPage1Objs.size());
+    auto pPage1Fly = dynamic_cast<SwFlyAtContentFrame*>(rPage1Objs[0]);
+    CPPUNIT_ASSERT(pPage1Fly);
+    SwFrame* pTab1 = pPage1Fly->GetLower();
+    SwFrame* pRow1 = pTab1->GetLower();
+    // Without the accompanying fix in place, this test would have failed, 
part of row 2 was on page
+    // 1.
+    CPPUNIT_ASSERT(!pRow1->GetNext());
+    auto pPage2 = dynamic_cast<SwPageFrame*>(pPage1->GetNext());
+    CPPUNIT_ASSERT(pPage2);
+    const SwSortedObjs& rPage2Objs = *pPage2->GetSortedObjs();
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rPage2Objs.size());
+    auto pPage2Fly = dynamic_cast<SwFlyAtContentFrame*>(rPage2Objs[0]);
+    CPPUNIT_ASSERT(pPage2Fly);
+    SwFrame* pTab2 = pPage2Fly->GetLower();
+    SwFrame* pRow2 = pTab2->GetLower();
+    CPPUNIT_ASSERT(!pRow2->GetNext());
+}
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/fly.cxx b/sw/source/core/layout/fly.cxx
index 9ffb1a480c96..6c6a2933952b 100644
--- a/sw/source/core/layout/fly.cxx
+++ b/sw/source/core/layout/fly.cxx
@@ -90,21 +90,36 @@ SwTwips GetFlyAnchorBottom(SwFlyFrame* pFly, const SwFrame& 
rAnchor)
     bool bLegacy = rIDSA.get(DocumentSettingId::TAB_OVER_MARGIN);
     if (bLegacy)
     {
-        // Word <= 2010 style: the fly can overlap with the bottom margin / 
footer area.
-        const SwFrame* pAnchorUpper = rAnchor.FindPageFrame();
-        if (!pAnchorUpper)
+        // Word <= 2010 style: the fly can overlap with the bottom margin / 
footer area in case the
+        // fly height fits the body height and the fly bottom fits the page.
+        const SwPageFrame* pPage = rAnchor.FindPageFrame();
+        if (!pPage)
         {
             return 0;
         }
 
-        // See if the fly height would still fit the body frame and the 
overlap would happen just
-        // because of a vertical offset.
+        const SwFrame* pBody = pPage->FindBodyCont();
+        if (!pBody)
+        {
+            return 0;
+        }
+
+        // See if the fly height would fit at least the page height, ignoring 
the vertical offset.
         SwTwips nFlyHeight = aRectFnSet.GetHeight(pFly->getFrameArea());
-        SwTwips nAnchorUpperHeight = 
aRectFnSet.GetHeight(pAnchorUpper->getFramePrintArea());
-        if (nFlyHeight <= nAnchorUpperHeight)
+        SwTwips nPageHeight = aRectFnSet.GetHeight(pPage->getFramePrintArea());
+        if (nFlyHeight <= nPageHeight)
         {
-            // Yes, it would fit: allow overlap.
-            return aRectFnSet.GetBottom(pAnchorUpper->getFrameArea());
+            // Yes, it would fit: allow overlap if there is no problematic 
vertical offset.
+            SwTwips nDeadline = aRectFnSet.GetBottom(pPage->getFrameArea());
+            SwTwips nFlyTop = aRectFnSet.GetTop(pFly->getFrameArea());
+            SwTwips nBodyHeight = 
aRectFnSet.GetHeight(pBody->getFramePrintArea());
+            if (nDeadline - nFlyTop > nBodyHeight)
+            {
+                // If the fly would now grow to nDeadline then it would not 
fit the body height, so
+                // limit the height.
+                nDeadline = nFlyTop + nBodyHeight;
+            }
+            return nDeadline;
         }
     }
 

Reply via email to