sw/qa/core/text/data/floattable-heading-split-hyphen.docx |binary sw/qa/core/text/widorp.cxx | 42 ++++++++++++++ sw/source/core/inc/cntfrm.hxx | 3 + sw/source/core/layout/calcmove.cxx | 20 ++++-- sw/source/core/layout/tabfrm.cxx | 15 +++++ sw/source/core/text/widorp.cxx | 10 +-- 6 files changed, 75 insertions(+), 15 deletions(-)
New commits: commit e03da71738c72bbaecb824b4ba356a0a9923a0ff Author: Miklos Vajna <vmik...@collabora.com> AuthorDate: Wed Jul 30 10:44:42 2025 +0200 Commit: Miklos Vajna <vmik...@collabora.com> CommitDate: Thu Jul 31 03:45:28 2025 +0200 Related: tdf#167222 sw floattable: fix split of fly and heading text fit check Open the bugdoc, the only floating table is at the top of page 4 in the document, while it's at the bottom of page 3 in Word. This time the layout decides in SwContentFrame::WouldFit_() that the floating table and its anchor text can't fit page 3 and instead of trying to split, it moves both to page 4. Fix the problem similar to what happens already in SwContentFrame::MakeAll() and SwTextFrameBreak, by ignoring "don't break" / "keep with next" when that would potentially result in not allowing a page break between the floating table and its anchor text. Also extract this "ignore value for split fly anchors" logic to a new SwContentFrame::IgnoringSplitFlyAnchor() to avoid duplication. Change-Id: Ic31f0e34c75d978b4d8b6cbe2451ee7e15ef2f4d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/188604 Tested-by: Jenkins Reviewed-by: Miklos Vajna <vmik...@collabora.com> diff --git a/sw/qa/core/text/data/floattable-heading-split-hyphen.docx b/sw/qa/core/text/data/floattable-heading-split-hyphen.docx new file mode 100644 index 000000000000..1ade6519e0a8 Binary files /dev/null and b/sw/qa/core/text/data/floattable-heading-split-hyphen.docx differ diff --git a/sw/qa/core/text/widorp.cxx b/sw/qa/core/text/widorp.cxx index 899624fe97d4..46e559b1eccd 100644 --- a/sw/qa/core/text/widorp.cxx +++ b/sw/qa/core/text/widorp.cxx @@ -11,6 +11,8 @@ #include <memory> +#include <com/sun/star/linguistic2/LinguServiceManager.hpp> + #include <IDocumentLayoutAccess.hxx> #include <doc.hxx> #include <frame.hxx> @@ -108,6 +110,46 @@ CPPUNIT_TEST_FIXTURE(Test, testFloattableHeadingSplitFooter) SwTextFrame* pPage3Para1 = pBody3->ContainsContent()->DynCastTextFrame(); CPPUNIT_ASSERT_EQUAL(u"page 3"_ustr, pPage3Para1->GetText()); } + +CPPUNIT_TEST_FIXTURE(Test, testFloattableHeadingSplitHyphen) +{ + // Page 3 top paragraph has a hyphenated word and changing that would influence the widow/orphan + // code, so just avoid this test if the English hyphen patterns are missing. + uno::Reference<linguistic2::XLinguServiceManager2> xLingu + = linguistic2::LinguServiceManager::create(m_xContext); + uno::Reference<linguistic2::XHyphenator> xHyphenator = xLingu->getHyphenator(); + if (!xHyphenator.is()) + { + return; + } + lang::Locale aLocale; + aLocale.Language = u"en"_ustr; + aLocale.Country = u"US"_ustr; + if (!xHyphenator->hasLocale(aLocale)) + { + return; + } + + // Given a document with a floating table on page 3: + // When loading that document & laying it out: + createSwDoc("floattable-heading-split-hyphen.docx"); + + // Then make sure that the floating table is on page 3 and the last heading + footnote is on + // page 4: + SwDocShell* pDocShell = getSwDocShell(); + SwDoc* pDoc = pDocShell->GetDoc(); + SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout(); + auto pPage1 = pLayout->Lower()->DynCastPageFrame(); + auto pPage2 = pPage1->GetNext()->DynCastPageFrame(); + auto pPage3 = pPage2->GetNext()->DynCastPageFrame(); + // Without the accompanying fix in place, this test would have failed, the floating table went + // to page 4, not to page 3. + CPPUNIT_ASSERT(pPage3->GetSortedObjs()); + CPPUNIT_ASSERT(!pPage3->FindFootnoteCont()); + auto pPage4 = pPage3->GetNext()->DynCastPageFrame(); + CPPUNIT_ASSERT(!pPage4->GetSortedObjs()); + CPPUNIT_ASSERT(pPage4->FindFootnoteCont()); +} } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/inc/cntfrm.hxx b/sw/source/core/inc/cntfrm.hxx index 224849a36af8..f2732cf0e8ee 100644 --- a/sw/source/core/inc/cntfrm.hxx +++ b/sw/source/core/inc/cntfrm.hxx @@ -117,6 +117,9 @@ public: inline SwContentFrame* GetPrevContentFrame() const; static bool CalcLowers(SwLayoutFrame & rLay, SwLayoutFrame const& rDontLeave, tools::Long nBottom, bool bSkipRowSpanCells); + + /// Returns bValue as is, unless this frame has split fly draw objects. + bool IgnoringSplitFlyAnchor(bool bValue) const; }; inline SwContentFrame* SwContentFrame::GetNextContentFrame() const diff --git a/sw/source/core/layout/calcmove.cxx b/sw/source/core/layout/calcmove.cxx index 2154f4b29164..6aafb9826e22 100644 --- a/sw/source/core/layout/calcmove.cxx +++ b/sw/source/core/layout/calcmove.cxx @@ -1373,15 +1373,11 @@ void SwContentFrame::MakeAll(vcl::RenderContext* /*pRenderContext*/) } bool bKeep{!isHiddenNow && IsKeep(rAttrs.GetAttrSet().GetKeep(), GetBreakItem())}; - if (bKeep && IsTextFrame()) + if (bKeep) { - auto pTextFrame = DynCastTextFrame(); - if (pTextFrame->HasSplitFlyDrawObjs()) - { - // The SwTextFrameBreak ctor already turns off bKeep for split fly anchors, don't - // change that decision here. - bKeep = false; - } + // The SwTextFrameBreak ctor already turns off bKeep for split fly anchors, don't + // change that decision here. + bKeep = IgnoringSplitFlyAnchor(bKeep); } std::unique_ptr<SwSaveFootnoteHeight> pSaveFootnote; @@ -2298,8 +2294,16 @@ bool SwContentFrame::WouldFit_( SwTwips nSpace, } } + bool bKeep = false; if (bRet && !bSplit && !isIgnoreKeep && pFrame->IsKeep(rAttrs.GetAttrSet().GetKeep(), GetBreakItem())) + { + // The SwTextFrameBreak ctor already turns off bKeep for split fly anchors, don't + // change that decision here. + bKeep = pFrame->IgnoringSplitFlyAnchor(true); + } + + if (bKeep) { if( bTstMove ) { diff --git a/sw/source/core/layout/tabfrm.cxx b/sw/source/core/layout/tabfrm.cxx index 145429b107b3..d79568dcf44e 100644 --- a/sw/source/core/layout/tabfrm.cxx +++ b/sw/source/core/layout/tabfrm.cxx @@ -1867,6 +1867,21 @@ bool SwContentFrame::CalcLowers(SwLayoutFrame & rLay, SwLayoutFrame const& rDont return bRet; } +bool SwContentFrame::IgnoringSplitFlyAnchor(bool bValue) const +{ + bool bRet = bValue; + if (IsTextFrame()) + { + auto pTextFrame = DynCastTextFrame(); + if (pTextFrame->HasSplitFlyDrawObjs()) + { + // Turn off the value for split fly anchors. + bRet = false; + } + } + return bRet; +} + // #i26945# - add parameter <_bOnlyRowsAndCells> to control // that only row and cell frames are formatted. static bool lcl_InnerCalcLayout( SwFrame *pFrame, diff --git a/sw/source/core/text/widorp.cxx b/sw/source/core/text/widorp.cxx index e5b02485ca50..70b0af630619 100644 --- a/sw/source/core/text/widorp.cxx +++ b/sw/source/core/text/widorp.cxx @@ -75,11 +75,11 @@ SwTextFrameBreak::SwTextFrameBreak( SwTextFrame *pNewFrame, const SwTwips nRst ) m_bKeep = m_bKeep || !m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetSplit().GetValue() || m_pFrame->GetTextNodeForParaProps()->GetSwAttrSet().GetKeep().GetValue(); - if (m_bKeep && m_pFrame->HasSplitFlyDrawObjs()) + if (m_bKeep) { // Ignore keep-together and keep-with-next if this is an anchor for a floating table. It's // OK to split the text frame into two, to separate the floating table and the anchor text. - m_bKeep = false; + m_bKeep = m_pFrame->IgnoringSplitFlyAnchor(m_bKeep); } m_bBreak = false; @@ -260,13 +260,9 @@ bool SwTextFrameBreak::IsBreakNow( SwTextMargin &rLine ) if( ( bFirstLine && m_pFrame->GetIndPrev() ) || ( rLine.GetLineNr() <= rLine.GetDropLines() ) ) { - bool bSplitFly = !m_bKeep && m_pFrame->HasSplitFlyDrawObjs(); // The SwTextFrameBreak ctor already turns off m_bKeep for split fly anchors, don't // change that decision here. - if (!bSplitFly) - { - m_bKeep = true; - } + m_bKeep = m_pFrame->IgnoringSplitFlyAnchor(true); m_bBreak = false; } else if(bFirstLine && m_pFrame->IsInFootnote() && !m_pFrame->FindFootnoteFrame()->GetPrev())