sw/qa/core/layout/data/floattable-3pages.docx                      |binary
 sw/qa/core/layout/flycnt.cxx                                       |   67 
+++++++++-
 sw/source/core/layout/anchoredobject.cxx                           |   20 ++
 sw/source/core/layout/flycnt.cxx                                   |   17 +-
 sw/source/core/objectpositioning/tocntntanchoredobjectposition.cxx |   23 ++-
 sw/source/core/text/frmform.cxx                                    |    8 +
 sw/source/core/text/itratr.cxx                                     |   15 ++
 7 files changed, 130 insertions(+), 20 deletions(-)

New commits:
commit 7c9acfe5baef275f07c185c6fedf8b6d62d88637
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Mon Feb 20 08:15:41 2023 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Mon Feb 20 08:09:20 2023 +0000

    sw floattable: fix handling of multiple splits, i.e. table over 3 or more 
pages
    
    - fix SwAnchoredObject::FindAnchorCharFrame() and
      SwToContentAnchoredObjectPosition::CalcPosition() to not assume that the
      precede of a follow fly is the master fly, which is not true when the 
table
      is split to 3 pages. In this case we have a master, then a normal follow
      and finally a last follow
    
    - fix SwFrame::GetNextFlyLeaf() to split the right anchor, which may be
      a follow text frame now, and register the new fly in master text
      frame, not at the split position
    
    - improve SwTextFrame::HasNonLastSplitFlyDrawObj() to look at flys in
      the master, but only consider the ones which will be positioned
      relative to the current anchor. Previously this was easier, as a
      "non-last split fly" happened to be always a master text frame, but
      now we han have split flys which are neither a master nor a last follow
    
    - finally improve SwTextFrame::MakePos(), because the anchor on the 2nd
      page is calculated after the 2nd fly is positioned, so it was only
      positioned inside the 2nd page, but not at the correct position
    
    Change-Id: Ibecaf5fcf0b8c56c5109461a16dcca058231b660
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/147308
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/sw/qa/core/layout/data/floattable-3pages.docx 
b/sw/qa/core/layout/data/floattable-3pages.docx
new file mode 100644
index 000000000000..d6cbdafc1945
Binary files /dev/null and b/sw/qa/core/layout/data/floattable-3pages.docx 
differ
diff --git a/sw/qa/core/layout/flycnt.cxx b/sw/qa/core/layout/flycnt.cxx
index a41fb17ef54d..4b083914b74d 100644
--- a/sw/qa/core/layout/flycnt.cxx
+++ b/sw/qa/core/layout/flycnt.cxx
@@ -93,7 +93,7 @@ CPPUNIT_TEST_FIXTURE(Test, testSplitFlyWithTable)
     CPPUNIT_ASSERT(!pPage1Anchor->HasPara());
 }
 
