sw/qa/extras/ooxmlexport/data/tdf147892.fodt |   25 ++++++++++++++++++++++
 sw/qa/extras/ooxmlexport/ooxmlexport12.cxx   |   10 +++++++++
 sw/source/filter/ww8/docxattributeoutput.cxx |   30 ++++++++++++++-------------
 sw/source/filter/ww8/docxattributeoutput.hxx |    4 +--
 4 files changed, 53 insertions(+), 16 deletions(-)

New commits:
commit 4c05f5b45ce8449adee81aeac355fae652b97786
Author:     László Németh <nem...@numbertext.org>
AuthorDate: Fri Feb 10 13:43:40 2023 +0100
Commit:     László Németh <nem...@numbertext.org>
CommitDate: Thu Mar 30 13:37:18 2023 +0000

    tdf#147892 DOCX: fix corrupt export at para marker revision history
    
    OOXML export of tracked deletion of tracked paragraph insertion
    resulted invalid document.xml, because change tracking history of
    paragraph markers isn't supported by OOXML. Export the tracked deletion
    of the last run of paragraphs without history (tracked insertion).
    This way w:p/w:pPr/w:rPr contains only w:del or w:ins, not both of them
    (with broken tag order).
    
    Note: it's possible to optimize the fix to keep the change
    tracking history of the characters of the last run of the paragraph,
    except the paragraph marker.
    
    Regression from commit eeee19b3fcf8b0374c361c7f6c285fd5c89b5a2f
    "tdf#142387 DOCX track changes: export w:del in w:ins".
    
    Minimal unit test document was created by Miklós Vajna.
    
    Change-Id: I425f4d63c8c6ac29ccd807c1574748c7b9b39e80
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/146782
    Tested-by: László Németh <nem...@numbertext.org>
    Reviewed-by: László Németh <nem...@numbertext.org>
    (cherry-picked from commit 382892341a63e400f221e1a533fd5a5d6b4d9d70)
    
    Conflicts:
            sw/source/filter/ww8/docxattributeoutput.cxx
    
    Change-Id: I0e29195512d8786ac5f6a145ed88a9261f1c456e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/149763
    Reviewed-by: Stéphane Guillou <stephane.guil...@libreoffice.org>
    Tested-by: Jenkins
    Reviewed-by: László Németh <nem...@numbertext.org>
    Tested-by: László Németh <nem...@numbertext.org>

