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

New commits:
commit fe07499d7ca032972715c6d518f2558bfd99b9ff
Author:     Justin Luth <[email protected]>
AuthorDate: Tue Feb 10 12:13:59 2026 -0500
Commit:     Miklos Vajna <[email protected]>
CommitDate: Thu Feb 12 13:33:38 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]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199202
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Miklos Vajna <[email protected]>

diff --git a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
index c6af7434cf10..64df72739eae 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlfieldexport.cxx
@@ -1033,7 +1033,13 @@ CPPUNIT_TEST_FIXTURE(Test, testContentControlShape)
     // When exporting that document back to DOCX:
     // 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(u"Office Open XML Text"_ustr);
+    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 217a22e81536..9bfa4ad7e5b6 100644
--- a/sw/source/filter/ww8/wrtw8nds.cxx
+++ b/sw/source/filter/ww8/wrtw8nds.cxx
@@ -2513,10 +2513,10 @@ void MSWordExportBase::OutputTextNode( SwTextNode& 
rNode )
             OUString aSavedSnippet ;
 
             // Don't redline content-controls--Word doesn't do them.
-            SwTextAttr* pAttr = rNode.GetTextAttrAt(nCurrentPos, 
RES_TXTATR_CONTENTCONTROL,
-                                                    
sw::GetTextAttrMode::Default);
+            const SwTextAttr* pSdt = rNode.GetTextAttrAt(nCurrentPos, 
RES_TXTATR_CONTENTCONTROL,
+                                                         
sw::GetTextAttrMode::Default);
             bool bIsStartOfContentControl = false;
-            if (pAttr && pAttr->GetStart() == nCurrentPos)
+            if (pSdt && pSdt->GetStart() == nCurrentPos)
             {
                 pRedlineData = nullptr;
                 bIsStartOfContentControl = true;
@@ -2845,6 +2845,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);
+                        }
                         nStateOfFlyFrame = aAttrIter.OutFlys( nEnd );
                         // insert final bookmarks if any before CR and after 
flys
                         AppendBookmarks( rNode, nEnd, 1 );

Reply via email to