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

Reply via email to