diff --git a/sw/qa/extras/ooxmlexport/data/tdf147892.fodt 
b/sw/qa/extras/ooxmlexport/data/tdf147892.fodt
new file mode 100644
index 000000000000..bb3ffc324dc1
--- /dev/null
+++ b/sw/qa/extras/ooxmlexport/data/tdf147892.fodt
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<office:document 
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" 
xmlns:dc="http://purl.org/dc/elements/1.1/"; 
xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" 
office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
+  <office:body>
+    <office:text>
+      <text:tracked-changes text:track-changes="false">
+        <text:changed-region xml:id="ct12345678" text:id="ct12345678">
+          <text:deletion>
+            <office:change-info>
+              <dc:creator>Bob</dc:creator>
+              <dc:date>2023-01-02T00:00:00</dc:date>
+            </office:change-info>
+          </text:deletion>
+          <text:insertion>
+            <office:change-info>
+              <dc:creator>Alice</dc:creator>
+              <dc:date>2023-01-01T00:00:00</dc:date>
+            </office:change-info>
+          </text:insertion>
+        </text:changed-region>
+      </text:tracked-changes>
+      <text:p><text:change-start text:change-id="ct12345678"/></text:p>
+      <text:p><text:change-end text:change-id="ct12345678"/></text:p>
+    </office:text>
+  </office:body>
+</office:document>
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx
index f831fd3ad61e..25a9932101ee 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx
@@ -2023,6 +2023,16 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf142387)
     assertXPathContent(pXmlDoc, 
"/w:document/w:body/w:p/w:ins/w:del/w:r/w:delText", "inserts ");
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testTdf147892)
+{
+    loadAndSave("tdf147892.fodt");
+    xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml");
+    // w:del in w:ins is exported correctly
+    // (both w:del and w:ins were exported for para marker)
+    assertXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:pPr/w:rPr/w:del", 1);
+    assertXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:pPr/w:rPr/w:ins", 0);
+}
+
 DECLARE_OOXMLEXPORT_TEST(testTdf123054, "tdf123054.docx")
 {
     CPPUNIT_ASSERT_EQUAL(OUString("No Spacing"),
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx 
b/sw/source/filter/ww8/docxattributeoutput.cxx
index 70c21933ebdd..b3866fbeca4d 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -1466,13 +1466,13 @@ void DocxAttributeOutput::EndParagraphProperties(const 
SfxItemSet& rParagraphMar
 
     if ( pRedlineParagraphMarkerDeleted )
     {
-        StartRedline( pRedlineParagraphMarkerDeleted );
-        EndRedline( pRedlineParagraphMarkerDeleted );
+        StartRedline( pRedlineParagraphMarkerDeleted, /*bLastRun=*/true );
+        EndRedline( pRedlineParagraphMarkerDeleted, /*bLastRun=*/true );
     }
     if ( pRedlineParagraphMarkerInserted )
     {
-        StartRedline( pRedlineParagraphMarkerInserted );
-        EndRedline( pRedlineParagraphMarkerInserted );
+        StartRedline( pRedlineParagraphMarkerInserted, /*bLastRun=*/true );
+        EndRedline( pRedlineParagraphMarkerInserted, /*bLastRun=*/true );
     }
 
     // mergeTopMarks() after paragraph mark properties child elements.
@@ -1575,7 +1575,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, bool 
/*bLastRun*/)
+void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool 
bLastRun)
 {
     int nFieldsInPrevHyperlink = m_nFieldsInHyperlink;
     // Reset m_nFieldsInHyperlink if a new hyperlink is about to start
@@ -1665,9 +1665,9 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, 
sal_Int32 nPos, bool /
             // 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, bLastRun );
             StartField_Impl( pNode, nPos, *pIt, true );
-            EndRedline( m_pRedlineData );
+            EndRedline( m_pRedlineData, bLastRun );
 
             if (m_startedHyperlink)
                 ++m_nFieldsInHyperlink;
@@ -1738,7 +1738,7 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, 
sal_Int32 nPos, bool /
     WriteContentControlStart();
 
     // if there is some redlining in the document, output it
-    StartRedline( m_pRedlineData );
+    StartRedline( m_pRedlineData, bLastRun );
 
     // XML_r node should be surrounded with bookmark-begin and bookmark-end 
nodes if it has bookmarks.
     // The same is applied for permission ranges.
@@ -1802,7 +1802,7 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, 
sal_Int32 nPos, bool /
 
     // if there is some redlining in the document, output it
     // (except in the case of fields with multiple runs)
-    EndRedline( m_pRedlineData );
+    EndRedline( m_pRedlineData, bLastRun );
 
     // enclose in a sdt block, if necessary: if one is already started, then 
don't do it for now
     // (so on export sdt blocks are never nested ATM)
@@ -1912,7 +1912,7 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, 
sal_Int32 nPos, bool /
 
     if ( m_pRedlineData )
     {
-        EndRedline( m_pRedlineData );
+        EndRedline( m_pRedlineData, bLastRun );
         m_pRedlineData = nullptr;
     }
 
@@ -3778,13 +3778,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 )
+void DocxAttributeOutput::StartRedline( const SwRedlineData * pRedlineData, 
bool bLastRun )
 {
     if ( !pRedlineData )
         return;
 
     // write out stack of this redline recursively (first the oldest)
-    StartRedline( pRedlineData->Next() );
+    if ( !bLastRun )
+        StartRedline( pRedlineData->Next(), false );
 
     OString aId( OString::number( m_nRedlineId++ ) );
 
@@ -3829,7 +3830,7 @@ void DocxAttributeOutput::StartRedline( const 
SwRedlineData * pRedlineData )
     }
 }
 
-void DocxAttributeOutput::EndRedline( const SwRedlineData * pRedlineData )
+void DocxAttributeOutput::EndRedline( const SwRedlineData * pRedlineData, bool 
bLastRun )
 {
     if ( !pRedlineData || m_bWritingField )
         return;
@@ -3855,7 +3856,8 @@ void DocxAttributeOutput::EndRedline( const SwRedlineData 
* pRedlineData )
     }
 
     // write out stack of this redline recursively (first the newest)
-    EndRedline( pRedlineData->Next() );
+    if ( !bLastRun )
+        EndRedline( pRedlineData->Next(), false );
 }
 
 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 09c6f8079e9d..d2a5ccdc7895 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -228,12 +228,12 @@ public:
     ///
     /// Start of the tag that encloses the run, fills the info according to
     /// the value of pRedlineData.
-    void StartRedline( const SwRedlineData * pRedlineData );
+    void StartRedline( const SwRedlineData * pRedlineData, bool bLastRun );
 
     /// Output redlining.
     ///
     /// End of the tag that encloses the run.
-    void EndRedline( const SwRedlineData * pRedlineData );
+    void EndRedline( const SwRedlineData * pRedlineData, bool bLastRun );
 
     virtual void SetStateOfFlyFrame( FlyProcessingState nStateOfFlyFrame ) 
override;
     virtual void SetAnchorIsLinkedToNode( bool bAnchorLinkedToNode ) override;

Reply via email to