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 815993dfdb63f4f746af2a286cfc27e114198f29 Author: Justin Luth <[email protected]> AuthorDate: Tue Jan 27 10:04:09 2026 -0500 Commit: Miklos Vajna <[email protected]> CommitDate: Fri Feb 6 08:32:57 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/+/198632 Reviewed-by: Justin Luth <[email protected]> Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198771 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 64c59c2cfd29..cdb4f5fdde89 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx @@ -143,6 +143,20 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf165359_SdtWithDrawing) assertXPath(pXmlDoc, "//w:sdtPr/w:text", 0); } +CPPUNIT_TEST_FIXTURE(Test, testTdf170516_drawingBeforePlainText) +{ + createSwDoc("tdf170516_drawingBeforePlainText.docx"); + + save(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 59b148679bd5..5d6b2227bb89 100644 --- a/sw/source/filter/ww8/wrtw8nds.cxx +++ b/sw/source/filter/ww8/wrtw8nds.cxx @@ -2515,9 +2515,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 ); @@ -2582,6 +2584,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];
