sw/qa/extras/ooxmlexport/data/tdf170516_drawingBeforePlainText.docx |binary
 sw/qa/extras/ooxmlexport/ooxmlexport25.cxx                          |   14 
++++++++
 sw/source/filter/ww8/wrtw8nds.cxx                                   |   17 
++++++++++
 3 files changed, 31 insertions(+)

New commits:
commit 2298be1d34f4bfd21c2d4278b1bd64541a8199c7
Author:     Justin Luth <[email protected]>
AuthorDate: Tue Jan 27 10:04:09 2026 -0500
Commit:     Justin Luth <[email protected]>
CommitDate: Thu Feb 5 14:50:35 2026 +0100

    tdf#170516 docx export: write flies before w:sdt starts
    
    If a run Pos start a runSdt content control (CH_TXTATR_BREAKWORD)
    AND it also anchors a floating shape,
    then the shape was being placed inside the sdtContent,
    and if the Sdt was plainText,
    then MS Word MIGHT consider that document to be corrupt.
    
    This patch follows plan B (see patchset notes).
    Plan A didn't pass CI unit tests (but it did pass locally...)
    
    For whatever reason, tdf#78333's original fix for corrupt SDTs
    skipped this situation when it happened in a floating table
            && !rNode.GetFlyFormat()
            ...
            {
                bPostponeWritingText = true ;
            }
    but the corruption is reported in that case too...
    
    (Floating shapes cannot anchor other flies,
    so all their images are inline [they have their own ].
    so only in a floating table is this situation likely to arise.
    Well, at least natively. ODT->DOCX can also trigger this.)
    
    make CppunitTest_sw_ooxmlexport25 \
        CPPUNIT_TEST_NAME=testTdf170516_drawingBeforePlainText
    
    Change-Id: I951fcef33fe54d71736f3036a43087f340927727
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198210
    Tested-by: Jenkins
    Reviewed-by: Justin Luth <[email protected]>

diff --git 
a/sw/qa/extras/ooxmlexport/data/tdf170516_drawingBeforePlainText.docx 
b/sw/qa/extras/ooxmlexport/data/tdf170516_drawingBeforePlainText.docx
new file mode 100644
index 000000000000..fa054c0c9ac3
Binary files /dev/null and 
b/sw/qa/extras/ooxmlexport/data/tdf170516_drawingBeforePlainText.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
index f021484152bf..113b5bd626b6 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx
@@ -184,6 +184,20 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf170438_dropdown)
     assertXPath(pXmlDoc, "//w:listItem[1]", "value", u""); // value may be 
empty
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testTdf170516_drawingBeforePlainText)
+{
+    createSwDoc("tdf170516_drawingBeforePlainText.docx");
+
+    saveAndReload(TestFilter::DOCX);
+
+    xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
+    assertXPath(pXmlDoc, "//w:sdt", 1); // only one sdt
+    assertXPath(pXmlDoc, "//w:tc/w:p/w:sdt", 1); // and it is inside a cell
+
+    assertXPath(pXmlDoc, "//mc:AlternateContent", 1); // only one drawing
+    assertXPath(pXmlDoc, "//w:tc/w:p/w:r/mc:AlternateContent", 1); // and it 
is not inside the sdt
+}
+
 CPPUNIT_TEST_FIXTURE(Test, testTdf170389_manyTabstops)
 {
     createSwDoc("tdf170389_manyTabstops.odt");
diff --git a/sw/source/filter/ww8/wrtw8nds.cxx 
b/sw/source/filter/ww8/wrtw8nds.cxx
index cde29c380202..cda69bc6d464 100644
--- a/sw/source/filter/ww8/wrtw8nds.cxx
+++ b/sw/source/filter/ww8/wrtw8nds.cxx
@@ -2508,9 +2508,11 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode 
)
             // Don't redline content-controls--Word doesn't do them.
             SwTextAttr* pAttr = rNode.GetTextAttrAt(nCurrentPos, 
RES_TXTATR_CONTENTCONTROL,
                                                     
sw::GetTextAttrMode::Default);
+            bool bIsStartOfContentControl = false;
             if (pAttr && pAttr->GetStart() == nCurrentPos)
             {
                 pRedlineData = nullptr;
+                bIsStartOfContentControl = true;
             }
 
             sal_Int32 nNextAttr = GetNextPos( &aAttrIter, rNode, nCurrentPos );
@@ -2574,6 +2576,21 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode 
)
 
             OUString aSymbolFont;
             sal_Int32 nLen = nNextAttr - nCurrentPos;
+
+            if (!bPostponeWritingText && bIsStartOfContentControl
+                && nStateOfFlyFrame == FLY_PROCESSED
+                && GetExportFormat() == MSWordExportBase::ExportFormat::DOCX)
+            {
+                // FLY_PROCESSED: there is at least 1 fly already written
+
+                // assurance: this will not duplicate what is done for fields 
below...
+                assert(bTextAtr); // because SwTextContentControl always 
SetHasDummyChar(true)
+
+                // write flys in a separate run before Sdt content control
+                AttrOutput().EndRun(&rNode, nCurrentPos, /*nLen=*/-1, 
/*bLastRun=*/false);
+                AttrOutput().StartRun(pRedlineData, nCurrentPos, 
bSingleEmptyRun);
+            }
+
             if ( !bTextAtr && nLen )
             {
                 sal_Unicode ch = aStr[nCurrentPos];

Reply via email to