sw/qa/core/layout/data/floattable-rowsplit.docx |binary
 sw/qa/core/layout/flycnt.cxx                    |   53 ++++++++++++++++++++++++
 sw/source/core/text/frmform.cxx                 |   14 +++++-
 3 files changed, 66 insertions(+), 1 deletion(-)

New commits:
commit f13eb476ea6620bc444d9533959fea78afe720c5
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Tue Feb 28 08:11:01 2023 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Tue Feb 28 08:07:36 2023 +0000

    sw floattable: fix fly pos invalidation in follow anchor frames
    
    The problem was that in case a row split moved the 2nd half of the row
    to a new page, the follow fly frame had a wrong position (top left
    corner of the page).
    
    What happened was that the follow anchor (text frame) was not yet
    positioned by the time the follow fly was positioned, and there was no
    invalidation once the follow anchor got its position.
    
    Fix the problem by improving SwTextFrame::MakePos(): it already had code
    to invalidate the position of follow flys when the position of the
    anchor changes, but it assumed that the flys of a follow anchor are in
    the follow anchor, while in fact flys are always nominally anchored in
    the master, so we didn't find the relevant fly frame and no invalidation
    happened.
    
    Once we use FindMaster() to look up the master and filter based on
    FindAnchorCharFrame(), we find the relevant fly to invalidate and the
    position is correct.
    
    Change-Id: Ic485527bb9dd05b3d5aed383eb5fa1c4f9f6a76d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/147943
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/sw/qa/core/layout/data/floattable-rowsplit.docx 
b/sw/qa/core/layout/data/floattable-rowsplit.docx
new file mode 100644
index 000000000000..8e1e9fb30184
Binary files /dev/null and b/sw/qa/core/layout/data/floattable-rowsplit.docx 
differ
diff --git a/sw/qa/core/layout/flycnt.cxx b/sw/qa/core/layout/flycnt.cxx
index 4b083914b74d..75e6d64366c7 100644
--- a/sw/qa/core/layout/flycnt.cxx
+++ b/sw/qa/core/layout/flycnt.cxx
@@ -210,6 +210,59 @@ CPPUNIT_TEST_FIXTURE(Test, testSplitFly3Pages)
     // No vert offset on the 3rd page:
     CPPUNIT_ASSERT_EQUAL(static_cast<SwTwips>(0), nPage3FlyTop - 
nPage3AnchorTop);
 }
+
+CPPUNIT_TEST_FIXTURE(Test, testSplitFlyRow)
+{
+    // Given a document with a floattable, single row split on 2 pages:
+    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-rowsplit.docx");
+
+    // When laying out that document:
+    calcLayout();
+
+    // Then make sure that the single row is split to 2 pages, and the fly 
frames have the correct
+    // coordinates:
+    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);
+    auto pPage1Anchor = 
dynamic_cast<SwTextFrame*>(pPage1->FindLastBodyContent());
+    CPPUNIT_ASSERT(pPage1Anchor);
+    // ~No offset between the fly and its anchor:
+    SwTwips nPage1AnchorTop = pPage1Anchor->getFrameArea().Top();
+    SwTwips nPage1FlyTop = pPage1Fly->getFrameArea().Top();
+    CPPUNIT_ASSERT_EQUAL(static_cast<SwTwips>(1), nPage1FlyTop - 
nPage1AnchorTop);
+    // Second page:
+    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);
+    auto pPage2Anchor = 
dynamic_cast<SwTextFrame*>(pPage2->FindLastBodyContent());
+    CPPUNIT_ASSERT(pPage2Anchor);
+    // No offset between the fly and its anchor:
+    SwTwips nPage2AnchorTop = pPage2Anchor->getFrameArea().Top();
+    SwTwips nPage2FlyTop = pPage2Fly->getFrameArea().Top();
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 0
+    // - Actual  : -1440
+    // i.e. the 2nd page's fly had a wrong position.
+    CPPUNIT_ASSERT_EQUAL(static_cast<SwTwips>(0), nPage2FlyTop - 
nPage2AnchorTop);
+}
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/frmform.cxx b/sw/source/core/text/frmform.cxx
index 74d1a7cec04e..b1e98d9771bc 100644
--- a/sw/source/core/text/frmform.cxx
+++ b/sw/source/core/text/frmform.cxx
@@ -342,8 +342,20 @@ void SwTextFrame::MakePos()
 {
     SwFrame::MakePos();
 
-    for (const auto& pFly : GetSplitFlyDrawObjs())
+    // Find the master frame.
+    const SwTextFrame* pMaster = this;
+    while (pMaster->IsFollow())
     {
+        pMaster = pMaster->FindMaster();
+    }
+    // Find which flys are effectively anchored to this frame.
+    for (const auto& pFly : pMaster->GetSplitFlyDrawObjs())
+    {
+        SwTextFrame* pFlyAnchor = pFly->FindAnchorCharFrame();
+        if (pFlyAnchor != this)
+        {
+            continue;
+        }
         // Possibly this fly was positioned relative to us, invalidate its 
position now that our
         // position is changed.
         pFly->InvalidatePos();

Reply via email to