Rebased ref, commits from common ancestor:
commit 02c3a83e2e86fc51f761169aa8e96d73e720389e
Author:     Andras Timar <[email protected]>
AuthorDate: Wed Dec 3 11:09:49 2025 +0100
Commit:     Andras Timar <[email protected]>
CommitDate: Wed Dec 3 13:52:39 2025 +0100

    Bump version to 25.2.7.3
    
    Change-Id: I1bec395a1e68bbf5b40e9247626bfcc674709a8f

diff --git a/configure.ac b/configure.ac
index 1a65b45eaa69..14e4f0085e8b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -9,7 +9,7 @@ dnl in order to create a configure script.
 # several non-alphanumeric characters, those are split off and used only for 
the
 # ABOUTBOXPRODUCTVERSIONSUFFIX in openoffice.lst. Why that is necessary, no 
idea.
 
-AC_INIT([LibreOffice],[25.2.7.2],[],[],[http://documentfoundation.org/])
+AC_INIT([LibreOffice],[25.2.7.3],[],[],[http://documentfoundation.org/])
 
 dnl libnumbertext needs autoconf 2.68, but that can pick up autoconf268 just 
fine if it is installed
 dnl whereas aclocal (as run by autogen.sh) insists on using autoconf and fails 
hard
commit feca4539651314b099f3f3f13cca980bd621fd47
Author:     Mike Kaganski <[email protected]>
AuthorDate: Thu Nov 27 16:44:53 2025 +0500
Commit:     Andras Timar <[email protected]>
CommitDate: Wed Dec 3 13:52:35 2025 +0100

    tdf#169399: try again when frames were moved forward by objects
    
    The problem in the bug document was caused by the extra call to
    SwFrame::OptCalc in SwLayAction::FormatLayout. It was there since
    initial import, as a shortcut; but its conditions were changed in
    commits 610c6f02b11b4b4c555a78b0feb2a1eb35159e39 (tdf#156724
    tdf#156722 tdf#156745 sw: layout: partially remove IsPaintLocked(),
    2023-08-14), 61a78a523a6131ff98b5d846368e5626fe58d99c (tdf#156724
    tdf#156722 sw: layout: remove IsPaintLocked() check, 2023-08-15).
    Commit c303981cfd95ce1c3881366023d5495ae2edce97 (tdf#156724 sw:
    layout: fix tables not splitting due to footnotes differently,
    2023-08-24) had dropped that code completely, but it was restored
    in commit 397d72e582c725d162c7e0b819dc6c0bb62e42b0 (Related:
    tdf#158986 sw floattable: fix unexpected page break with sections,
    2024-02-23), with more strict conditions. Its conditions were
    restricted in commit 607fcac441c7f3a7d3c169c19039e581d707f2bb
    (tdf#160067 sw floattable: fix missing move bwd of paras in split
    section frame, 2024-04-08). There, it was noted, that "probably a
    cleaner way would be to completely stop calculating content frames
    in SwLayAction::FormatLayout() and only do that in FormatContent()",
    which emphasizes the problematic nature of that code.
    
    I looked into restricting the conditions there more for the bug
    document, but failed to come with the working condition. So the
    change drops that code again, and tries to keep the bugs fixed by
    re-introduction of that code fixed.
    
    My understanding of the mechanism causing tdf#158986 is following:
    
    Document's SwLayouter keeps track of the nodes, which frames were
    moved forward by objects. This data will disallow those frames from
    moving backwards (see uses of SwLayouter::FrameMovedFwdByObjPos).
    
    The problem in the bug document comes from the fly attached to a
    paragraph inside a multicolumn section. During the iterative layout
    of the section, there is a stage where the height of the section is
    too small for the object, and its paragraph gets moved forward to
    a new page (together with some previous paragraphs). When layout
    finishes, the height of the section is enough, but the paragraph
    cannot move back, keeping the extra page.
    
    I tried hard to solve it in many less-bruteforce ways. The ideas
    included prevention of the wrong too small section height during
    layout (e.g., in lcl_FormatContentOfLayoutFrame); attempts to find
    a place where a specific "moved forward" record becomes obsolete
    and should be removed (tried to modify fly.cxx' CalcContent and
    SwObjectFormatterTextFrame::DoFormatObj); and modifications to let
    page body's resize code (called when a section's height is changed)
    to re-position the objects. But all these attempts produced layout
    loops.
    
    This change introduces a hack. In SwLayAction::Action, when the
    first call to InternalAction increases the number of frames that
    were moved forward, it clears the layouter entries, and tries once
    more. I hope that at some point, a better fix will be possible.
    
    Change-Id: I47315473982cb1fe5c8d77f7603cd24de08afa89
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/194712
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/194755
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/sw/qa/extras/layout/data/tdf169399.fodt 
b/sw/qa/extras/layout/data/tdf169399.fodt
new file mode 100644
index 000000000000..29a375ca84a8
--- /dev/null
+++ b/sw/qa/extras/layout/data/tdf169399.fodt
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" 
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" 
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 
xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" 
xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" 
office:version="1.4" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:styles>
+  <style:style style:name="Frame" style:family="graphic"/>
+ </office:styles>
+ <office:automatic-styles>
+  <style:style style:name="P1" style:family="paragraph">
+   <style:paragraph-properties fo:line-height="115%"/>
+  </style:style>
+  <style:style style:name="fr1" style:family="graphic" 
style:parent-style-name="Frame">
+   <style:graphic-properties style:wrap="none" style:vertical-pos="from-top" 
style:vertical-rel="paragraph" style:horizontal-pos="from-left" 
style:horizontal-rel="paragraph-content" fo:border="0.06pt solid #000000"/>
+  </style:style>
+  <style:page-layout style:name="pm1">
+   <style:page-layout-properties fo:page-width="210mm" fo:page-height="297mm" 
style:print-orientation="portrait" fo:margin-top="15mm" fo:margin-bottom="1cm" 
fo:margin-left="2cm" fo:margin-right="2cm"/>
+  </style:page-layout>
+ </office:automatic-styles>
+ <office:master-styles>
+  <style:master-page style:name="Standard" style:page-layout-name="pm1"/>
+ </office:master-styles>
+ <office:body>
+  <office:text>
+   <text:p>p1</text:p>
+   <text:p><draw:frame draw:style-name="fr1" draw:name="Frame1" 
text:anchor-type="paragraph" svg:x="0" svg:y="0" svg:width="5cm" 
svg:height="78mm">
+     <draw:text-box/>
+    </draw:frame>p2</text:p>
+   <text:section text:name="Section1">
+    <text:p>p3</text:p>
+   </text:section>
+   <text:p>p4</text:p>
+   <text:section text:name="Section2">
+    <text:p>p5</text:p>
+   </text:section>
+   <text:section text:name="Section3">
+    <text:p>p6</text:p>
+   </text:section>
+   <text:p>p7</text:p>
+   <text:section text:name="Section4">
+    <text:p 
text:style-name="P1">p8<text:line-break/><text:line-break/><text:line-break/><text:line-break/><text:line-break/><text:line-break/></text:p>
+    <text:section text:name="Section5">
+     <text:p>p9 must be on page 1</text:p>
+     <text:p>p10</text:p>
+    </text:section>
+   </text:section>
+  </office:text>
+ </office:body>
+</office:document>
\ No newline at end of file
diff --git a/sw/qa/extras/layout/layout5.cxx b/sw/qa/extras/layout/layout5.cxx
index 6ec968e1dd70..fb5e1fddde6a 100644
--- a/sw/qa/extras/layout/layout5.cxx
+++ b/sw/qa/extras/layout/layout5.cxx
@@ -1820,6 +1820,15 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter5, testTdf166181)
     CPPUNIT_ASSERT_LESS(sal_Int32(500), height.toInt32());
 }
 
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter5, testTdf169399)
+{
+    createSwDoc("tdf169399.fodt");
+
+    xmlDocUniquePtr pXmlDoc = parseLayoutDump();
+    // Without the fix, this failed, because there were two pages:
+    assertXPath(pXmlDoc, "/root/page", 1);
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentLayoutManager.cxx 
b/sw/source/core/doc/DocumentLayoutManager.cxx
index c1d7cfb2edf7..abf24927db4b 100644
--- a/sw/source/core/doc/DocumentLayoutManager.cxx
+++ b/sw/source/core/doc/DocumentLayoutManager.cxx
@@ -492,6 +492,17 @@ void DocumentLayoutManager::ClearSwLayouterEntries()
     SwLayouter::ClearMoveBwdLayoutInfo( m_rDoc );
 }
 
+size_t DocumentLayoutManager::GetMovedFwdFramesCount() const
+{
+    return SwLayouter::GetMovedFwdFramesCount(m_rDoc);
+}
+
+void DocumentLayoutManager::ClearSwLayouterEntriesWithInvalidation()
+{
+    SwLayouter::InvalidateMovedFwdFrames(m_rDoc);
+    ClearSwLayouterEntries();
+}
+
 DocumentLayoutManager::~DocumentLayoutManager()
 {
 }
diff --git a/sw/source/core/inc/DocumentLayoutManager.hxx 
b/sw/source/core/inc/DocumentLayoutManager.hxx
index 8db5cbe328fe..42315aeb6d55 100644
--- a/sw/source/core/inc/DocumentLayoutManager.hxx
+++ b/sw/source/core/inc/DocumentLayoutManager.hxx
@@ -54,6 +54,8 @@ public:
 
     //Non Interface methods
     void ClearSwLayouterEntries();
+    size_t GetMovedFwdFramesCount() const;
+    void ClearSwLayouterEntriesWithInvalidation();
 
     virtual ~DocumentLayoutManager() override;
 
diff --git a/sw/source/core/inc/layouter.hxx b/sw/source/core/inc/layouter.hxx
index 34d025843ccd..afd3d98370b7 100644
--- a/sw/source/core/inc/layouter.hxx
+++ b/sw/source/core/inc/layouter.hxx
@@ -117,6 +117,8 @@ public:
     static bool FrameMovedFwdByObjPos( const SwDoc& _rDoc,
                                      const SwTextFrame& _rTextFrame,
                                      sal_uInt32& _ornToPageNum );
+    static size_t GetMovedFwdFramesCount(const SwDoc& _rDoc);
+    static void InvalidateMovedFwdFrames(const SwDoc& _rDoc);
     // --> #i40155# - unmark given frame as to be moved forward.
     static void RemoveMovedFwdFrame( const SwDoc& _rDoc,
                                    const SwTextFrame& _rTextFrame );
diff --git a/sw/source/core/inc/movedfwdfrmsbyobjpos.hxx 
b/sw/source/core/inc/movedfwdfrmsbyobjpos.hxx
index c42944060ced..c1f6a639201f 100644
--- a/sw/source/core/inc/movedfwdfrmsbyobjpos.hxx
+++ b/sw/source/core/inc/movedfwdfrmsbyobjpos.hxx
@@ -51,6 +51,10 @@ class SwMovedFwdFramesByObjPos
         bool DoesRowContainMovedFwdFrame( const SwRowFrame& _rRowFrame ) const;
 
         void Clear() { maMovedFwdFrames.clear(); };
+
+        size_t Count() const { return maMovedFwdFrames.size(); }
+
+        void InvalidateAll();
 };
 
 #endif
