sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx |    6 ++++++
 sw/source/filter/ww8/wrtw8nds.cxx             |   16 +++++++++++++---
 2 files changed, 19 insertions(+), 3 deletions(-)

New commits:
commit e606e8f75a9297af67177d4b97f27a550ce2b6cf
Author:     Justin Luth <[email protected]>
AuthorDate: Tue Feb 10 12:13:59 2026 -0500
Commit:     Justin Luth <[email protected]>
CommitDate: Wed Feb 11 13:45:05 2026 +0100

    tdf#170686 docx export: end SDT before writing final flies
    
    A plainText-y content control is reported as corrupt
    by Microsoft Word if it contains a floating frame.
    
    When a runSdt was at the end of the paragraph,
    the export was dumping any flies at the end
    into the Sdt run.
    
    Instead, the Sdt should first be closed,
    and then the remaining flies
    should be placed into their own separate run.
    
    Possibly, this should happen for fields as well?
    Or even for any paragraph, like we did in bug tdf#170516?
    It probably also belongs in the other 'if nNextAttr == nEnd'
    but I couldn't find any example unit tests to confirm.
    
    make CppunitTest_sw_ooxmlfieldexport \
        CPPUNIT_TEST_NAME=testContentControlShape
    
    Change-Id: Ic1c160ed9c9985d4fcc308a271ee168fe00ea82b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199092
    Tested-by: Jenkins
    Reviewed-by: Justin Luth <[email protected]>

diff --git a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
index 4b65fe4b8fc8..f8d806e8a041 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
@@ -1096,6 +1096,12 @@ CPPUNIT_TEST_FIXTURE(Test, testContentControlShape)
     // Then make sure that completes without an assertion failure, which would 
mean not-well-formed
     // output was produced, since the <w:sdt> was conditional but the </w:sdt> 
was unconditional:
     save(TestFilter::DOCX);
+
+    // tdf#170686: floating shapes are not allowed inside plainText date 
controls
+    xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
+    assertXPath(pXmlDoc, "//w:sdt", 1); // only one sdt
+    assertXPath(pXmlDoc, "//mc:AlternateContent", 1); // only one drawing
+    assertXPath(pXmlDoc, "//w:body/w:p/w:r/mc:AlternateContent", 1); // and it 
is not inside the sdt
 }
 
 CPPUNIT_TEST_FIXTURE(Test, testTdf104823)
diff --git a/sw/source/filter/ww8/wrtw8nds.cxx 
b/sw/source/filter/ww8/wrtw8nds.cxx
index e1c036339a92..64118f6aa51a 100644
--- a/sw/source/filter/ww8/wrtw8nds.cxx
+++ b/sw/source/filter/ww8/wrtw8nds.cxx
@@ -2492,9 +2492,9 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode )
             const SwRedlineData* pRedlineData = aAttrIter.GetRunLevelRedline( 
nCurrentPos );
 
             // Don't redline content-controls--Word doesn't do them.
-            SwTextAttr* pAttr = rNode.GetTextAttrAt(nCurrentPos, 
RES_TXTATR_CONTENTCONTROL,
-                                                    
sw::GetTextAttrMode::Default);
-            if (pAttr && pAttr->GetStart() == nCurrentPos)
+            const SwTextAttr* pSdt = rNode.GetTextAttrAt(nCurrentPos, 
RES_TXTATR_CONTENTCONTROL,
+                                                         
sw::GetTextAttrMode::Default);
+            if (pSdt && pSdt->GetStart() == nCurrentPos)
             {
                 pRedlineData = nullptr;
             }
@@ -2772,6 +2772,16 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode 
)
                     else
                     {
                         // insert final graphic anchors if any before CR
+                        if (pSdt && *pSdt->GetEnd() == nEnd && 
aAttrIter.HasFlysAt(nEnd))
+                        {
+                            // Close the content control run before exporting 
final flies,
+                            // otherwise the flies will be moved into the Sdt 
run,
+                            // which (for a non-richText Sdt) will be 
considered corrupt in MS Word.
+                            AttrOutput().EndRun(&rNode, nCurrentPos, nLen, 
/*bLastRun=*/false);
+                            nLen = 0;
+                            nCurrentPos = nEnd;
+                            AttrOutput().StartRun(pRedlineData, nCurrentPos, 
bSingleEmptyRun);
+                        }
                         aAttrIter.OutFlys(nEnd);
                         // insert final bookmarks if any before CR and after 
flys
                         AppendBookmarks( rNode, nEnd, 1 );

Reply via email to