sw/qa/extras/ooxmlexport/data/comment-annotationref.docx |binary
 sw/qa/extras/ooxmlexport/ooxmlexport22.cxx               |   13 +++++++++++++
 sw/source/filter/ww8/docxattributeoutput.cxx             |    6 +++++-
 sw/source/filter/ww8/docxexport.cxx                      |    9 ++++++++-
 sw/source/filter/ww8/docxexport.hxx                      |    3 ++-
 5 files changed, 28 insertions(+), 3 deletions(-)

New commits:
commit ab866086efa7c4184e57123486c82ccedb4e98b5
Author:     Jaume Pujantell <jaume.pujant...@collabora.com>
AuthorDate: Tue Apr 8 17:56:13 2025 +0200
Commit:     Jaume Pujantell <jaume.pujant...@collabora.com>
CommitDate: Fri Apr 11 09:02:50 2025 +0200

    sw: docx: add annotationRef on comments
    
    Right now we never add <w:annotationRef/> to any comment when saving to
    docx. According to the standard, not having it is the same as having it
    in the beginning. But when round-tripping a document with multiple
    comments without text from MS Word, the comments get mangled if the
    annotationRef tags aren't kept. So it's better to always write the
    implicit annotationRef at the beginning of comments.
    
    Change-Id: I6c395bbc691a29533e2be9a93663993e083db65b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/183976
    Reviewed-by: Jaume Pujantell <jaume.pujant...@collabora.com>
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>

diff --git a/sw/qa/extras/ooxmlexport/data/comment-annotationref.docx 
b/sw/qa/extras/ooxmlexport/data/comment-annotationref.docx
new file mode 100644
index 000000000000..2350a8c6bb1d
Binary files /dev/null and 
b/sw/qa/extras/ooxmlexport/data/comment-annotationref.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx
index 46991f634f85..463f7f7db7c5 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport22.cxx
@@ -55,6 +55,19 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf165933_noDelTextOnMove)
     assertXPath(pXmlDoc, "//w:moveFrom/w:r/w:delText"_ostr, 0);
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testAnnotationRef)
+{
+    loadAndSave("comment-annotationref.docx");
+
+    // Check that the exported comment also has annotationRef
+    xmlDocUniquePtr pXmlComments = parseExport(u"word/comments.xml"_ustr);
+    CPPUNIT_ASSERT(pXmlComments);
+    // Wihtout the fix it fails with
+    // - Expected: 1
+    // - Actual  : 0
+    assertXPath(pXmlComments, 
"//w:comments/w:comment[1]/w:p[1]/w:r[1]/w:annotationRef"_ostr);
+}
+
 } // end of anonymous namespace
 CPPUNIT_PLUGIN_IMPLEMENT();
 
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx 
b/sw/source/filter/ww8/docxattributeoutput.cxx
index 18162ccf88e6..0f88ea5a6235 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -8488,7 +8488,8 @@ DocxAttributeOutput::hasProperties 
DocxAttributeOutput::WritePostitFields()
         if (f->GetTextObject() != nullptr)
         {
             // richtext
-            data.lastParaId = GetExport().WriteOutliner(*f->GetTextObject(), 
TXT_ATN, bNeedParaId);
+            data.lastParaId
+                = GetExport().WriteOutliner(*f->GetTextObject(), TXT_ATN, 
bNeedParaId, true);
         }
         else
         {
@@ -8502,6 +8503,9 @@ DocxAttributeOutput::hasProperties 
DocxAttributeOutput::WritePostitFields()
             }
             m_pSerializer->startElementNS(XML_w, XML_p, FSNS(XML_w14, 
XML_paraId), aParaId);
             m_pSerializer->startElementNS(XML_w, XML_r);
+            m_pSerializer->singleElementNS(XML_w, XML_annotationRef);
+            m_pSerializer->endElementNS(XML_w, XML_r);
+            m_pSerializer->startElementNS(XML_w, XML_r);
             RunText(f->GetText());
             m_pSerializer->endElementNS(XML_w, XML_r);
             m_pSerializer->endElementNS(XML_w, XML_p);
diff --git a/sw/source/filter/ww8/docxexport.cxx 
b/sw/source/filter/ww8/docxexport.cxx
index 7abd0d5ebd7c..104f0c92ac89 100644
--- a/sw/source/filter/ww8/docxexport.cxx
+++ b/sw/source/filter/ww8/docxexport.cxx
@@ -2000,7 +2000,7 @@ bool DocxExport::ignoreAttributeForStyleDefaults( 
sal_uInt16 nWhich ) const
 }
 
 sal_Int32 DocxExport::WriteOutliner(const OutlinerParaObject& rParaObj, 
sal_uInt8 nTyp,
-                                    bool bNeedsLastParaId)
+                                    bool bNeedsLastParaId, bool bWriteAnnotRef)
 {
     const EditTextObject& rEditObj = rParaObj.GetTextObject();
     MSWord_SdrAttrIter aAttrIter( *this, rEditObj, nTyp );
@@ -2025,6 +2025,13 @@ sal_Int32 DocxExport::WriteOutliner(const 
OutlinerParaObject& rParaObj, sal_uInt
         SfxItemSet aParagraphMarkerProperties(m_rDoc.GetAttrPool());
         AttrOutput().EndParagraphProperties(aParagraphMarkerProperties, 
nullptr, nullptr, nullptr);
 
+        if (bWriteAnnotRef && n == 0)
+        {
+            m_pAttrOutput->GetSerializer()->startElementNS(XML_w, XML_r);
+            m_pAttrOutput->GetSerializer()->singleElementNS(XML_w, 
XML_annotationRef);
+            m_pAttrOutput->GetSerializer()->endElementNS(XML_w, XML_r);
+        }
+
         do {
             AttrOutput().StartRun( nullptr, 0 );
             const sal_Int32 nNextAttr = std::min(aAttrIter.WhereNext(), nEnd);
diff --git a/sw/source/filter/ww8/docxexport.hxx 
b/sw/source/filter/ww8/docxexport.hxx
index 7f4ab826c40d..b285e998af4d 100644
--- a/sw/source/filter/ww8/docxexport.hxx
+++ b/sw/source/filter/ww8/docxexport.hxx
@@ -205,7 +205,8 @@ public:
     /// Writes the shape using drawingML syntax.
     void OutputDML( css::uno::Reference< css::drawing::XShape > const & xShape 
);
 
-    sal_Int32 WriteOutliner(const OutlinerParaObject& rOutliner, sal_uInt8 
nTyp, bool bNeedsLastParaId);
+    sal_Int32 WriteOutliner(const OutlinerParaObject& rOutliner, sal_uInt8 
nTyp,
+                            bool bNeedsLastParaId, bool bWriteAnnotRef = 
false);
 
     virtual ExportFormat GetExportFormat() const override { return 
ExportFormat::DOCX; }
 

Reply via email to