diff --git a/sw/source/core/layout/layact.cxx b/sw/source/core/layout/layact.cxx
index db71081a8e49..c4cfb2d625a5 100644
--- a/sw/source/core/layout/layact.cxx
+++ b/sw/source/core/layout/layact.cxx
@@ -36,6 +36,7 @@
 #include <IDocumentDrawModelAccess.hxx>
 #include <IDocumentStatistics.hxx>
 #include <IDocumentLayoutAccess.hxx>
+#include <DocumentLayoutManager.hxx>
 
 #include <sfx2/event.hxx>
 
@@ -393,9 +394,20 @@ void SwLayAction::Action(OutputDevice* pRenderContext)
     if ( IsCalcLayout() )
         SetCheckPages( false );
 
+    // tdf#169399: workaround for frames unable to move backward after moved 
forward by objects
+    // in incomplete layout
+    auto& rLayoutManager = 
m_pRoot->GetFormat()->GetDoc()->GetDocumentLayoutManager();
+    const auto nOldMovedCount = rLayoutManager.GetMovedFwdFramesCount();
+
     InternalAction(pRenderContext);
     if (RemoveEmptyBrowserPages())
         SetAgain(true);
+    if (nOldMovedCount < rLayoutManager.GetMovedFwdFramesCount())
+    {
+        // Only do it once
+        rLayoutManager.ClearSwLayouterEntriesWithInvalidation();
+        SetAgain(true);
+    }
     while ( IsAgain() )
     {
         SetAgain(false);
@@ -1475,11 +1487,6 @@ bool SwLayAction::FormatLayout( OutputDevice 
*pRenderContext, SwLayoutFrame *pLa
                 PopFormatLayout();
             }
         }
