sw/qa/extras/layout/data/tdf170477.docx |binary sw/qa/extras/layout/layout5.cxx | 7 +++++++ sw/source/core/layout/tabfrm.cxx | 27 +++++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 2 deletions(-)
New commits: commit bcf6c52cff15147d25ef68df02ac81a0020285cd Author: Mike Kaganski <[email protected]> AuthorDate: Mon Jan 26 10:46:04 2026 +0500 Commit: Miklos Vajna <[email protected]> CommitDate: Tue Jan 27 09:45:54 2026 +0100 tdf#170477: detect MoveFwd oscillation in SwTabFrame::MakeAll In the specific bug document, some constellation of factors created the case where the last inner floating table tries to move forward, in the process creates a split fly on the same page (subject to move forward later, when outer table formats), moves to it (leaving the old fly empty), which recreates the same layout as before; and that oscillated infinitely. The fix implemented here is to detect when MoveFwd call created the described situation, and then use SwLayouter::InsertMovedFwdFrame to break the oscillation. I was not able to reproduce this problem anew; test document was simplified from the original, with the aim to minimize instability (but still, the resulting layout in Writer differs from Word's, which makes the test likely to stop testing the intended code path in the future - sigh). Change-Id: Iaa796a169cb9f97d5a6b3dd7ebf1cf5b726ca568 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198116 Reviewed-by: Mike Kaganski <[email protected]> Tested-by: Jenkins Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198139 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> diff --git a/sw/qa/extras/layout/data/tdf170477.docx b/sw/qa/extras/layout/data/tdf170477.docx new file mode 100644 index 000000000000..646d7191c92d Binary files /dev/null and b/sw/qa/extras/layout/data/tdf170477.docx differ diff --git a/sw/qa/extras/layout/layout5.cxx b/sw/qa/extras/layout/layout5.cxx index b0cde5f76107..9c4d94099d5b 100644 --- a/sw/qa/extras/layout/layout5.cxx +++ b/sw/qa/extras/layout/layout5.cxx @@ -2342,6 +2342,13 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter5, testTdf170381_split_float_table_in_float_t assertCellLines(2, r + 1, page2cells[r]); } +CPPUNIT_TEST_FIXTURE(SwLayoutWriter5, testTdf170477) +{ + // This document must not hang on layout: + createSwDoc("tdf170477.docx"); + calcLayout(); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx index 6163e6660a4a..e40b38fdc3d1 100644 --- a/sw/source/core/layout/tabfrm.cxx +++ b/sw/source/core/layout/tabfrm.cxx @@ -3270,6 +3270,7 @@ void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext) // #i29771# Reset bTryToSplit flag on change of upper const SwFrame* pOldUpper = GetUpper(); + const SwPageFrame* pOldUpperPage = nullptr; //Let's see if we find some place anywhere... if (!bMovedFwd) @@ -3284,6 +3285,7 @@ void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext) // If the anchor of the split has a previous frame, MoveFwd() is allowed to move // forward. bMoveAlways = true; + pOldUpperPage = pFlyFrame->FindPageFrame(); } } // don't make the effort to move fwd if its known @@ -3297,8 +3299,29 @@ void SwTabFrame::MakeAll(vcl::RenderContext* pRenderContext) // #i29771# Reset bSplitError flag on change of upper if ( GetUpper() != pOldUpper ) { - bTryToSplit = true; - nUnSplitted = 5; + bool bResetSplit = true; + if (GetUpper() && GetUpper()->IsFlyFrame() && pOldUpperPage + && GetUpper()->FindPageFrame() == pOldUpperPage + && static_cast<const SwFlyFrame*>(GetUpper())->IsFlySplitAllowed()) + { + // MoveFwd created a split (follow) fly on the same page, that was intended to move + // to the next page together with its anchor. Then the content was moved to the new + // fly, and the old fly became empty; it called its DelEmpty(). The new fly is now + // basically a copy of the old one; move to the next page and prevent oscillation. + SwTextFrame* pAnchor = static_cast<SwFlyFrame*>(GetUpper())->FindAnchorCharFrame(); + if (pAnchor) + { + bResetSplit = false; + SwPageFrame* pPage = pAnchor->FindPageFrame(); + SwLayouter::InsertMovedFwdFrame(*pPage->GetFormat()->GetDoc(), *pAnchor, + pPage->GetPhyPageNum() + 1); + } + } + if (bResetSplit) + { + bTryToSplit = true; + nUnSplitted = 5; + } } aRectFnSet.Refresh(this);
