sw/qa/extras/layout/data/footer_field_rest_portion.fodt |  156 ++++++++++++++++
 sw/qa/extras/layout/layout2.cxx                         |   48 ++++
 sw/source/core/text/itrtxt.cxx                          |   17 -
 3 files changed, 213 insertions(+), 8 deletions(-)

New commits:
commit 8fdcc9c181b07925b86f112648f4028edb05e019
Author:     Michael Stahl <[email protected]>
AuthorDate: Wed Nov 5 16:23:58 2025 +0100
Commit:     Michael Stahl <[email protected]>
CommitDate: Thu Nov 6 13:19:09 2025 +0100

    tdf#136220 sw: text formatting: fix breaking line with multiple fields
    
    ... when there is an anchored object in the footer, and the last field
    fits only partially on the line.
    
    This stopped working with commit 8e3afdb5989d571410350f1d43fcf26492a4eaff
    removing the IsFooterFrame() check in SwTextFly::GetTop().
    
    Previously a new line below the last line that fits on the page started
    with the field rest portion, when the not-fitting line is deleted in
    SwTextIter::TruncLines() this caused pDel->IsRest() to be true and
    setting SetFollowField(true) which later caused the mbFieldFollow to be
    set in the follow text frame.
    
    Now the pDel->IsRest() is false because the line contains only a big fly
    portion due to the fly in the footer, and then SetFieldFollow(false) and
    the follow doesn't start with the rest of the field.
    
    There is a long standing bug in lcl_NeedsFieldRest() causing this, it
    simply doesn't work if there are multiple fields in a line, it
    erroneously considers the first field to be the last one in the line.
    
    Change-Id: I99e9f68e8e9f8019e4d22ce5d7874d4cdac285e5
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193474
    Reviewed-by: Michael Stahl <[email protected]>
    Tested-by: Jenkins
    (cherry picked from commit d2c387bc609a235406b5c6c434ac4cc06a268513)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193525
    Tested-by: allotropia jenkins <[email protected]>

diff --git a/sw/qa/extras/layout/data/footer_field_rest_portion.fodt 
b/sw/qa/extras/layout/data/footer_field_rest_portion.fodt
new file mode 100644
index 000000000000..dc1dd5c1dc41
--- /dev/null
+++ b/sw/qa/extras/layout/data/footer_field_rest_portion.fodt
@@ -0,0 +1,156 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<office:document xmlns:officeooo="http://openoffice.org/2009/office"; 
xmlns:css3t="http://www.w3.org/TR/css3-text/"; 
xmlns:grddl="http://www.w3.org/2003/g/data-view#"; 
xmlns:xhtml="http://www.w3.org/1999/xhtml"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xmlns:xsd="http://www.w3.org/2001/XMLSchema"; 
xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" 
xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" 
xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" 
xmlns:rpt="http://openoffice.org/2005/report"; 
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 
xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" 
xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" 
xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" 
xmlns:xlink="http://www.w3.org/1999/xlink"; 
xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" 
xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" 
xmlns:dc="http://purl.org/dc/eleme
 nts/1.1/" xmlns:ooo="http://openoffice.org/2004/office"; 
xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" 
xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0"
 
xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0"
 xmlns:drawooo="http://openoffice.org/2010/draw"; 
xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" 
xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" 
xmlns:ooow="http://openoffice.org/2004/writer"; 
xmlns:oooc="http://openoffice.org/2004/calc"; 
xmlns:tableooo="http://openoffice.org/2009/table"; 
xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0"
 xmlns:math="http://www.w3.org/1998/Math/MathML"; 
xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" 
xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0
 " xmlns:dom="http://www.w3.org/2001/xml-events"; 
xmlns:xforms="http://www.w3.org/2002/xforms"; office:version="1.2" 
office:mimetype="application/vnd.oasis.opendocument.text">
+ 
<office:meta><meta:creation-date>2025-11-05T15:56:32.678347631</meta:creation-date><meta:editing-cycles>3</meta:editing-cycles><meta:editing-duration>PT18M21S</meta:editing-duration><dc:subject>the
 subject is testing a text formatting 