-        else if (pLay->IsSctFrame() && pLay->GetNext() && 
pLay->GetNext()->IsSctFrame() && pLow->IsTextFrame() && pLow == 
pLay->GetLastLower())
-        {
-            // else: only calc the last text lower of sections, followed by 
sections
-            pLow->OptCalc();
-        }
 
         if ( IsAgain() )
             return false;
diff --git a/sw/source/core/layout/layouter.cxx 
b/sw/source/core/layout/layouter.cxx
index a9dcd3a45e5d..08d4fdcd4c59 100644
--- a/sw/source/core/layout/layouter.cxx
+++ b/sw/source/core/layout/layouter.cxx
@@ -368,6 +368,27 @@ bool SwLayouter::FrameMovedFwdByObjPos( const SwDoc& _rDoc,
     }
 }
 
+// static
+size_t SwLayouter::GetMovedFwdFramesCount(const SwDoc& _rDoc)
+{
+    if (_rDoc.getIDocumentLayoutAccess().GetLayouter()
+        && _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames)
+    {
+        return 
_rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames->Count();
+    }
+    return 0;
+}
+
+// static
+void SwLayouter::InvalidateMovedFwdFrames(const SwDoc& _rDoc)
+{
+    if (_rDoc.getIDocumentLayoutAccess().GetLayouter()
+        && _rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames)
+    {
+        
_rDoc.getIDocumentLayoutAccess().GetLayouter()->mpMovedFwdFrames->InvalidateAll();
+    }
+}
+
 // #i26945#
 bool SwLayouter::DoesRowContainMovedFwdFrame( const SwDoc& _rDoc,
                                             const SwRowFrame& _rRowFrame )
diff --git a/sw/source/core/layout/movedfwdfrmsbyobjpos.cxx 
b/sw/source/core/layout/movedfwdfrmsbyobjpos.cxx
index 7aba4b74a37b..a99e35110324 100644
--- a/sw/source/core/layout/movedfwdfrmsbyobjpos.cxx
+++ b/sw/source/core/layout/movedfwdfrmsbyobjpos.cxx
@@ -87,4 +87,19 @@ bool SwMovedFwdFramesByObjPos::DoesRowContainMovedFwdFrame( 
const SwRowFrame& _r
     return bDoesRowContainMovedFwdFrame;
 }
 
+void SwMovedFwdFramesByObjPos::InvalidateAll()
+{
+    for (const auto& rEntry : maMovedFwdFrames)
+    {
+        SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> 
aFrameIter(
+            *rEntry.first);
+        for (SwTextFrame* pTextFrame = aFrameIter.First(); pTextFrame;
+             pTextFrame = aFrameIter.Next())
+        {
+            pTextFrame->InvalidatePos();
+            pTextFrame->InvalidatePage();
+        }
+    }
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to