oox/source/token/tokens.txt                           |    1 
 sw/inc/docufld.hxx                                    |   10 +++
 sw/inc/unoprnms.hxx                                   |    2 
 sw/qa/extras/ooxmlexport/data/CommentReply.docx       |binary
 sw/qa/extras/ooxmlexport/ooxmlexport16.cxx            |   16 +++++
 sw/source/core/fields/docufld.cxx                     |   50 ++++++++++++++++-
 sw/source/core/inc/unofldmid.h                        |    1 
 sw/source/core/unocore/unofield.cxx                   |   27 +++++++++
 sw/source/core/unocore/unomap.cxx                     |    2 
 sw/source/filter/ww8/docxattributeoutput.cxx          |   51 +++++++++++++++---
 sw/source/filter/ww8/docxattributeoutput.hxx          |   12 +++-
 sw/source/filter/ww8/docxexport.cxx                   |    4 -
 writerfilter/inc/dmapper/CommentProperties.hxx        |    6 +-
 writerfilter/source/dmapper/DomainMapper_Impl.cxx     |    7 ++
 writerfilter/source/dmapper/DomainMapper_Impl.hxx     |    2 
 writerfilter/source/ooxml/OOXMLFastContextHandler.cxx |    7 ++
 writerfilter/source/ooxml/OOXMLFastContextHandler.hxx |    2 
 writerfilter/source/ooxml/model.xml                   |    3 -
 18 files changed, 183 insertions(+), 20 deletions(-)

New commits:
commit 0c2ed51a775871c91ac8f01c8982f23c34e8248d
Author:     Paris Oplopoios <paris.oplopo...@collabora.com>
AuthorDate: Fri Jan 13 19:49:23 2023 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Wed Jan 18 15:28:37 2023 +0000

    tdf#119229 docx: Preserve w15:paraIdParent attribute in commentsExtended
    
    w15:paraIdParent attribute indicates that the comment is a reply to the
    value id
    
    Change-Id: I9e6eca6a656594c956629c1434b8e5c3aa573c60
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145314
    Tested-by: Jenkins
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/oox/source/token/tokens.txt b/oox/source/token/tokens.txt
index 25951891d2a7..dee9010df789 100644
--- a/oox/source/token/tokens.txt
+++ b/oox/source/token/tokens.txt
@@ -3884,6 +3884,7 @@ parTransId
 parTxLTRAlign
 parTxRTLAlign
 paraId
+paraIdParent
 paragraph
 parallel
 parallelogram
diff --git a/sw/inc/docufld.hxx b/sw/inc/docufld.hxx
index b907763a2d8c..89a5341222db 100644
--- a/sw/inc/docufld.hxx
+++ b/sw/inc/docufld.hxx
@@ -456,6 +456,8 @@ class SW_DLLPUBLIC SwPostItField final : public SwField
     std::optional<OutlinerParaObject> mpText;
     rtl::Reference<SwTextAPIObject> m_xTextObject;
     sal_uInt32 m_nPostItId;
+    sal_uInt32 m_nParentId;
+    sal_uInt32 m_nParaId;
 
 public:
     static sal_uInt32 s_nLastPostItId;
@@ -467,7 +469,9 @@ public:
                    OUString aName,
                    const DateTime& rDate,
                    const bool bResolved = false,
-                   const sal_uInt32 nPostItId = 0);
+                   const sal_uInt32 nPostItId = 0,
+                   const sal_uInt32 nParentId = 0,
+                   const sal_uInt32 nParaId = 0);
 
     SwPostItField(const SwPostItField&) = delete;
     SwPostItField* operator=(const SwPostItField&) = delete;
@@ -482,6 +486,10 @@ public:
     tools::Time GetTime() const                 { return 
tools::Time(m_aDateTime.GetTime()); }
     sal_uInt32 GetPostItId() const             { return m_nPostItId; }
     void SetPostItId(const sal_uInt32 nPostItId = 0);
+    sal_uInt32 GetParentId() const             { return m_nParentId; }
+    void SetParentId(const sal_uInt32 nParentId);
+    sal_uInt32 GetParaId() const               { return m_nParaId; }
+    void SetParaId(const sal_uInt32 nParaId);
 
     /// Author
     virtual OUString        GetPar1() const override;