-CPPUNIT_TEST_FIXTURE(Test, testSplitFlyVertoffset)
+CPPUNIT_TEST_FIXTURE(Test, testSplitFlyVertOffset)
 {
     // Given a document with a floattable, split on 2 pages and a positive 
vertical offset:
     std::shared_ptr<comphelper::ConfigurationChanges> pChanges(
@@ -145,6 +145,71 @@ CPPUNIT_TEST_FIXTURE(Test, testSplitFlyVertoffset)
     // i.e. the fly frame on the 2nd page was also shifted down in Writer, but 
not in Word.
     CPPUNIT_ASSERT_EQUAL(static_cast<SwTwips>(0), nPage2FlyTop - 
nPage2AnchorTop);
 }
+
+CPPUNIT_TEST_FIXTURE(Test, testSplitFly3Pages)
+{
+    // Given a document with a floattable, split on 3 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-3pages.docx");
+
+    // When laying out that document:
+    calcLayout();
+
+    // Then make sure that row 1, 2 & 3 go to page 1, 2 & 3, while the table 
is floating:
+    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);
+    SwTwips nPage1AnchorTop = pPage1Anchor->getFrameArea().Top();
+    SwTwips nPage1FlyTop = pPage1Fly->getFrameArea().Top();
+    // The vert offset should be there on the first page:
+    CPPUNIT_ASSERT_EQUAL(static_cast<SwTwips>(1135), nPage1FlyTop - 
nPage1AnchorTop);
+    // Second page:
+    auto pPage2 = dynamic_cast<SwPageFrame*>(pPage1->GetNext());
+    CPPUNIT_ASSERT(pPage2);
+    const SwSortedObjs& rPage2Objs = *pPage2->GetSortedObjs();
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 1
+    // - Actual  : 2
+    // i.e. both the 2nd and 3rd fly was anchored on page 2.
+    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);
+    SwTwips nPage2AnchorTop = pPage2Anchor->getFrameArea().Top();
+    SwTwips nPage2FlyTop = pPage2Fly->getFrameArea().Top();
+    // No vert offset on the second page:
+    CPPUNIT_ASSERT_EQUAL(static_cast<SwTwips>(0), nPage2FlyTop - 
nPage2AnchorTop);
+    // 3rd page:
+    auto pPage3 = dynamic_cast<SwPageFrame*>(pPage1->GetNext());
+    CPPUNIT_ASSERT(pPage3);
+    const SwSortedObjs& rPage3Objs = *pPage3->GetSortedObjs();
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), rPage3Objs.size());
+    auto pPage3Fly = dynamic_cast<SwFlyAtContentFrame*>(rPage3Objs[0]);
+    CPPUNIT_ASSERT(pPage3Fly);
+    auto pPage3Anchor = 
dynamic_cast<SwTextFrame*>(pPage3->FindLastBodyContent());
+    CPPUNIT_ASSERT(pPage3Anchor);
+    SwTwips nPage3AnchorTop = pPage3Anchor->getFrameArea().Top();
+    SwTwips nPage3FlyTop = pPage3Fly->getFrameArea().Top();
+    // No vert offset on the 3rd page:
+    CPPUNIT_ASSERT_EQUAL(static_cast<SwTwips>(0), nPage3FlyTop - 
nPage3AnchorTop);
+}
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/anchoredobject.cxx 
b/sw/source/core/layout/anchoredobject.cxx
index 4272437f9e25..5f8a307867bf 100644
--- a/sw/source/core/layout/anchoredobject.cxx
+++ b/sw/source/core/layout/anchoredobject.cxx
@@ -724,14 +724,24 @@ SwTextFrame* SwAnchoredObject::FindAnchorCharFrame()
             if (pFlyFrame->IsFlySplitAllowed())
             {
                 auto pFlyAtContentFrame = 
static_cast<SwFlyAtContentFrame*>(pFlyFrame);
-                if (pFlyAtContentFrame->GetPrecede())
+                SwFlyAtContentFrame* pFly = pFlyAtContentFrame;
+                SwTextFrame* pAnchor = 
static_cast<SwTextFrame*>(AnchorFrame());
+                // If we have to jump back N frames to find the master fly, 
then we have to step N
+                // frames from the master anchor to reach the correct follow 
anchor.
+                while (pFly->GetPrecede())
                 {
-                    SwTextFrame* 
pFrame(static_cast<SwTextFrame*>(AnchorFrame()));
-                    const SwTextFrame* pFollow = pFrame->GetFollow();
-                    if (pFollow)
+                    pFly = pFly->GetPrecede();
+                    if (!pAnchor)
                     {
-                        pAnchorCharFrame = const_cast<SwTextFrame*>(pFollow);
+                        SAL_WARN("sw.layout", 
"SwAnchoredObject::FindAnchorCharFrame: fly chain "
+                                              "length is longer then anchor 
chain length");
+                        break;
                     }
+                    pAnchor = pAnchor->GetFollow();
+                }
+                if (pAnchor)
+                {
+                    pAnchorCharFrame = pAnchor;
                 }
             }
         }
diff --git a/sw/source/core/layout/flycnt.cxx b/sw/source/core/layout/flycnt.cxx
index 9e8d1a3ec13b..f96669c537d0 100644
--- a/sw/source/core/layout/flycnt.cxx
+++ b/sw/source/core/layout/flycnt.cxx
@@ -1600,19 +1600,22 @@ SwLayoutFrame *SwFrame::GetNextFlyLeaf( MakePageType 
eMakePage )
     if( pLayLeaf )
     {
         SwFlyAtContentFrame* pNew = nullptr;
-        SwFrame* pFlyAnchor = const_cast<SwFrame*>(pFly->GetAnchorFrame());
-        if (pFlyAnchor && pFlyAnchor->IsTextFrame())
+        // Find the anchor frame to split.
+        SwTextFrame* pFlyAnchor = pFly->FindAnchorCharFrame();
+        if (pFlyAnchor)
         {
             // Split the anchor at char 0: all the content goes to the follow 
of the anchor.
-            auto pFlyAnchorTextFrame = static_cast<SwTextFrame*>(pFlyAnchor);
-            pFlyAnchorTextFrame->SplitFrame(TextFrameIndex(0));
+            pFlyAnchor->SplitFrame(TextFrameIndex(0));
             auto pNext = static_cast<SwTextFrame*>(pFlyAnchor->GetNext());
             pNext->MoveSubTree(pLayLeaf);
 
-            // Now create the follow of the fly and anchor it in the just 
created follow of the
-            // anchor.
+            // Now create the follow of the fly and anchor it in the master of 
the anchor.
             pNew = new SwFlyAtContentFrame(*pFly);
-            pFlyAnchorTextFrame->AppendFly(pNew);
+            while (pFlyAnchor->IsFollow())
+            {
+                pFlyAnchor = pFlyAnchor->FindMaster();
+            }
+            pFlyAnchor->AppendFly(pNew);
         }
         pLayLeaf = pNew;
     }
