sw/qa/extras/ooxmlexport/data/tdf170952_delText.docx |binary sw/qa/extras/ooxmlexport/ooxmlexport25.cxx | 15 +++++++ sw/source/filter/ww8/docxattributeoutput.cxx | 38 +++++++++---------- sw/source/filter/ww8/docxattributeoutput.hxx | 5 +- 4 files changed, 35 insertions(+), 23 deletions(-)
New commits: commit afeb7f3dc431ddbd85b454730e5775c0d410d2ff Author: Justin Luth <[email protected]> AuthorDate: Sat Feb 21 17:08:34 2026 -0500 Commit: Justin Luth <[email protected]> CommitDate: Tue Feb 24 13:14:33 2026 +0100 tdf#170952 docx export redlines: treat LastRun same as all others This fixes a 25.8.4 exposure of an existing problem that dates back to 7.4.7. There shouldn't be any good reason why the last run would be treated any differently in change-tracking. This basically reverts commit 382892341a63e400f221e1a533fd5a5d6b4d9d70 Author: László Németh on Sat Feb 11 21:06:21 2023 +0000 tdf#147892 DOCX: fix corrupt export at para marker revision history Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146782 which was basically replaced earlier in 25.2.3 by commit 7b57004cf20ae1715eabd6623ad9007a68a0a32e Author: Jaume Pujantell on Fri Mar 7 17:31:26 2025 +0100 tdf#165059 sw fix not valid moveFrom/moveTo tag Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182503 The only time bParagraphProps is true is the time that Laszlo was trying for, so bIsLastRun can be removed. I assume that what Jaume did was to implement Laszlo's comment Note: it's possible to optimize the fix to keep the change tracking history of the characters of the last run of the para, except the paragraph marker. The problem is that GetRedlineTypeForTextToken had determined that a w:delText should be used, but now that is not being wrapped in a w:del since the full history is not being written out. make CppunitTest_sw_ooxmlexport25 \ CPPUNIT_TEST_NAME=testTdf170952_delText Change-Id: Ia6ee78a63c5f392d7c7a7eaaa65ff289944954d1 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199972 Tested-by: Jenkins CollaboraOffice <[email protected]> Reviewed-by: Justin Luth <[email protected]> diff --git a/sw/qa/extras/ooxmlexport/data/tdf170952_delText.docx b/sw/qa/extras/ooxmlexport/data/tdf170952_delText.docx new file mode 100644 index 000000000000..57ed7f7de7df Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/tdf170952_delText.docx differ diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx index 21f2c1a055b5..6df37dc9db60 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport25.cxx @@ -342,6 +342,21 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf167082) CPPUNIT_ASSERT_EQUAL(OUString("Heading 1"), aStyleName); } +CPPUNIT_TEST_FIXTURE(Test, testTdf170952_delText) +{ + // Given a document with deleted text at the end of the paragraph + + skipValidation(); // ERROR: The content of element 'w:rPrChange' is not complete. One of '{"http://schemas.openxmlformats.org/wordprocessingml/2006/main":rPr}' is expected. + createSwDoc("tdf170952_delText.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", 3); // there are three delTree elements + CPPUNIT_ASSERT_EQUAL(3, countXPathNodes(pXmlDoc, "//w:del/w:r/w:delText")); // all are in w:del +} + CPPUNIT_TEST_FIXTURE(Test, testRangeCommentInDeleteDocxExport) { // Given a document with a comment that is inside a delete redline: diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index b8f9524fc1ab..f89f73ca5385 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -1697,13 +1697,13 @@ void DocxAttributeOutput::EndParagraphProperties(const SfxItemSet& rParagraphMar if ( pRedlineParagraphMarkerDeleted ) { - StartRedline(pRedlineParagraphMarkerDeleted, /*bLastRun=*/true, /*bParagraphProps=*/true); - EndRedline(pRedlineParagraphMarkerDeleted, /*bLastRun=*/true, /*bParagraphProps=*/true); + StartRedline(pRedlineParagraphMarkerDeleted, /*bParagraphProps=*/true); + EndRedline(pRedlineParagraphMarkerDeleted, /*bParagraphProps=*/true); } if ( pRedlineParagraphMarkerInserted ) { - StartRedline(pRedlineParagraphMarkerInserted, /*bLastRun=*/true, /*bParagraphProps=*/true); - EndRedline(pRedlineParagraphMarkerInserted, /*bLastRun=*/true, /*bParagraphProps=*/true); + StartRedline(pRedlineParagraphMarkerInserted, /*bParagraphProps=*/true); + EndRedline(pRedlineParagraphMarkerInserted, /*bParagraphProps=*/true); } // mergeTopMarks() after paragraph mark properties child elements. @@ -1843,7 +1843,7 @@ void DocxAttributeOutput::StartRun( const SwRedlineData* pRedlineData, sal_Int32 m_pSerializer->mark(Tag_StartRun_3); // let's call it "postponed text" } -void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, sal_Int32 nLen, bool bLastRun) +void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, sal_Int32 nLen, bool /*bLastRun*/) { int nFieldsInPrevHyperlink = m_nFieldsInHyperlink; // Reset m_nFieldsInHyperlink if a new hyperlink is about to start @@ -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, bLastRun ); + StartRedline(m_pRedlineData); StartField_Impl( pNode, nPos, *pIt, true ); - EndRedline( m_pRedlineData, bLastRun ); + EndRedline(m_pRedlineData); if (m_nHyperLinkCount.back() > 0) ++m_nFieldsInHyperlink; @@ -2015,12 +2015,12 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, sal_In && m_pRedlineData->GetType() == RedlineType::Delete; if (bHasAnnotationMarkReferencesInDel) { - StartRedline(m_pRedlineData, bLastRun); + StartRedline(m_pRedlineData); } DoWriteAnnotationMarks(); if (bHasAnnotationMarkReferencesInDel) { - EndRedline(m_pRedlineData, bLastRun); + EndRedline(m_pRedlineData); } // if there is some redlining in the document, output it @@ -2038,7 +2038,7 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, sal_In if (!bSkipRedline) { - StartRedline(m_pRedlineData, bLastRun); + StartRedline(m_pRedlineData); } if (m_closeHyperlinkInThisRun && m_nHyperLinkCount.back() > 0 && !m_hyperLinkAnchor.isEmpty() @@ -2112,7 +2112,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, bLastRun); + EndRedline(m_pRedlineData); } DoWriteBookmarksEnd(m_rBookmarksEnd, false, true); // Write moverange bookmarks @@ -2229,7 +2229,7 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, sal_In if ( m_pRedlineData ) { - EndRedline( m_pRedlineData, bLastRun ); + EndRedline(m_pRedlineData); m_pRedlineData = nullptr; } @@ -4474,15 +4474,14 @@ void DocxAttributeOutput::Redline( const SwRedlineData* pRedlineData) // The difference between 'Redline' and 'StartRedline'+'EndRedline' is that: // 'Redline' is used for tracked changes of formatting information of a run like Bold, Underline. (the '<w:rPrChange>' is inside the 'run' node) // 'StartRedline' is used to output tracked changes of run insertion and deletion (the run is inside the '<w:ins>' node) -void DocxAttributeOutput::StartRedline(const SwRedlineData* pRedlineData, bool bLastRun, - bool bParagraphProps) +void DocxAttributeOutput::StartRedline(const SwRedlineData* pRedlineData, bool bParagraphProps) { if ( !pRedlineData ) return; // write out stack of this redline recursively (first the oldest) - if ( !bLastRun ) - StartRedline( pRedlineData->Next(), false ); + if (!bParagraphProps) + StartRedline( pRedlineData->Next()); OString aId( OString::number( m_nRedlineId++ ) ); @@ -4540,8 +4539,7 @@ void DocxAttributeOutput::StartRedline(const SwRedlineData* pRedlineData, bool b } } -void DocxAttributeOutput::EndRedline(const SwRedlineData* pRedlineData, bool bLastRun, - bool bParagraphProps) +void DocxAttributeOutput::EndRedline(const SwRedlineData* pRedlineData, bool bParagraphProps) { if ( !pRedlineData || m_bWritingField ) return; @@ -4576,8 +4574,8 @@ void DocxAttributeOutput::EndRedline(const SwRedlineData* pRedlineData, bool bLa } // write out stack of this redline recursively (first the newest) - if ( !bLastRun ) - EndRedline( pRedlineData->Next(), false ); + if (!bParagraphProps) + EndRedline(pRedlineData->Next()); } void DocxAttributeOutput::FormatDrop( const SwTextNode& /*rNode*/, const SwFormatDrop& /*rSwFormatDrop*/, sal_uInt16 /*nStyle*/, ww8::WW8TableNodeInfo::Pointer_t /*pTextNodeInfo*/, ww8::WW8TableNodeInfoInner::Pointer_t ) diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx index fa6380be64cf..a65bded20481 100644 --- a/sw/source/filter/ww8/docxattributeoutput.hxx +++ b/sw/source/filter/ww8/docxattributeoutput.hxx @@ -285,13 +285,12 @@ public: /// /// Start of the tag that encloses the run, fills the info according to /// the value of pRedlineData. - void StartRedline(const SwRedlineData* pRedlineData, bool bLastRun, - bool bParagraphProps = false); + void StartRedline(const SwRedlineData* pRedlineData, bool bParagraphProps = false); /// Output redlining. /// /// End of the tag that encloses the run. - void EndRedline(const SwRedlineData* pRedlineData, bool bLastRun, bool bParagraphProps = false); + void EndRedline(const SwRedlineData* pRedlineData, bool bParagraphProps = false); virtual bool IsFlyProcessingPostponed() override; virtual void ResetFlyProcessingFlag() override;