diff --git a/sw/inc/unoprnms.hxx b/sw/inc/unoprnms.hxx
index 85a1938d61ce..eb70f1d79d8d 100644
--- a/sw/inc/unoprnms.hxx
+++ b/sw/inc/unoprnms.hxx
@@ -923,6 +923,8 @@ inline constexpr OUStringLiteral UNO_NAME_ID = u"Id";
 inline constexpr OUStringLiteral UNO_NAME_TAB_INDEX = u"TabIndex";
 inline constexpr OUStringLiteral UNO_NAME_LOCK = u"Lock";
 inline constexpr OUStringLiteral UNO_NAME_DATE_STRING = u"DateString";
+inline constexpr OUStringLiteral UNO_NAME_PARA_ID = u"ParaId";
+inline constexpr OUStringLiteral UNO_NAME_PARA_ID_PARENT = u"ParaIdParent";
 #endif
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/extras/ooxmlexport/data/CommentReply.docx 
b/sw/qa/extras/ooxmlexport/data/CommentReply.docx
new file mode 100644
index 000000000000..4a78d84d55e6
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/CommentReply.docx 
differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
index 4c27a18101fd..30ef1735c73f 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
@@ -880,6 +880,22 @@ DECLARE_OOXMLEXPORT_TEST(testTdf105688, "tdf105688.docx")
     CPPUNIT_ASSERT_EQUAL(2, getPages());
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testCommentReply)
+{
+    loadAndSave("CommentReply.docx");
+    xmlDocUniquePtr pXmlComm = parseExport("word/comments.xml");
+    xmlDocUniquePtr pXmlCommExt = parseExport("word/commentsExtended.xml");
+    OUString sParentId = getXPath(pXmlComm, "/w:comments/w:comment[1]/w:p[1]", 
"paraId");
+    OUString sChildId = getXPath(pXmlComm, "/w:comments/w:comment[2]/w:p[1]", 
"paraId");
+    OUString sChildIdEx = getXPath(pXmlCommExt, 
"/w15:commentsEx/w15:commentEx", "paraId");
+    OUString sChildParentId = getXPath(pXmlCommExt,
+        "/w15:commentsEx/w15:commentEx", "paraIdParent");
+    // Make sure exported extended paraId matches the one in comments.xml
+    CPPUNIT_ASSERT_EQUAL(sChildId, sChildIdEx);
+    // Make sure the paraIdParent matches the id of its parent
+    CPPUNIT_ASSERT_EQUAL(sParentId, sChildParentId);
+}
+
 CPPUNIT_TEST_FIXTURE(Test, testCommentDone)
 {
     loadAndSave("CommentDone.docx");
diff --git a/sw/source/core/fields/docufld.cxx 
b/sw/source/core/fields/docufld.cxx
index 98010119566e..d9e998ad41f4 100644
--- a/sw/source/core/fields/docufld.cxx
+++ b/sw/source/core/fields/docufld.cxx
@@ -1741,7 +1741,9 @@ SwPostItField::SwPostItField( SwPostItFieldType* pT,
         OUString aName,
         const DateTime& rDateTime,
         const bool bResolved,
-        const sal_uInt32 nPostItId
+        const sal_uInt32 nPostItId,
+        const sal_uInt32 nParentId,
+        const sal_uInt32 nParaId
 )
     : SwField( pT )
     , m_sText( std::move(aText) )
@@ -1750,6 +1752,8 @@ SwPostItField::SwPostItField( SwPostItFieldType* pT,
     , m_sName( std::move(aName) )
     , m_aDateTime( rDateTime )
     , m_bResolved( bResolved )
+    , m_nParentId( nParentId )
+    , m_nParaId( nParaId )
 {
     m_nPostItId = nPostItId == 0 ? s_nLastPostItId++ : nPostItId;
 }
@@ -1792,7 +1796,7 @@ bool SwPostItField::GetResolved() const
 std::unique_ptr<SwField> SwPostItField::Copy() const
 {
     std::unique_ptr<SwPostItField> pRet(new SwPostItField( 
static_cast<SwPostItFieldType*>(GetTyp()), m_sAuthor, m_sText, m_sInitials, 
m_sName,
-                                                           m_aDateTime, 
m_bResolved, m_nPostItId));
+                                                           m_aDateTime, 
m_bResolved, m_nPostItId, m_nParentId, m_nParaId));
     if (mpText)
         pRet->SetTextObject( *mpText );
 
@@ -1847,6 +1851,16 @@ void SwPostItField::SetPostItId(const sal_uInt32 
nPostItId)
     m_nPostItId = nPostItId == 0 ? s_nLastPostItId++ : nPostItId;
 }
 
