sw/qa/core/text/data/floattable-avoid-last-manip-ofst.docx |binary
 sw/qa/core/text/frmform.cxx                                |   37 +++++++++++++
 sw/source/core/text/frmform.cxx                            |   20 +++++--
 3 files changed, 54 insertions(+), 3 deletions(-)

New commits:
commit 262a1ae36eaf20e741759cd2c216456bbb472f7c
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Wed Aug 2 09:56:49 2023 +0200
Commit:     Michael Stahl <michael.st...@allotropia.de>
CommitDate: Thu Aug 3 11:55:19 2023 +0200

    tdf#156260 sw floattable: avoid moving text from the last anchor to its 
precede
    
    The 5 page long bugdoc had a "dt" in the last paragraph, expanding that
    to an actual dummy text with F3 resulted in anchor text on both page 4
    and page 5, while the expected behavior is that text only wraps on the
    last page, i.e. page 5.
    
    What happened is that the (text) "offset" of the text frame anchors on
    pages 1..5 are meant to be all 0 (so the actual anchor text is only on
    the last page), but in practice the anchor text frame on page 5 had an
    offset of 1123, which breaks the invariant that in case a fly frame is
    split to N pages, then only the last matching anchor text frame has
    text. (If it has too much text, then a next page with just anchor text
    is fine, but 0..N-1 pages should have no anchor text.)
    
    Fix the problem by SwTextFrame::AdjustFollow_(): in case it would reach
    the end for the "has follow, but no follow's follow" case, then still
    avoid setting the offset of the follow in case we know this is text
    frame has a non-last split fly anchored in it.
    
    With this, the render result matches in no overlaps and also what Word
    does in a similar edit session. (No F3 there, but can paste similar text
    at the document end.)
    
    Change-Id: I6c8d4f825d9dc356bb2a3a87a2f9c2e6529ce533
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155209
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins
    (cherry picked from commit b6a22e2be79cd874c7526107a6793fae692620dc)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155290
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>

diff --git a/sw/qa/core/text/data/floattable-avoid-last-manip-ofst.docx 
b/sw/qa/core/text/data/floattable-avoid-last-manip-ofst.docx
new file mode 100644
index 000000000000..017b3001b813
Binary files /dev/null and 
b/sw/qa/core/text/data/floattable-avoid-last-manip-ofst.docx differ
diff --git a/sw/qa/core/text/frmform.cxx b/sw/qa/core/text/frmform.cxx
index d23611a7eb05..f2f942bde324 100644
--- a/sw/qa/core/text/frmform.cxx
+++ b/sw/qa/core/text/frmform.cxx
@@ -17,6 +17,8 @@
 #include <anchoredobject.hxx>
 #include <pagefrm.hxx>
 #include <txtfrm.hxx>
+#include <docsh.hxx>
+#include <wrtsh.hxx>
 
 namespace
 {
@@ -79,6 +81,41 @@ CPPUNIT_TEST_FIXTURE(Test, testFloattableAvoidManipOfst)
     // anchors of non-last split fly frames should contain no text.
     CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0), 
pAnchor->GetOffset().get());
 }
+
+CPPUNIT_TEST_FIXTURE(Test, testFloattableAvoidLastManipOfst)
+{
+    // Given a document with a 5-page floating table and some anchor text:
+    createSwDoc("floattable-avoid-last-manip-ofst.docx");
+    SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell();
+    pWrtShell->SttEndDoc(/*bStt=*/false);
+    pWrtShell->Insert2("dt");
+
+    // When expanding dummy text on the last page:
+    dispatchCommand(mxComponent, ".uno:ExpandGlossary", {});
+
+    // Then make sure the expanded text starts on page 5:
+    SwDoc* pDoc = getSwDocShell()->GetDoc();
+    SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
+    auto pPage1 = dynamic_cast<SwPageFrame*>(pLayout->Lower());
+    CPPUNIT_ASSERT(pPage1);
+    auto pPage2 = dynamic_cast<SwPageFrame*>(pPage1->GetNext());
+    CPPUNIT_ASSERT(pPage2);
+    auto pPage3 = dynamic_cast<SwPageFrame*>(pPage2->GetNext());
+    CPPUNIT_ASSERT(pPage3);
+    auto pPage4 = dynamic_cast<SwPageFrame*>(pPage3->GetNext());
+    CPPUNIT_ASSERT(pPage4);
+    auto pPage5 = dynamic_cast<SwPageFrame*>(pPage4->GetNext());
+    CPPUNIT_ASSERT(pPage5);
+    SwContentFrame* pAnchor = pPage5->FindFirstBodyContent();
+    SwTextFrame* pAnchorText = pAnchor->DynCastTextFrame();
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 0
+    // - Actual  : 1123
+    // i.e. the expand result went to page 4 and page 5 (page 5's content had 
no zero offset),
+    // instead of starting on page 5 (and creating a 6th page).
+    CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(0),
+                         static_cast<sal_Int32>(pAnchorText->GetOffset()));
+}
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/text/frmform.cxx b/sw/source/core/text/frmform.cxx
index cdf51563c0ae..c3c7f872b88a 100644
--- a/sw/source/core/text/frmform.cxx
+++ b/sw/source/core/text/frmform.cxx
@@ -602,7 +602,8 @@ void SwTextFrame::AdjustFollow_( SwTextFormatter &rLine,
     // We got the rest of the text mass: Delete all Follows
     // DummyPortions() are a special case.
     // Special cases are controlled by parameter <nMode>.
-    if( HasFollow() && !(nMode & 1) && nOffset == nEnd )
+    bool bDontJoin = nMode & 1;
+    if( HasFollow() && !bDontJoin && nOffset == nEnd )
     {
         while( GetFollow() )
         {
@@ -634,14 +635,20 @@ void SwTextFrame::AdjustFollow_( SwTextFormatter &rLine,
     const TextFrameIndex nNewOfst = (IsInFootnote() && (!GetIndNext() || 
HasFollow()))
         ? rLine.FormatQuoVadis(nOffset) : nOffset;
 
-    if( !(nMode & 1) )
+    bool bHasNonLastSplitFlyDrawObj = false;
+    if (GetFollow() && GetOffset() == GetFollow()->GetOffset())
+    {
+        bHasNonLastSplitFlyDrawObj = HasNonLastSplitFlyDrawObj();
+    }
+
+    if( !bDontJoin )
     {
         // We steal text mass from our Follows
         // It can happen that we have to join some of them
         while( GetFollow() && GetFollow()->GetFollow() &&
                nNewOfst >= GetFollow()->GetFollow()->GetOffset() )
         {
-            if (HasNonLastSplitFlyDrawObj())
+            if (bHasNonLastSplitFlyDrawObj)
             {
                 // A non-last split fly is anchored to us, don't move content 
from the last frame to
                 // this one and don't join.
@@ -661,6 +668,13 @@ void SwTextFrame::AdjustFollow_( SwTextFormatter &rLine,
     // The Offset moved
     if( GetFollow() )
     {
+        if (!bDontJoin && bHasNonLastSplitFlyDrawObj)
+        {
+            // A non-last split fly is anchored to us, our follow is the last 
one in the text frame
+            // chain. No move of text from that follow to this text frame.
+            return;
+        }
+
         if ( nMode )
             GetFollow()->ManipOfst(TextFrameIndex(0));
 

Reply via email to