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];