bug</dc:subject><dc:date>2025-11-05T16:14:53.609128781</dc:date><meta:generator>CIB_OfficeDev/6.4.0.36$Linux_X86_64
 
LibreOffice_project/f7cbc2488cf71408796fcfc8ecfd7a17e72d3ef7</meta:generator><meta:document-statistic
 meta:table-count="0" meta:image-count="1" meta:object-count="0" 
meta:page-count="2" meta:paragraph-count="2" meta:word-count="39" 
meta:character-count="172" 
meta:non-whitespace-character-count="131"/></office:meta>
+ <office:font-face-decls>
+  <style:font-face style:name="Liberation Serif" svg:font-family="'Liberation 
Serif'" style:font-family-generic="roman" style:font-pitch="variable"/>
+  <style:font-face style:name="Lucida Sans" svg:font-family="'Lucida Sans'" 
style:font-family-generic="system" style:font-pitch="variable"/>
+  <style:font-face style:name="Noto Serif CJK SC" svg:font-family="'Noto Serif 
CJK SC'" style:font-family-generic="system" style:font-pitch="variable"/>
+ </office:font-face-decls>
+ <office:styles>
+  <style:default-style style:family="graphic">
+   <style:graphic-properties svg:stroke-color="#3465a4" 
draw:fill-color="#729fcf" fo:wrap-option="no-wrap" draw:shadow-offset-x="0.3cm" 
draw:shadow-offset-y="0.3cm" draw:start-line-spacing-horizontal="0.283cm" 
draw:start-line-spacing-vertical="0.283cm" 
draw:end-line-spacing-horizontal="0.283cm" 
draw:end-line-spacing-vertical="0.283cm" style:flow-with-text="false"/>
+   <style:paragraph-properties style:text-autospace="ideograph-alpha" 
style:line-break="strict" style:writing-mode="lr-tb" 
style:font-independent-line-spacing="false">
+    <style:tab-stops/>
+   </style:paragraph-properties>
+   <style:text-properties style:use-window-font-color="true" 
style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="de" 
fo:country="DE" style:letter-kerning="true" style:font-name-asian="Noto Serif 
CJK SC" style:font-size-asian="10.5pt" style:language-asian="zh" 
style:country-asian="CN" style:font-name-complex="Lucida Sans" 
style:font-size-complex="12pt" style:language-complex="hi" 
style:country-complex="IN"/>
+  </style:default-style>
+  <style:default-style style:family="paragraph">
+   <style:paragraph-properties fo:orphans="2" fo:widows="2" 
fo:hyphenation-ladder-count="no-limit" style:text-autospace="ideograph-alpha" 
style:punctuation-wrap="hanging" style:line-break="strict" 
style:tab-stop-distance="1.251cm" style:writing-mode="page"/>
+   <style:text-properties style:use-window-font-color="true" 
style:font-name="Liberation Serif" fo:font-size="12pt" fo:language="de" 
fo:country="DE" style:letter-kerning="true" style:font-name-asian="Noto Serif 
CJK SC" style:font-size-asian="10.5pt" style:language-asian="zh" 
style:country-asian="CN" style:font-name-complex="Lucida Sans" 
style:font-size-complex="12pt" style:language-complex="hi" 
style:country-complex="IN" fo:hyphenate="false" 
fo:hyphenation-remain-char-count="2" fo:hyphenation-push-char-count="2" 
loext:hyphenation-no-caps="false"/>
+  </style:default-style>
+  <style:default-style style:family="table">
+   <style:table-properties table:border-model="collapsing"/>
+  </style:default-style>
+  <style:default-style style:family="table-row">
+   <style:table-row-properties fo:keep-together="auto"/>
+  </style:default-style>
+  <style:style style:name="Standard" style:family="paragraph" 
style:class="text"/>
+  <style:style style:name="Header_20_and_20_Footer" style:display-name="Header 
and Footer" style:family="paragraph" style:parent-style-name="Standard" 
style:class="extra">
+   <style:paragraph-properties text:number-lines="false" text:line-number="0">
+    <style:tab-stops>
+     <style:tab-stop style:position="8.5cm" style:type="center"/>
+     <style:tab-stop style:position="17cm" style:type="right"/>
+    </style:tab-stops>
+   </style:paragraph-properties>
+  </style:style>
+  <style:style style:name="Footer" style:family="paragraph" 
style:parent-style-name="Header_20_and_20_Footer" style:class="extra">
+   <style:paragraph-properties text:number-lines="false" text:line-number="0">
+    <style:tab-stops>
+     <style:tab-stop style:position="8.5cm" style:type="center"/>
+     <style:tab-stop style:position="17cm" style:type="right"/>
+    </style:tab-stops>
+   </style:paragraph-properties>
+  </style:style>
+  <style:style style:name="Graphics" style:family="graphic">
+   <style:graphic-properties text:anchor-type="paragraph" svg:x="0cm" 
svg:y="0cm" style:wrap="dynamic" style:number-wrapped-paragraphs="no-limit" 
style:wrap-contour="false" style:vertical-pos="top" 
style:vertical-rel="paragraph" style:horizontal-pos="center" 
style:horizontal-rel="paragraph"/>
+  </style:style>
+  <text:outline-style style:name="Outline">
+   <text:outline-level-style text:level="1" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="2" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="3" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="4" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="5" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="6" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="7" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="8" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="9" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+   <text:outline-level-style text:level="10" style:num-format="">
+    <style:list-level-properties 
text:list-level-position-and-space-mode="label-alignment">
+     <style:list-level-label-alignment text:label-followed-by="listtab"/>
+    </style:list-level-properties>
+   </text:outline-level-style>
+  </text:outline-style>
+  <text:notes-configuration text:note-class="footnote" style:num-format="1" 
text:start-value="0" text:footnotes-position="page" 
text:start-numbering-at="document"/>
+  <text:notes-configuration text:note-class="endnote" style:num-format="i" 
text:start-value="0"/>
+  <text:linenumbering-configuration text:number-lines="false" 
text:offset="0.499cm" style:num-format="1" text:number-position="left" 
text:increment="5"/>
+ </office:styles>
+ <office:automatic-styles>
+  <style:style style:name="P1" style:family="paragraph" 
style:parent-style-name="Standard" style:master-page-name="">
+   <style:paragraph-properties fo:margin-top="0cm" fo:margin-bottom="0cm" 
loext:contextual-spacing="false" fo:orphans="0" fo:widows="0" 
style:page-number="auto"/>
+  </style:style>
+  <style:style style:name="P2" style:family="paragraph" 
style:parent-style-name="Standard" style:master-page-name="">
+   <style:paragraph-properties fo:margin-top="19.812cm" fo:margin-bottom="0cm" 
loext:contextual-spacing="false" style:page-number="auto"/>
+  </style:style>
+  <style:style style:name="fr1" style:family="graphic" 
style:parent-style-name="Graphics">
+   <style:graphic-properties style:vertical-pos="from-top" 
style:vertical-rel="paragraph" style:horizontal-pos="from-left" 
style:horizontal-rel="paragraph" style:mirror="none" fo:clip="rect(0cm, 0cm, 
0cm, 0cm)" draw:luminance="0%" draw:contrast="0%" draw:red="0%" draw:green="0%" 
draw:blue="0%" draw:gamma="100%" draw:color-inversion="false" 
draw:image-opacity="100%" draw:color-mode="standard" 
style:flow-with-text="true"/>
+  </style:style>
+  <style:page-layout style:name="pm1">
+   <style:page-layout-properties fo:page-width="21.001cm" 
fo:page-height="29.7cm" style:num-format="1" style:print-orientation="portrait" 
fo:margin-top="2cm" fo:margin-bottom="2cm" fo:margin-left="2cm" 
fo:margin-right="2cm" style:writing-mode="lr-tb" 
style:footnote-max-height="0cm">
+    <style:footnote-sep style:width="0.018cm" 
style:distance-before-sep="0.101cm" style:distance-after-sep="0.101cm" 
style:line-style="solid" style:adjustment="left" style:rel-width="25%" 
style:color="#000000"/>
+   </style:page-layout-properties>
+   <style:header-style/>
+   <style:footer-style>
+    <style:header-footer-properties svg:height="3.401cm" fo:margin-left="0cm" 
fo:margin-right="0cm" fo:margin-top="0.101cm" fo:background-color="transparent" 
style:dynamic-spacing="false" draw:fill="none"/>
+   </style:footer-style>
+  </style:page-layout>
+ </office:automatic-styles>
+ <office:master-styles>
+  <style:master-page style:name="Standard" style:page-layout-name="pm1">
+   <style:footer>
+    <text:p text:style-name="Footer"><draw:frame draw:style-name="fr1" 
draw:name="Image1" text:anchor-type="paragraph" svg:x="0cm" svg:y="0cm" 
svg:width="17.3cm" svg:height="3.3cm" draw:z-index="0"><draw:image 
loext:mime-type="image/png">
+       
<office:binary-data>iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAABGdBTUEAANbY1E9YMgAAABl0
+        
RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAFpSURBVHjaYvz//z8DtQBAADER
+        
o+jjZGuibAQIICZiDOK/cgzFwEnrV/4HYXS1AAHERIxBR58yMiAb2DtzM1b1AAHERIxBIIBu
+        
IDYAEEBMxBjE0bgdxcBL3vcZLl16jaEPIICYiDFIU9MSw8BeoeUYhgEEEBMxBnFx8WE1EN3L
+        
AAHERIxBIECMgQABxAhKtPgM+vbtE9xmGP/69eMMP+o9wWLW0kD9OlYM/LlHGQECiAndoKg/
+        
USgGgTTmdS8C0yA+zIUgdeguBAggljtWdQwMVkDXACWMjd0ZXRun/Id5DWTA9C23GSaVxoEN
+        
zISoARvoamnBYF2/hPHs2Z3/z0JdDhBADCBvIuPkhsn/QeDr14//QWwQjY0PVYeiFyCA8OaA
+        
3cdPoEQAiI8PAAQQEwMVAUAAsWATBAX0jx9fsWrAJQ4CAAGE1TBQwOMC9+9fwikHEEBYDQPF
+        IAzIe8TglEMHAAHESM2SFiDAADEwCe4BJwcYAAAAAElFTkSuQmCC
+       </office:binary-data>
+      </draw:image>
+     </draw:frame></text:p>
+   </style:footer>
+  </style:master-page>
+ </office:master-styles>
+ <office:body>
+  <office:text text:use-soft-page-breaks="true">
+   <office:forms form:automatic-focus="false" form:apply-design-mode="false"/>
+   <text:sequence-decls>
+    <text:sequence-decl text:display-outline-level="0" 
text:name="Illustration"/>
+    <text:sequence-decl text:display-outline-level="0" text:name="Table"/>
+    <text:sequence-decl text:display-outline-level="0" text:name="Text"/>
+    <text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>
+    <text:sequence-decl text:display-outline-level="0" text:name="Figure"/>
+   </text:sequence-decls>
+   <text:p text:style-name="P2"><text:line-break/><text:line-break/></text:p>
+   <text:p text:style-name="P1">a<text:line-break/>b c d e f g h i j 
<text:s/><text:subject>the subject is testing a text formatting 
bug</text:subject><text:s/><text:subject>the subject is testing a text 
formatting bug</text:subject><text:s/><text:soft-page-break/>and some plain 
text in follow on page 2 to test it properly</text:p>
+  </office:text>
+ </office:body>
+</office:document>
\ No newline at end of file
diff --git a/sw/qa/extras/layout/layout2.cxx b/sw/qa/extras/layout/layout2.cxx
index 406df8998b1e..9c09f18bb7ae 100644
--- a/sw/qa/extras/layout/layout2.cxx
+++ b/sw/qa/extras/layout/layout2.cxx
@@ -155,6 +155,54 @@ void SwLayoutWriter2::CheckRedlineCharAttributesHidden()
     assertXPath(pXmlDoc, "/root/page[1]/body/txt[11]/Text[1]", "Portion", 
"foobaz");
 }
 
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testTdf136220)
+{
+    SwDoc* pDoc = createDoc("footer_field_rest_portion.fodt");
+
+    {
+        xmlDocPtr pXmlDoc = parseLayoutDump();
+
+        assertXPath(pXmlDoc, "/root/page", 2);
+        assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/LineBreak", 2);
+        assertXPath(pXmlDoc,
+                    
"/root/page[1]/body/txt[2]/LineBreak[1]/following-sibling::Special[1]",
+                    "rText", u"the subject is testing a text formatting bug");
+        assertXPath(pXmlDoc,
+                    
"/root/page[1]/body/txt[2]/LineBreak[1]/following-sibling::Special[2]",
+                    "rText", u"the subject is testing a text formatting bug");
+        assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/LineBreak", 1);
+        assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/Text[1]", "Portion",
+                    u"and some plain text in follow on page 2 to test it 
properly");
+        discardDumpedLayout();
+    }
+
+    SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell();
+
+    pWrtShell->Down(false, 4);
+    pWrtShell->Insert(u"X");
+
+    {
+        xmlDocPtr pXmlDoc = parseLayoutDump();
+
+        assertXPath(pXmlDoc, "/root/page", 2);
+        assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/LineBreak", 2);
+        assertXPath(pXmlDoc,
+                    
"/root/page[1]/body/txt[2]/LineBreak[1]/following-sibling::Special[1]",
+                    "rText", u"the subject is testing a text formatting bug");
+        assertXPath(pXmlDoc,
+                    
"/root/page[1]/body/txt[2]/LineBreak[1]/following-sibling::Special[2]",
+                    "rText", u"the subject is testing a text formatting ");
+        assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/LineBreak", 1);
+        // the problem was that the field follow / rest portion was missing
+        assertXPath(pXmlDoc,
+                    "/root/page[2]/body/txt[1]/Special[1]",
+                    "rText", u"bug");
+        assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/Text[1]", "Portion",
+                    u" and some plain text in follow on page 2 to test it 
properly");
+        discardDumpedLayout();
+    }
+}
+
 CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testRedlineCharAttributes)
 {
     createDoc("redline_charatr.fodt");
diff --git a/sw/source/core/text/itrtxt.cxx b/sw/source/core/text/itrtxt.cxx
index 5a9976bead8d..eca5de9081cb 100644
--- a/sw/source/core/text/itrtxt.cxx
+++ b/sw/source/core/text/itrtxt.cxx
@@ -309,16 +309,17 @@ void SwTextIter::TwipsToLine( const SwTwips y)
 // Local helper function to check, if pCurr needs a field rest portion:
 static bool lcl_NeedsFieldRest( const SwLineLayout* pCurr )
 {
-    const SwLinePortion *pPor = pCurr->GetNextPortion();
-    bool bRet = false;
-    while( pPor && !bRet )
+    // first, find the *last* field portion on the line
+    SwFieldPortion const* pLastField{nullptr};
+    for (SwLinePortion const* pPor{pCurr->GetNextPortion()};
+            pPor != nullptr; pPor = pPor->GetNextPortion())
     {
-        bRet = pPor->InFieldGrp() && static_cast<const 
SwFieldPortion*>(pPor)->HasFollow();
-        if( !pPor->GetNextPortion() || !pPor->GetNextPortion()->InFieldGrp() )
-            break;
-        pPor = pPor->GetNextPortion();
+        if (pPor->InFieldGrp())
+        {
+            pLastField = static_cast<const SwFieldPortion*>(pPor);
+        }
     }
-    return bRet;
+    return pLastField != nullptr ? pLastField->HasFollow() : false;
 }
 
 void SwTextIter::TruncLines( bool bNoteFollow )

Reply via email to