+void SwPostItField::SetParentId(const sal_uInt32 nParentId)
+{
+    m_nParentId = nParentId;
+}
+
+void SwPostItField::SetParaId(const sal_uInt32 nParaId)
+{
+    m_nParaId = nParaId;
+}
+
 bool SwPostItField::QueryValue( uno::Any& rAny, sal_uInt16 nWhichId ) const
 {
     switch( nWhichId )
@@ -1897,6 +1911,24 @@ bool SwPostItField::QueryValue( uno::Any& rAny, 
sal_uInt16 nWhichId ) const
             rAny <<= m_aDateTime.GetUNODateTime();
         }
         break;
+    case FIELD_PROP_PAR5:
+        {
+            OUString sTemp;
+            std::stringstream ss;
+            ss << std::uppercase << std::hex << m_nParentId;
+            sTemp = OUString::createFromAscii(ss.str().c_str());
+            rAny <<= sTemp;
+        }
+        break;
+    case FIELD_PROP_PAR6:
+        {
+            OUString sTemp;
+            std::stringstream ss;
+            ss << std::uppercase << std::hex << m_nPostItId;
+            sTemp = OUString::createFromAscii(ss.str().c_str());
+            rAny <<= sTemp;
+        }
+        break;
     default:
         assert(false);
     }
@@ -1941,6 +1973,20 @@ bool SwPostItField::PutValue( const uno::Any& rAny, 
sal_uInt16 nWhichId )
         m_aDateTime = DateTime(aDateTimeValue);
     }
     break;
+    case FIELD_PROP_PAR5:
+    {
+        OUString sTemp;
+        rAny >>= sTemp;
+        m_nParentId = sTemp.toInt32(16);
+    }
+    break;
+    case FIELD_PROP_PAR6:
+    {
+        OUString sTemp;
+        rAny >>= sTemp;
+        m_nPostItId = sTemp.toInt32(16);
+    }
+    break;
     default:
         assert(false);
     }
diff --git a/sw/source/core/inc/unofldmid.h b/sw/source/core/inc/unofldmid.h
index e6146fe011a5..f9b7d5bdc75e 100644
--- a/sw/source/core/inc/unofldmid.h
+++ b/sw/source/core/inc/unofldmid.h
@@ -48,6 +48,7 @@
 
 #define FIELD_PROP_TEXT             34
 #define FIELD_PROP_TITLE 35
+#define FIELD_PROP_PAR6             36
 
 #endif
 
diff --git a/sw/source/core/unocore/unofield.cxx 
b/sw/source/core/unocore/unofield.cxx
index 84ae8fe2d8f5..d5126bfaaf81 100644
--- a/sw/source/core/unocore/unofield.cxx
+++ b/sw/source/core/unocore/unofield.cxx
@@ -1042,6 +1042,8 @@ struct SwFieldProperties_Impl
     OUString    sPar2;
     OUString    sPar3;
     OUString    sPar4;
+    OUString    sPar5;
+    OUString    sPar6;
     Date            aDate;
     double          fDouble;
     uno::Sequence<beans::PropertyValue> aPropSeq;