diff --git a/sw/source/core/objectpositioning/tocntntanchoredobjectposition.cxx 
b/sw/source/core/objectpositioning/tocntntanchoredobjectposition.cxx
index 1a58d3a650dd..8756da6a325c 100644
--- a/sw/source/core/objectpositioning/tocntntanchoredobjectposition.cxx
+++ b/sw/source/core/objectpositioning/tocntntanchoredobjectposition.cxx
@@ -236,15 +236,26 @@ void SwToContentAnchoredObjectPosition::CalcPosition()
             if (pFlyFrame->IsFlySplitAllowed())
             {
                 auto pFlyAtContentFrame = 
static_cast<SwFlyAtContentFrame*>(pFlyFrame);
-                if (pFlyAtContentFrame->GetPrecede())
+                // Decrement pFly to point to the master; increment pAnchor to 
point to the correct
+                // follow anchor.
+                SwFlyAtContentFrame* pFly = pFlyAtContentFrame;
+                SwTextFrame* pAnchor = 
const_cast<SwTextFrame*>(&rAnchorTextFrame);
+                while (pFly->GetPrecede())
                 {
-                    const SwTextFrame* pFollow = rAnchorTextFrame.GetFollow();
-                    if (pFollow)
+                    pFly = pFly->GetPrecede();
+                    if (!pAnchor)
                     {
-                        pOrientFrame = pFollow;
-                        // Anchored object has a precede, so it's a follow.
-                        bFollowSplitFly = true;
+                        SAL_WARN("sw.core", 
"SwToContentAnchoredObjectPosition::CalcPosition: fly "
+                                            "chain length is longer then 
anchor chain length");
+                        break;
                     }
+                    pAnchor = pAnchor->GetFollow();
+                }
+                if (pAnchor && pAnchor->GetPrecede())
+                {
+                    pOrientFrame = pAnchor;
+                    // Anchored object has a precede, so it's a follow.
+                    bFollowSplitFly = true;
                 }
             }
         }
diff --git a/sw/source/core/text/frmform.cxx b/sw/source/core/text/frmform.cxx
index 5b3db006a2ee..74d1a7cec04e 100644
--- a/sw/source/core/text/frmform.cxx
+++ b/sw/source/core/text/frmform.cxx
@@ -341,6 +341,14 @@ bool SwTextFrame::CalcFollow(TextFrameIndex const 
nTextOfst)
 void SwTextFrame::MakePos()
 {
     SwFrame::MakePos();
+
+    for (const auto& pFly : GetSplitFlyDrawObjs())
+    {
+        // Possibly this fly was positioned relative to us, invalidate its 
position now that our
+        // position is changed.
+        pFly->InvalidatePos();
+    }
+
     // Inform LOK clients about change in position of redlines (if any)
     if(!comphelper::LibreOfficeKit::isActive())
         return;
diff --git a/sw/source/core/text/itratr.cxx b/sw/source/core/text/itratr.cxx
index 4a0cdc5e6807..613a2aa44265 100644
--- a/sw/source/core/text/itratr.cxx
+++ b/sw/source/core/text/itratr.cxx
@@ -1495,8 +1495,21 @@ bool SwTextFrame::HasNonLastSplitFlyDrawObj() const
     // At this point we know what we're part of a chain that is an anchor for 
split fly frames, but
     // we're not the last one. See if we have a matching fly.
 
-    for (const auto& pFly : GetSplitFlyDrawObjs())
+    // Look up the master of the anchor.
+    const SwTextFrame* pAnchor = this;
+    while (pAnchor->IsFollow())
     {
+        pAnchor = pAnchor->FindMaster();
+    }
+    for (const auto& pFly : pAnchor->GetSplitFlyDrawObjs())
+    {
+        // Nominally all flys are anchored in the master; see if this fly is 
effectively anchored in
+        // us.
+        SwTextFrame* pFlyAnchor = pFly->FindAnchorCharFrame();
+        if (pFlyAnchor != pAnchor)
+        {
+            continue;
+        }
         if (pFly->GetFollow())
         {
             return true;

Reply via email to