sw/qa/extras/ooxmlexport/data/tdf170908_delText.docx |binary sw/qa/extras/ooxmlexport/ooxmlexport25.cxx | 14 ++++++ sw/source/filter/ww8/docxattributeoutput.cxx | 40 ++++++++++--------- sw/source/filter/ww8/docxattributeoutput.hxx | 2 4 files changed, 37 insertions(+), 19 deletions(-)
New commits: commit 8a34dc1659f78beab7d2e8cadc0cc4e11ca6113d Author: Justin Luth <[email protected]> AuthorDate: Sat Feb 21 17:43:59 2026 -0500 Commit: Miklos Vajna <[email protected]> CommitDate: Wed Feb 25 09:25:03 2026 +0100 tdf#170908 docx export: stack run's m_pRedlineData The cached m_pRedlineData was being overwritten by the runs in OutFlys. It should be safe to use a stack for export, since internally any run needs to both start and stop. There are some sdraw things that don't use start/endRun though. So what to do about them - is pushing a nullptr needed to prevent them from inheritting redline info? I believe so - otherwise it might use element XML_delText. make CppunitTest_sw_ooxmlexport25 \ CPPUNIT_TEST_NAME=testTdf170908_delText Change-Id: Ic5f1d68da984a751ccf880718147df0dbc11c654 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199954 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Justin Luth <[email protected]> Reviewed-by: Miklos Vajna <[email protected]> diff --git a/sw/qa/extras/ooxmlexport/data/tdf170908_delText.docx b/sw/qa/extras/ooxmlexport/data/tdf170908_delText.docx new file mode 100644 index 000000000000..bfdd26bc6e0b Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf170908_delText.docx differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx index 6df37dc9db60..fcbddda5444c 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx @@ -342,6 +342,20 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf167082) CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), aStyleName); } +CPPUNIT_TEST_FIXTURE(Test, testTdf170908_delText) +{ + // Given a file with deleted text at the end of the paragraph just before anchored stuff + createSwDoc("tdf170908_delText.docx"); + + // When exporting that to DOCX: + save(TestFilter::DOCX); + + xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr); + // delText must be inside a w:del or MS Word considers the document to be corrupt + assertXPath(pXmlDoc, "//w:delText", 1); // there is a delTree element + CPPUNIT_ASSERT_EQUAL(1, countXPathNodes(pXmlDoc, "//w:del/w:r/w:delText")); // must be in w:del +} + CPPUNIT_TEST_FIXTURE(Test, testTdf170952_delText) { // Given a document with deleted text at the end of the paragraph diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index f89f73ca5385..c51b18ecf662 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -1828,7 +1828,7 @@ void DocxAttributeOutput::StartRun( const SwRedlineData* pRedlineData, sal_Int32 { // Don't start redline data here, possibly there is a hyperlink later, and // that has to be started first. - m_pRedlineData = pRedlineData; + m_pRedlineData.push_back(pRedlineData); // this mark is used to be able to enclose the run inside a sdr tag. m_pSerializer->mark(Tag_StartRun_1); @@ -1936,9 +1936,9 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, sal_In // InputField with extra grabbag params - it is sdt field (pIt->eType == ww::eFILLIN && static_cast<const SwInputField*>(pIt->pField.get())->getGrabBagParams().hasElements()))) { - StartRedline(m_pRedlineData); + StartRedline(m_pRedlineData.back()); StartField_Impl( pNode, nPos, *pIt, true ); - EndRedline(m_pRedlineData); + EndRedline(m_pRedlineData.back()); if (m_nHyperLinkCount.back() > 0) ++m_nFieldsInHyperlink; @@ -2011,16 +2011,17 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, sal_In DoWritePermissionsStart(); // Surround annotation references with redline start/end markup if we're inside a delete. - bool bHasAnnotationMarkReferencesInDel = !m_rAnnotationMarksEnd.empty() && m_pRedlineData - && m_pRedlineData->GetType() == RedlineType::Delete; + const bool bHasAnnotationMarkReferencesInDel + = !m_rAnnotationMarksEnd.empty() && m_pRedlineData.back() + && m_pRedlineData.back()->GetType() == RedlineType::Delete; if (bHasAnnotationMarkReferencesInDel) { - StartRedline(m_pRedlineData); + StartRedline(m_pRedlineData.back()); } DoWriteAnnotationMarks(); if (bHasAnnotationMarkReferencesInDel) { - EndRedline(m_pRedlineData); + EndRedline(m_pRedlineData.back()); } // if there is some redlining in the document, output it @@ -2038,7 +2039,7 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, sal_In if (!bSkipRedline) { - StartRedline(m_pRedlineData); + StartRedline(m_pRedlineData.back()); } if (m_closeHyperlinkInThisRun && m_nHyperLinkCount.back() > 0 && !m_hyperLinkAnchor.isEmpty() @@ -2112,7 +2113,7 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, sal_In // (except in the case of fields with multiple runs) if (!bSkipRedline) { - EndRedline(m_pRedlineData); + EndRedline(m_pRedlineData.back()); } DoWriteBookmarksEnd(m_rBookmarksEnd, false, true); // Write moverange bookmarks @@ -2154,7 +2155,7 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, sal_In if ( !m_bWritingField ) { - m_pRedlineData = nullptr; + m_pRedlineData.back() = nullptr; } if ( m_closeHyperlinkInThisRun ) @@ -2227,11 +2228,12 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, sal_In } } - if ( m_pRedlineData ) + if (m_pRedlineData.back()) { - EndRedline(m_pRedlineData); - m_pRedlineData = nullptr; + EndRedline(m_pRedlineData.back()); } + assert(m_pRedlineData.size()); + m_pRedlineData.pop_back(); DoWriteBookmarksStart(m_rFinalBookmarksStart); DoWriteBookmarksEnd(m_rFinalBookmarksEnd); // Write all final bookmarks @@ -3055,7 +3057,7 @@ void DocxAttributeOutput::DoWriteCmd( std::u16string_view rCmd ) } // Write the Field command sal_Int32 nTextToken = XML_instrText; - if ( m_pRedlineData && m_pRedlineData->GetType() == RedlineType::Delete ) + if (m_pRedlineData.size() && m_pRedlineData.back() && m_pRedlineData.back()->GetType() == RedlineType::Delete) nTextToken = XML_delInstrText; m_pSerializer->startElementNS(XML_w, nTextToken, FSNS(XML_xml, XML_space), "preserve"); @@ -4070,11 +4072,12 @@ void DocxAttributeOutput::RunText( const OUString& rText, rtl_TextEncoding /*eCh break; } } - bool bMoved = isInMoveBookmark && m_pRedlineData && m_pRedlineData->IsMoved() && + const bool bHasRedlineData = m_pRedlineData.size() && m_pRedlineData.back(); + const bool bMoved = isInMoveBookmark && bHasRedlineData && m_pRedlineData.back()->IsMoved() && // tdf#150166 save tracked moving around TOC as w:ins, w:del SwDoc::GetCurTOX(*m_rExport.m_pCurPam->GetPoint()) == nullptr; - - if (GetRedlineTypeForTextToken(m_pRedlineData) == RedlineType::Delete && !bMoved) + if (!bMoved && bHasRedlineData + && GetRedlineTypeForTextToken(m_pRedlineData.back()) == RedlineType::Delete) { nTextToken = XML_delText; } @@ -6815,6 +6818,7 @@ void DocxAttributeOutput::WriteOutliner(const OutlinerParaObject& rParaObj) { const EditTextObject& rEditObj = rParaObj.GetTextObject(); MSWord_SdrAttrIter aAttrIter( m_rExport, rEditObj, TXT_HFTXTBOX ); + m_pRedlineData.push_back(nullptr); sal_Int32 nPara = rEditObj.GetParagraphCount(); @@ -6872,6 +6876,7 @@ void DocxAttributeOutput::WriteOutliner(const OutlinerParaObject& rParaObj) while( nCurrentPos < nEnd ); EndParagraph(ww8::WW8TableNodeInfoInner::Pointer_t()); } + m_pRedlineData.pop_back(); m_pSerializer->endElementNS( XML_w, XML_txbxContent ); } @@ -10781,7 +10786,6 @@ DocxAttributeOutput::DocxAttributeOutput( DocxExport &rExport, const FSHelperPtr m_pFootnotesList( new ::docx::FootnotesList() ), m_pEndnotesList( new ::docx::FootnotesList() ), m_footnoteEndnoteRefTag( 0 ), - m_pRedlineData( nullptr ), m_nRedlineId( 0 ), m_bOpenedSectPr( false ), m_bHadSectPr(false), diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx index a65bded20481..ca9db3e30ece 100644 --- a/sw/source/filter/ww8/docxattributeoutput.hxx +++ b/sw/source/filter/ww8/docxattributeoutput.hxx @@ -869,7 +869,7 @@ private: std::unique_ptr< const WW8_SepInfo > m_pSectionInfo; /// Redline data to remember in the text run. - const SwRedlineData *m_pRedlineData; + std::vector<const SwRedlineData*> m_pRedlineData; /// Id of the redline sal_Int32 m_nRedlineId;
