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));