sw/qa/extras/layout/data/footer_field_rest_portion.fodt | 156 ++++++++++++++++ sw/qa/extras/layout/layout2.cxx | 46 ++++ sw/source/core/text/itrtxt.cxx | 17 - 3 files changed, 211 insertions(+), 8 deletions(-)
New commits: commit 078ec0a15638c6a56b8f856ba2d3e80a4fdfcf40 Author: Michael Stahl <[email protected]> AuthorDate: Wed Nov 5 16:23:58 2025 +0100 Commit: Caolán McNamara <[email protected]> CommitDate: Sat Nov 29 15:18:59 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/+/193528 Reviewed-by: Caolán McNamara <[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 0608c291e91c..a5ba9d7f9399 100644 --- a/sw/qa/extras/layout/layout2.cxx +++ b/sw/qa/extras/layout/layout2.cxx @@ -215,6 +215,52 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testTdf148897) assertXPath(pXmlDoc, "/root/page", 4); } +CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testTdf136220) +{ + createSwDoc("footer_field_rest_portion.fodt"); + + { + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + + assertXPath(pXmlDoc, "/root/page", 2); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/SwParaPortion/SwLineLayout", 2); + assertXPath(pXmlDoc, + "/root/page[1]/body/txt[2]/SwParaPortion/SwLineLayout[2]/SwFieldPortion[1]", + "expand", u"the subject is testing a text formatting bug"); + assertXPath(pXmlDoc, + "/root/page[1]/body/txt[2]/SwParaPortion/SwLineLayout[2]/SwFieldPortion[2]", + "expand", u"the subject is testing a text formatting bug"); + assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/SwParaPortion/SwLineLayout", 1); + assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/SwParaPortion/SwLineLayout/*[1]", "portion", + u"and some plain text in follow on page 2 to test it properly"); + } + + SwWrtShell* pWrtShell = getSwDocShell()->GetWrtShell(); + + pWrtShell->Down(false, 4); + pWrtShell->Insert(u"X"_ustr); + + { + xmlDocUniquePtr pXmlDoc = parseLayoutDump(); + + assertXPath(pXmlDoc, "/root/page", 2); + assertXPath(pXmlDoc, "/root/page[1]/body/txt[2]/SwParaPortion/SwLineLayout", 2); + assertXPath(pXmlDoc, + "/root/page[1]/body/txt[2]/SwParaPortion/SwLineLayout[2]/SwFieldPortion[1]", + "expand", u"the subject is testing a text formatting bug"); + assertXPath(pXmlDoc, + "/root/page[1]/body/txt[2]/SwParaPortion/SwLineLayout[2]/SwFieldPortion[2]", + "expand", u"the subject is testing a text formatting "); + assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/SwParaPortion/SwLineLayout", 1); + // the problem was that the field follow / rest portion was missing + assertXPath(pXmlDoc, + "/root/page[2]/body/txt[1]/SwParaPortion/SwLineLayout[1]/SwFieldPortion[1]", + "expand", u"bug"); + assertXPath(pXmlDoc, "/root/page[2]/body/txt[1]/SwParaPortion/SwLineLayout[1]/*[2]", + "portion", u" and some plain text in follow on page 2 to test it properly"); + } +} + CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testRedlineCharAttributes) { createSwDoc("redline_charatr.fodt"); diff --git a/sw/source/core/text/itrtxt.cxx b/sw/source/core/text/itrtxt.cxx index 1d1eed3e0837..8c4bac91d563 100644 --- a/sw/source/core/text/itrtxt.cxx +++ b/sw/source/core/text/itrtxt.cxx @@ -350,16 +350,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 )