@@ -1373,6 +1375,14 @@ void SAL_CALL SwXTextField::attach(
                     {
                         aDateTime = *(m_pImpl->m_pProps->pDateTime);
                     }
+
+                    sal_uInt32 nImportedId = 0;
+                    if (!m_pImpl->m_pProps->sPar6.isEmpty())
+                        nImportedId = m_pImpl->m_pProps->sPar6.toInt32(16);
+                    sal_uInt32 nParentId = 0;
+                    if (!m_pImpl->m_pProps->sPar5.isEmpty())
+                        nParentId = m_pImpl->m_pProps->sPar5.toInt32(16);
+
                     SwPostItField* pPostItField = new SwPostItField(
                         static_cast<SwPostItFieldType*>(pFieldType),
                         m_pImpl->m_pProps->sPar1, // author
@@ -1380,7 +1390,10 @@ void SAL_CALL SwXTextField::attach(
                         m_pImpl->m_pProps->sPar3, // author's initials
                         m_pImpl->m_pProps->sPar4, // name
                         aDateTime,
-                        m_pImpl->m_pProps->bBool1 // resolvedflag
+                        m_pImpl->m_pProps->bBool1, // resolvedflag
+                        0, // id
+                        nParentId, // parent id
+                        nImportedId // imported para id
                     );
                     if ( m_pImpl->m_xTextObject.is() )
                     {
@@ -2220,6 +2233,12 @@ SwXTextField::setPropertyValue(
         case FIELD_PROP_PAR4:
             rValue >>= m_pImpl->m_pProps->sPar4;
             break;
+        case FIELD_PROP_PAR5:
+            rValue >>= m_pImpl->m_pProps->sPar5;
+            break;
+        case FIELD_PROP_PAR6:
+            rValue >>= m_pImpl->m_pProps->sPar6;
+            break;
         case FIELD_PROP_FORMAT:
             rValue >>= m_pImpl->m_pProps->nFormat;
             m_pImpl->m_pProps->bFormatIsDefault = false;
@@ -2420,6 +2439,12 @@ uno::Any SAL_CALL SwXTextField::getPropertyValue(const 
OUString& rPropertyName)
             case FIELD_PROP_PAR4:
                 aRet <<= m_pImpl->m_pProps->sPar4;
                 break;
+            case FIELD_PROP_PAR5:
+                aRet <<= m_pImpl->m_pProps->sPar5;
+                break;
+            case FIELD_PROP_PAR6:
+                aRet <<= m_pImpl->m_pProps->sPar6;
+                break;
             case FIELD_PROP_FORMAT:
                 aRet <<= m_pImpl->m_pProps->nFormat;
                 break;
diff --git a/sw/source/core/unocore/unomap.cxx 
b/sw/source/core/unocore/unomap.cxx
index 0e036bff1884..92f0d8aea861 100644
--- a/sw/source/core/unocore/unomap.cxx
+++ b/sw/source/core/unocore/unomap.cxx
@@ -855,6 +855,8 @@ o3tl::span<const SfxItemPropertyMapEntry> 
SwUnoPropertyMapProvider::GetPropertyM
                     {UNO_NAME_CONTENT,    FIELD_PROP_PAR2,    
cppu::UnoType<OUString>::get(),   PROPERTY_NONE, 0},
                     {UNO_NAME_INITIALS,   FIELD_PROP_PAR3,    
cppu::UnoType<OUString>::get(),   PROPERTY_NONE, 0},
                     {UNO_NAME_NAME,       FIELD_PROP_PAR4,    
cppu::UnoType<OUString>::get(),   PROPERTY_NONE, 0},
+                    {UNO_NAME_PARA_ID_PARENT,       FIELD_PROP_PAR5,    
cppu::UnoType<OUString>::get(),   PROPERTY_NONE, 0},
+                    {UNO_NAME_PARA_ID,       FIELD_PROP_PAR6,    
cppu::UnoType<OUString>::get(),   PROPERTY_NONE, 0},
                     {UNO_NAME_RESOLVED,       FIELD_PROP_BOOL1,    
cppu::UnoType<bool>::get(),   PROPERTY_NONE, 0},
                     {UNO_NAME_DATE_TIME_VALUE,    FIELD_PROP_DATE_TIME,   
cppu::UnoType<css::util::DateTime>::get(),    PROPERTY_NONE, 0},
                     {UNO_NAME_DATE,    FIELD_PROP_DATE,   
cppu::UnoType<css::util::Date>::get(),    PROPERTY_NONE, 0},
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx 
b/sw/source/filter/ww8/docxattributeoutput.cxx
index fd4794593f1f..e1516042cd95 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -8794,12 +8794,29 @@ void DocxAttributeOutput::WritePostitFieldReference()
     }
 }
 
-DocxAttributeOutput::hasResolved DocxAttributeOutput::WritePostitFields()
+DocxAttributeOutput::hasProperties DocxAttributeOutput::WritePostitFields()
 {
     bool bRemovePersonalInfo = SvtSecurityOptions::IsOptionSet(
         SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo );
 
-    hasResolved eResult = hasResolved::no;
+    hasProperties eResult = hasProperties::no;
+    for (auto& [f1, data1] : m_postitFields)
+    {
+        if (f1->GetParentId() != 0)
+        {
+            for (size_t i = 0; i < m_postitFields.size(); i++)
+            {
+                auto& [f2, data2] = m_postitFields[i];
+                if (f2->GetParaId() == f1->GetParentId())
+                {
+                    data2.parentStatus = ParentStatus::IsParent;
+                    data1.parentStatus = ParentStatus::HasParent;
+                    data1.parentIndex = i;
+                    break;
+                }
+            }
+        }
+    }
     for (auto& [f, data] : m_postitFields)
     {
         OString idstr = OString::number(data.id);
@@ -8821,9 +8838,10 @@ DocxAttributeOutput::hasResolved 
DocxAttributeOutput::WritePostitFields()
                  : f->GetInitials().toUtf8());
         m_pSerializer->startElementNS( XML_w, XML_comment, pAttributeList );
 
-        const bool bNeedParaId = f->GetResolved();
+        // Make sure to give parent/child fields a paraId
+        const bool bNeedParaId = f->GetResolved() || data.parentStatus != 
ParentStatus::None;
         if (bNeedParaId)
-            eResult = hasResolved::yes;
+            eResult = hasProperties::yes;
 
         if (f->GetTextObject() != nullptr)
         {
@@ -8856,11 +8874,30 @@ void DocxAttributeOutput::WritePostItFieldsResolved()
 {
     for (auto& [f, data] : m_postitFields)
     {
-        if (!f->GetResolved())
+        // Parent fields don't need to be exported here if they don't have a 
resolved attribute
+        if (!f->GetResolved() && data.parentStatus != ParentStatus::HasParent)
             continue;
         OUString idstr = NumberToHexBinary(data.lastParaId);
-        m_pSerializer->singleElementNS(XML_w15, XML_commentEx, FSNS(XML_w15, 
XML_paraId), idstr,
-                                       FSNS(XML_w15, XML_done), "1");
+        std::optional<OUString> sDone, sParentId;
+        if (f->GetParentId() != 0)
+        {
+            if (data.parentStatus == ParentStatus::HasParent)
+            {
+                // Since parent fields have been resolved first, they should 
already have an id
+                auto& aParentFieldData = 
m_postitFields[data.parentIndex].second;
+                sParentId = NumberToHexBinary(aParentFieldData.lastParaId);
+            }
+            else
+            {
+                SAL_WARN("sw.ww8", "SwPostItField has a parent id, but a 
matching parent was not found");
+            }
+        }
+        if (f->GetResolved())
+            sDone = "1";
+        m_pSerializer->singleElementNS(XML_w15, XML_commentEx,
+            FSNS(XML_w15, XML_paraId), idstr,
+            FSNS(XML_w15, XML_done), sDone,
+            FSNS(XML_w15, XML_paraIdParent), sParentId);
     }
 }
 
diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx 
b/sw/source/filter/ww8/docxattributeoutput.hxx
index 989a76268e81..1f5cda25eda3 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -982,9 +982,17 @@ private:
     std::vector<PostponedDrawing> m_aPostponedActiveXControls;
     const SwField* m_PendingPlaceholder;
 
+    enum class ParentStatus
+    {
+        None,
+        IsParent,
+        HasParent
+    };
     struct PostItDOCXData{
         sal_Int32 id;
         sal_Int32 lastParaId = 0; // [MS-DOCX] 2.5.3.1 CT_CommentEx needs 
paraId attribute
+        ParentStatus parentStatus = ParentStatus::None;
+        size_t parentIndex = 0;
     };
     /// Maps postit fields to ID's, used in commentRangeStart/End, 
commentReference and comment.xml.
     std::vector<std::pair<const SwPostItField*, PostItDOCXData>> 
m_postitFields;
@@ -1061,8 +1069,8 @@ public:
     static void WriteFootnoteEndnotePr( ::sax_fastparser::FSHelperPtr const & 
fs, int tag, const SwEndNoteInfo& info, int listtag );
 
     bool HasPostitFields() const;
-    enum class hasResolved { no, yes };
-    hasResolved WritePostitFields();
+    enum class hasProperties { no, yes };
+    hasProperties WritePostitFields();
     void WritePostItFieldsResolved();
 
     /// VMLTextExport
diff --git a/sw/source/filter/ww8/docxexport.cxx 
b/sw/source/filter/ww8/docxexport.cxx
index f060773576eb..a0dced9a5559 100644
--- a/sw/source/filter/ww8/docxexport.cxx
+++ b/sw/source/filter/ww8/docxexport.cxx
@@ -756,12 +756,12 @@ void DocxExport::WritePostitFields()
 
     pPostitFS->startElementNS( XML_w, XML_comments, MainXmlNamespaces());
     m_pAttrOutput->SetSerializer( pPostitFS );
-    const auto eHasResolved = m_pAttrOutput->WritePostitFields();
+    const auto eHasProperties = m_pAttrOutput->WritePostitFields();
     m_pAttrOutput->SetSerializer( m_pDocumentFS );
     pPostitFS->endElementNS( XML_w, XML_comments );
     pPostitFS->endDocument();
 
-    if (eHasResolved != DocxAttributeOutput::hasResolved::yes)
+    if (eHasProperties != DocxAttributeOutput::hasProperties::yes)
         return;
 
     m_rFilter.addRelation(m_pDocumentFS->getOutputStream(),
diff --git a/writerfilter/inc/dmapper/CommentProperties.hxx 
b/writerfilter/inc/dmapper/CommentProperties.hxx
index d22a2f7261d3..fe66cceed3f8 100644
--- a/writerfilter/inc/dmapper/CommentProperties.hxx
+++ b/writerfilter/inc/dmapper/CommentProperties.hxx
@@ -9,12 +9,14 @@
 
 #pragma once
 
+#include <rtl/ustring.hxx>
+
 namespace writerfilter
 {
 /**
  A container for the extended comment properties linked to the last paragraph 
of a comment
 
- Corresponds to the data available in w15:commentEx elements from 
commentsExtended stream
+ Corresponds to the data available in w5:commentEx elements from 
commentsExtended stream
  ([MS-DOCX]): resolved state and parent (referring to comment that this one 
answers to; TODO).
 
  @since 7.2
@@ -22,7 +24,7 @@ namespace writerfilter
 struct CommentProperties
 {
     bool bDone;
-    // TODO: a reference to a parent comment (paraIdParent: [MS-DOCX] sect. 
2.5.3.1 CT_CommentEx)
+    OUString sParaIdParent;
 };
 }
 
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx 
b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
index 600086ca6121..8358596bdf8e 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx
@@ -2675,6 +2675,8 @@ void DomainMapper_Impl::finishParagraph( const 
PropertyMapPtr& pPropertyMap, con
             if (const auto& item = m_aCommentProps.find(sParaId); item != 
m_aCommentProps.end())
             {
                 m_bAnnotationResolved = item->second.bDone;
+                m_sAnnotationParent = item->second.sParaIdParent;
+                m_sAnnotationImportedParaId = sParaId;
             }
         }
     }
@@ -3825,6 +3827,9 @@ void DomainMapper_Impl::PopAnnotation()
         if (m_bAnnotationResolved)
             m_xAnnotationField->setPropertyValue("Resolved", 
css::uno::Any(true));
 
+        m_xAnnotationField->setPropertyValue("ParaIdParent", 
css::uno::Any(m_sAnnotationParent));
+        m_xAnnotationField->setPropertyValue("ParaId", 
css::uno::Any(m_sAnnotationImportedParaId));
+
         // See if the annotation will be a single position or a range.
         if (m_nAnnotationId == -1 || 
!m_aAnnotationPositions[m_nAnnotationId].m_xStart.is() || 
!m_aAnnotationPositions[m_nAnnotationId].m_xEnd.is())
         {
@@ -3872,6 +3877,8 @@ void DomainMapper_Impl::PopAnnotation()
     }
 
     m_xAnnotationField.clear();
+    m_sAnnotationParent.clear();
+    m_sAnnotationImportedParaId.clear();
     m_nAnnotationId = -1;
     m_bAnnotationResolved = false;
 }
diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx 
b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
index 0c6c8cd75f31..d4d25a75b4dc 100644
--- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx
+++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx
@@ -619,6 +619,8 @@ private:
     css::uno::Reference< css::beans::XPropertySet > m_xAnnotationField;
     sal_Int32 m_nAnnotationId;
     bool m_bAnnotationResolved = false;
+    OUString m_sAnnotationParent;
+    OUString m_sAnnotationImportedParaId;
     std::unordered_map< sal_Int32, AnnotationPosition > m_aAnnotationPositions;
 
     void SetNumberFormat(const OUString& rCommand, 
css::uno::Reference<css::beans::XPropertySet> const& xPropertySet, bool 
bDetectFormat = false);
diff --git a/writerfilter/source/ooxml/OOXMLFastContextHandler.cxx 
b/writerfilter/source/ooxml/OOXMLFastContextHandler.cxx
index 2563956bf97a..bf12a4e35dd1 100644
--- a/writerfilter/source/ooxml/OOXMLFastContextHandler.cxx
+++ b/writerfilter/source/ooxml/OOXMLFastContextHandler.cxx
@@ -2323,7 +2323,7 @@ 
OOXMLFastContextHandlerCommentEx::OOXMLFastContextHandlerCommentEx(
 
 void OOXMLFastContextHandlerCommentEx::lcl_endFastElement(Token_t /*Element*/)
 {
-    mpStream->commentProps(m_sParaId, { m_bDone });
+    mpStream->commentProps(m_sParaId, { m_bDone, m_sParentId });
 }
 
 void OOXMLFastContextHandlerCommentEx::att_paraId(const OOXMLValue::Pointer_t& 
pValue)
@@ -2337,6 +2337,11 @@ void OOXMLFastContextHandlerCommentEx::att_done(const 
OOXMLValue::Pointer_t& pVa
         m_bDone = true;
 }
 
+void OOXMLFastContextHandlerCommentEx::att_paraIdParent(const 
OOXMLValue::Pointer_t& pValue)
+{
+    m_sParentId = pValue->getString();
+}
+
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/writerfilter/source/ooxml/OOXMLFastContextHandler.hxx 
b/writerfilter/source/ooxml/OOXMLFastContextHandler.hxx
index 30491f08dc43..14dd62f00d64 100644
--- a/writerfilter/source/ooxml/OOXMLFastContextHandler.hxx
+++ b/writerfilter/source/ooxml/OOXMLFastContextHandler.hxx
@@ -621,10 +621,12 @@ public:
 
     void att_paraId(const OOXMLValue::Pointer_t& pValue);
     void att_done(const OOXMLValue::Pointer_t& pValue);
+    void att_paraIdParent(const OOXMLValue::Pointer_t& pValue);
 
 private:
     OUString m_sParaId;
     bool m_bDone = false;
+    OUString m_sParentId {};
 };
 
 }
diff --git a/writerfilter/source/ooxml/model.xml 
b/writerfilter/source/ooxml/model.xml
index 6ec446d67d48..352623d3e86d 100644
--- a/writerfilter/source/ooxml/model.xml
+++ b/writerfilter/source/ooxml/model.xml
@@ -5404,11 +5404,9 @@
         <attribute name="paraId">
           <ref name="ST_LongHexNumber"/>
         </attribute>
-        <!-- Not yet used
         <attribute name="paraIdParent">
           <ref name="ST_LongHexNumber"/>
         </attribute>
-        -->
         <attribute name="done">
           <ref name="ST_OnOff"/>
         </attribute>
@@ -5430,6 +5428,7 @@
     </resource>
     <resource name="CT_CommentEx" resource="CommentEx">
       <attribute name="paraId" tokenid="ooxml:CT_CommentEx_paraId" 
action="att_paraId"/>
+      <attribute name="paraIdParent" tokenid="ooxml:CT_CommentEx_paraIdParent" 
action="att_paraIdParent"/>
       <attribute name="done" tokenid="ooxml:CT_CommentEx_done" 
action="att_done"/>
     </resource>
     <resource name="ST_LongHexNumber" resource="String"/>

Reply via email to