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: */