sw/qa/extras/layout/data/tdf170811.fodt |   30 ++++++++++++++++++++++++++++++
 sw/qa/extras/layout/layout6.cxx         |    8 ++++++++
 sw/source/core/text/porexp.cxx          |    9 +++++++++
 3 files changed, 47 insertions(+)

New commits:
commit e9db7ec995b2c89630b8cc1893ae24cd73af6ece
Author:     Mike Kaganski <[email protected]>
AuthorDate: Mon Feb 16 14:06:37 2026 +0500
Commit:     Mike Kaganski <[email protected]>
CommitDate: Mon Feb 16 17:49:25 2026 +0100

    tdf#170811: SwBlankPortion as part of field expansion must use SwTextSlot
    
    Since commit e3c068cb190e6cf04a49f9984f984d5ef72f9d9a (tdf#167946:
    reimplement the fix for tdf#120677, 2026-02-07), SwBlankPortion stopped
    using SwExpandPortion::Format, and therefore, SwTextSlot (that replaced
    the text in SwTextFormatInfo). That was the intended change, because
    replacing the text destroyed the context for formatting, and that was
    the root of the problem.
    
    However, SwBlankPortion may appear as part of a field expansion. Then
    its length is zero (see SwTextFormatter::NewPortion, which sets length
    to zero in that case). To avoid wrong formatting (where zero length
    automatically means in SwTextGuess::Guess, that line break goes here),
    which would change portion lingth to 1 (see SwTextPortion::BreakCut),
    which would then try to access missing string indexes ... - to avoid
    all that, restore use of SwTextSlot optionally for the "length is 0"
    case.
    
    A follow-up needs to improve the use of SwTextSlot here, to use the
    actual, full expansion of the field text - which would allow to format
    the NBSP in context of the surrounding text, and to fix tdf#56085.
    
    Change-Id: I86497ed3099eec585f4454e1db19e070ce1b17d6
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199458
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <[email protected]>

diff --git a/sw/qa/extras/layout/data/tdf170811.fodt 
b/sw/qa/extras/layout/data/tdf170811.fodt
new file mode 100644
index 000000000000..bf34144cf5f2
--- /dev/null
+++ b/sw/qa/extras/layout/data/tdf170811.fodt
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<office:document 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta: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:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" 
office:version="1.4" office:mimetype="application/vnd.oasis.opendocument.text">
+ <office:meta><meta:user-defined meta:name="NBSP">word nobreakspace word space 
word nobreakspace word</meta:user-defined></office:meta>
+ <office:font-face-decls>
+  <style:font-face style:name="Liberation Serif" 
svg:font-family="&apos;Liberation Serif&apos;" 
style:font-family-generic="roman" style:font-pitch="variable"/>
+ </office:font-face-decls>
+ <office:styles>
+  <style:default-style style:family="paragraph">
+   <style:text-properties style:font-name="Liberation Serif" 
fo:font-size="12pt" fo:language="en" fo:country="US"/>
+  </style:default-style>
+  <style:style style:name="Standard" style:family="paragraph" 
style:class="text"/>
+ </office:styles>
+ <office:automatic-styles>
+  <style:style style:name="P1" style:family="paragraph" 
style:parent-style-name="Standard">
+   <style:paragraph-properties fo:margin-left="0" fo:margin-right="10cm"/>
+  </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="2cm" fo:margin-bottom="2cm" 
fo:margin-left="2cm" fo:margin-right="2cm" style:writing-mode="lr-tb"/>
+  </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 text:style-name="P1"><text:user-defined text:name="NBSP"/></text:p>
+  </office:text>
+ </office:body>
+</office:document>
\ No newline at end of file
diff --git a/sw/qa/extras/layout/layout6.cxx b/sw/qa/extras/layout/layout6.cxx
index 30c935a59276..c1eac3679167 100644
--- a/sw/qa/extras/layout/layout6.cxx
+++ b/sw/qa/extras/layout/layout6.cxx
@@ -2162,6 +2162,14 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, testTdf169607)
     assertXPath(pXmlDoc, "//page", 3);
 }
 
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter6, testTdf170811)
+{
+    // This used to fail assertion
+    createSwDoc("tdf170811.fodt");
+    // This must succeed
+    parseLayoutDump();
+}
+
 } // end of anonymous namespace
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/core/text/porexp.cxx b/sw/source/core/text/porexp.cxx
index a9bb3add21b0..eb6dd2719fe6 100644
--- a/sw/source/core/text/porexp.cxx
+++ b/sw/source/core/text/porexp.cxx
@@ -195,6 +195,15 @@ void SwBlankPortion::FormatEOL( SwTextFormatInfo &rInf )
  */
 bool SwBlankPortion::Format( SwTextFormatInfo &rInf )
 {
+    std::optional<SwTextSlot> oTextSlot;
+    if (!GetLen())
+    {
+        // This is a hook char of a field portion. Use expansion.
+        // FIXME: tdf#56085 the slot must use parent field portion text, to 
correctly format
+        // in the context of surrounding text
+        oTextSlot.emplace(&rInf, this, true, false);
+    }
+
     const bool bFull = rInf.IsUnderflow() || SwTextPortion::Format(rInf);
     if( bFull && MayUnderflow( rInf, rInf.GetIdx(), rInf.IsUnderflow() ) )
     {

Reply via email to