[Libreoffice-commits] core.git: Branch 'distro/collabora/co-2021' - include/oox include/sax oox/source sax/source sw/qa sw/source

Mike Kaganski (via logerrit) logerrit at kemper.freedesktop.org
Fri Apr 16 20:04:27 UTC 2021


 include/oox/token/relationship.hxx             |    1 
 include/sax/tools/converter.hxx                |   11 ++++
 oox/source/token/namespaces-strict.txt         |    1 
 oox/source/token/namespaces.txt                |    1 
 oox/source/token/relationship.inc              |    1 
 oox/source/token/tokens.txt                    |    5 +
 sax/source/tools/converter.cxx                 |   15 +++++
 sw/qa/extras/ooxmlexport/data/CommentDone.docx |binary
 sw/qa/extras/ooxmlexport/ooxmlexport16.cxx     |   24 +++++++++
 sw/qa/unit/swmodeltestbase.cxx                 |    2 
 sw/source/filter/ww8/attributeoutputbase.hxx   |    2 
 sw/source/filter/ww8/docxattributeoutput.cxx   |   64 ++++++++++++++++++++-----
 sw/source/filter/ww8/docxattributeoutput.hxx   |   17 +++++-
 sw/source/filter/ww8/docxexport.cxx            |   30 ++++++++++-
 sw/source/filter/ww8/docxexport.hxx            |    2 
 sw/source/filter/ww8/rtfattributeoutput.cxx    |    4 +
 sw/source/filter/ww8/rtfattributeoutput.hxx    |    3 -
 sw/source/filter/ww8/wrtw8nds.cxx              |    2 
 sw/source/filter/ww8/ww8attributeoutput.hxx    |    2 
 19 files changed, 164 insertions(+), 23 deletions(-)

New commits:
commit 21ac373fdfc7842a77f5cb1b5504dd73afac311b
Author:     Mike Kaganski <mike.kaganski at collabora.com>
AuthorDate: Tue Apr 13 09:06:52 2021 +0300
Commit:     Mike Kaganski <mike.kaganski at collabora.com>
CommitDate: Fri Apr 16 23:02:32 2021 +0300

    tdf#122222: add DOCX export of resolved comments as "done"
    
    Since implementation of tdf#119228, Writer comments may have
    "Resolved" state, which is the equivalent of Word's internal
    "done" flag.
    
    This relies on [MS-DOCX] extensions available since Word 2013.
    DOCX import will be implemented in a follow-up commit.
    
    [MS-DOCX]: https://docs.microsoft.com/en-us/openspecs/office_standards/ms-docx
    
    Change-Id: I3be1e8a096bdec41c8268974fe81328480eb0704
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/114023
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kaganski at collabora.com>

diff --git a/include/oox/token/relationship.hxx b/include/oox/token/relationship.hxx
index adffd4b6617e..54de0700d64e 100644
--- a/include/oox/token/relationship.hxx
+++ b/include/oox/token/relationship.hxx
@@ -22,6 +22,7 @@ enum class Relationship
     CHART,
     COMMENTS,
     COMMENTAUTHORS,
+    COMMENTSEXTENDED,
     CONTROL,
     CTRLPROP,
     CUSTOMXML,
diff --git a/include/sax/tools/converter.hxx b/include/sax/tools/converter.hxx
index e5e6d5764d0f..a8877ad59a4a 100644
--- a/include/sax/tools/converter.hxx
+++ b/include/sax/tools/converter.hxx
@@ -23,6 +23,7 @@
 #include <sal/config.h>
 
 #include <optional>
+#include <type_traits>
 
 #include <sax/saxdllapi.h>
 
@@ -215,6 +216,16 @@ public:
                            OUStringBuffer&          rsType ,
                            const css::uno::Any& rValue);
 
+    /** convert specified byte sequence to xsd:hexBinary string **/
+    static void convertBytesToHexBinary(OUStringBuffer& rBuffer, const void* pBytes,
+                                        sal_Int32 nBytes);
+
+    template <typename T, std::enable_if_t<std::is_arithmetic_v<T>, int> = 0>
+    static void convertNumberToHexBinary(OUStringBuffer& rBuffer, T n)
+    {
+        convertBytesToHexBinary(rBuffer, &n, sizeof(n));
+    }
+
 };
 
 }
diff --git a/oox/source/token/namespaces-strict.txt b/oox/source/token/namespaces-strict.txt
index 9a62a301a513..5024249bfacc 100644
--- a/oox/source/token/namespaces-strict.txt
+++ b/oox/source/token/namespaces-strict.txt
@@ -84,6 +84,7 @@ p14                     http://schemas.microsoft.com/office/powerpoint/2010/main
 
 # MSO 2012/2013 extensions ---------------------------------------------------------
 
+w15                     http://schemas.microsoft.com/office/word/2012/wordml
 p15                     http://schemas.microsoft.com/office/powerpoint/2012/main
 x12ac                   http://schemas.microsoft.com/office/spreadsheetml/2011/1/ac
 c15                     http://schemas.microsoft.com/office/drawing/2012/chart
diff --git a/oox/source/token/namespaces.txt b/oox/source/token/namespaces.txt
index 82bec7274c32..f18e0833f31d 100644
--- a/oox/source/token/namespaces.txt
+++ b/oox/source/token/namespaces.txt
@@ -84,6 +84,7 @@ p14                     http://schemas.microsoft.com/office/powerpoint/2010/main
 
 # MSO 2012/2013 extensions ---------------------------------------------------------
 
+w15                     http://schemas.microsoft.com/office/word/2012/wordml
 p15                     http://schemas.microsoft.com/office/powerpoint/2012/main
 x12ac                   http://schemas.microsoft.com/office/spreadsheetml/2011/1/ac
 c15                     http://schemas.microsoft.com/office/drawing/2012/chart
diff --git a/oox/source/token/relationship.inc b/oox/source/token/relationship.inc
index 2b973ded1653..31d46cdd7d71 100644
--- a/oox/source/token/relationship.inc
+++ b/oox/source/token/relationship.inc
@@ -2,6 +2,7 @@
 {Relationship::CHART, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart"},
 {Relationship::COMMENTS, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments"},
 {Relationship::COMMENTAUTHORS, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/commentAuthors"},
+{Relationship::COMMENTSEXTENDED, "http://schemas.microsoft.com/office/2011/relationships/commentsExtended"},
 {Relationship::CONTROL, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/control"},
 {Relationship::CTRLPROP, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp"},
 {Relationship::CUSTOMXML, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/customXml"},
diff --git a/oox/source/token/tokens.txt b/oox/source/token/tokens.txt
index 27396f5b8dfa..25f607cb5ae0 100644
--- a/oox/source/token/tokens.txt
+++ b/oox/source/token/tokens.txt
@@ -1315,12 +1315,14 @@ comma
 command
 commandType
 comment
+commentEx
 commentList
 commentPr
 commentRangeEnd
 commentRangeStart
 commentReference
 comments
+commentsEx
 comp
 compact
 compactData
@@ -1902,6 +1904,7 @@ doNotValidateAgainstSchema
 doNotVertAlignCellWithSp
 doNotVertAlignInTxbx
 doNotWrapTextWithPunct
+done
 doc
 docDefaults
 docEnd
@@ -3851,6 +3854,7 @@ parTrans
 parTransId
 parTxLTRAlign
 parTxRTLAlign
+paraId
 paragraph
 parallel
 parallelogram
@@ -5673,6 +5677,7 @@ vt
 w
 w10
 w14
+w15
 wAfter
 wArH
 wBefore
diff --git a/sax/source/tools/converter.cxx b/sax/source/tools/converter.cxx
index 4e43e332f1e7..c168f887e681 100644
--- a/sax/source/tools/converter.cxx
+++ b/sax/source/tools/converter.cxx
@@ -2444,6 +2444,21 @@ bool Converter::convertAny(OUStringBuffer&    rsValue,
     return bConverted;
 }
 
+void Converter::convertBytesToHexBinary(OUStringBuffer& rBuffer, const void* pBytes,
+                                        sal_Int32 nBytes)
+{
+    rBuffer.setLength(0);
+    rBuffer.ensureCapacity(nBytes * 2);
+    auto pChars = static_cast<const unsigned char*>(pBytes);
+    for (sal_Int32 i = 0; i < nBytes; ++i)
+    {
+        sal_Int32 c = *pChars++;
+        if (c < 16)
+            rBuffer.append('0');
+        rBuffer.append(c, 16);
+    }
+}
+
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/extras/ooxmlexport/data/CommentDone.docx b/sw/qa/extras/ooxmlexport/data/CommentDone.docx
new file mode 100644
index 000000000000..1ce5993d440b
Binary files /dev/null and b/sw/qa/extras/ooxmlexport/data/CommentDone.docx differ
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
index b6e8cc341b02..3bb23d4e40aa 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx
@@ -18,6 +18,7 @@
 #include <com/sun/star/text/XTextViewCursorSupplier.hpp>
 #include <com/sun/star/text/XTextTable.hpp>
 #include <com/sun/star/text/XTextTablesSupplier.hpp>
+#include <comphelper/propertysequence.hxx>
 #include <editeng/escapementitem.hxx>
 
 char const DATA_DIRECTORY[] = "/sw/qa/extras/ooxmlexport/data/";
@@ -171,6 +172,29 @@ DECLARE_OOXMLEXPORT_TEST(testTdf133473_shadowSize, "tdf133473.docx")
     CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(200000), nSize1);
 }
 
+DECLARE_OOXMLEXPORT_TEST(testCommentDone, "CommentDone.docx")
+{
+    if (!mbExported)
+    {
+        // This manually toggles (enables) the resolved state of the first comment now, while
+        // import is not yet implemented (TODO)
+        uno::Sequence<beans::PropertyValue> aPropertyValues = comphelper::InitPropertySequence(
+            { { "Id", uno::makeAny(OUString("1")) } });
+        dispatchCommand(mxComponent, ".uno:ResolveComment", aPropertyValues);
+        return;
+    }
+
+    xmlDocUniquePtr pXmlComm = parseExport("word/comments.xml");
+    assertXPath(pXmlComm, "/w:comments/w:comment[1]/w:p", 2);
+    OUString idLastPara = getXPath(pXmlComm, "/w:comments/w:comment[1]/w:p[2]", "paraId");
+    xmlDocUniquePtr pXmlCommExt = parseExport("word/commentsExtended.xml");
+    assertXPath(pXmlCommExt, "/w15:commentsEx", "Ignorable", "w15");
+    assertXPath(pXmlCommExt, "/w15:commentsEx/w15:commentEx", 1);
+    OUString idLastParaEx = getXPath(pXmlCommExt, "/w15:commentsEx/w15:commentEx", "paraId");
+    CPPUNIT_ASSERT_EQUAL(idLastPara, idLastParaEx);
+    assertXPath(pXmlCommExt, "/w15:commentsEx/w15:commentEx", "done", "1");
+}
+
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/unit/swmodeltestbase.cxx b/sw/qa/unit/swmodeltestbase.cxx
index b520967b21f4..e2c991353b7f 100644
--- a/sw/qa/unit/swmodeltestbase.cxx
+++ b/sw/qa/unit/swmodeltestbase.cxx
@@ -722,6 +722,8 @@ void SwModelTestBase::registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx)
                        BAD_CAST("http://schemas.openxmlformats.org/package/2006/relationships"));
     xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("w14"),
                        BAD_CAST("http://schemas.microsoft.com/office/word/2010/wordml"));
+    xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("w15"),
+                       BAD_CAST("http://schemas.microsoft.com/office/word/2012/wordml"));
     xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("m"),
                        BAD_CAST("http://schemas.openxmlformats.org/officeDocument/2006/math"));
     xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("ContentType"),
diff --git a/sw/source/filter/ww8/attributeoutputbase.hxx b/sw/source/filter/ww8/attributeoutputbase.hxx
index 327f1fa9d26d..566e9c986a5f 100644
--- a/sw/source/filter/ww8/attributeoutputbase.hxx
+++ b/sw/source/filter/ww8/attributeoutputbase.hxx
@@ -155,7 +155,7 @@ public:
     virtual void RTLAndCJKState( bool bIsRTL, sal_uInt16 nScript ) = 0;
 
     /// Start of the paragraph.
-    virtual void StartParagraph( ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo ) = 0;
+    virtual sal_Int32 StartParagraph( ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo, bool bGenerateParaId ) = 0;
 
     /// End of the paragraph.
     virtual void EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) = 0;
diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx
index 318c40dd9248..cf61fcd09370 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -89,6 +89,7 @@
 #include <editeng/editobj.hxx>
 #include <editeng/keepitem.hxx>
 #include <editeng/borderline.hxx>
+#include <sax/tools/converter.hxx>
 #include <svx/xdef.hxx>
 #include <svx/xfillit0.hxx>
 #include <svx/xflclit.hxx>
@@ -291,6 +292,14 @@ class FieldMarkParamsHelper
     }
 };
 
+// [ISO/IEC29500-1:2016] 17.18.50 ST_LongHexNumber (Eight Digit Hexadecimal Value)
+OUString NumberToHexBinary(sal_Int32 n)
+{
+    OUStringBuffer aBuf;
+    sax::Converter::convertNumberToHexBinary(aBuf, n);
+    return aBuf.makeStringAndClear();
+}
+
 }
 
 void DocxAttributeOutput::RTLAndCJKState( bool bIsRTL, sal_uInt16 /*nScript*/ )
@@ -381,7 +390,8 @@ static void checkAndWriteFloatingTables(DocxAttributeOutput& rDocxAttributeOutpu
     }
 }
 
-void DocxAttributeOutput::StartParagraph( ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo )
+sal_Int32 DocxAttributeOutput::StartParagraph(ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo,
+                                              bool bGenerateParaId)
 {
     // look ahead for floating tables that were put into a frame during import
     // floating tables in shapes are not supported: exclude this case
@@ -470,7 +480,14 @@ void DocxAttributeOutput::StartParagraph( ww8::WW8TableNodeInfo::Pointer_t pText
     // We will only know if we have to do that later.
     m_pSerializer->mark(Tag_StartParagraph_1);
 
-    m_pSerializer->startElementNS(XML_w, XML_p);
+    std::optional<OUString> aParaId;
+    sal_Int32 nParaId = 0;
+    if (bGenerateParaId)
+    {
+        nParaId = m_nNextParaId++;
+        aParaId = NumberToHexBinary(nParaId);
+    }
+    m_pSerializer->startElementNS(XML_w, XML_p, FSNS(XML_w14, XML_paraId), aParaId);
 
     // postpone the output of the run (we get it before the paragraph
     // properties, but must write it after them)
@@ -481,6 +498,8 @@ void DocxAttributeOutput::StartParagraph( ww8::WW8TableNodeInfo::Pointer_t pText
 
     m_bParagraphOpened = true;
     m_bIsFirstParagraph = false;
+
+    return nParaId;
 }
 
 static OString convertToOOXMLVertOrient(sal_Int16 nOrient)
@@ -6149,7 +6168,7 @@ void DocxAttributeOutput::WriteOutliner(const OutlinerParaObject& rParaObj)
         sal_Int32 nCurrentPos = 0;
         sal_Int32 nEnd = aStr.getLength();
 
-        StartParagraph(ww8::WW8TableNodeInfo::Pointer_t());
+        StartParagraph(ww8::WW8TableNodeInfo::Pointer_t(), false);
 
         // Write paragraph properties.
         StartParagraphProperties();
@@ -7940,14 +7959,14 @@ void DocxAttributeOutput::PostitField( const SwField* pField )
     else
         // Otherwise get a new one.
         nId = m_nNextAnnotationMarkId++;
-    m_postitFields.emplace_back(pPostItField, nId);
+    m_postitFields.emplace_back(pPostItField, PostItDOCXData{ nId });
 }
 
 void DocxAttributeOutput::WritePostitFieldReference()
 {
     while( m_postitFieldsMaxId < m_postitFields.size())
     {
-        OString idstr = OString::number(m_postitFields[m_postitFieldsMaxId].second);
+        OString idstr = OString::number(m_postitFields[m_postitFieldsMaxId].second.id);
 
         // In case this file is inside annotation marks, we want to write the
         // comment reference after the annotation mark is closed, not here.
@@ -7959,27 +7978,37 @@ void DocxAttributeOutput::WritePostitFieldReference()
     }
 }
 
-void DocxAttributeOutput::WritePostitFields()
+DocxAttributeOutput::hasResolved DocxAttributeOutput::WritePostitFields()
 {
-    for (const auto& rPair : m_postitFields)
+    hasResolved eResult = hasResolved::no;
+    for (auto& [f, data] : m_postitFields)
     {
-        OString idstr = OString::number( rPair.second);
-        const SwPostItField* f = rPair.first;
+        OString idstr = OString::number(data.id);
         m_pSerializer->startElementNS( XML_w, XML_comment, FSNS( XML_w, XML_id ), idstr,
             FSNS( XML_w, XML_author ), f->GetPar1(),
             FSNS( XML_w, XML_date ), DateTimeToOString(f->GetDateTime()),
             FSNS( XML_w, XML_initials ), f->GetInitials() );
 
+        const bool bNeedParaId = f->GetResolved();
+        if (bNeedParaId)
+            eResult = hasResolved::yes;
+
         if (f->GetTextObject() != nullptr)
         {
             // richtext
-            GetExport().WriteOutliner(*f->GetTextObject(), TXT_ATN);
+            data.lastParaId = GetExport().WriteOutliner(*f->GetTextObject(), TXT_ATN, bNeedParaId);
         }
         else
         {
             // just plain text - eg. when the field was created via the
             // .uno:InsertAnnotation API
-            m_pSerializer->startElementNS(XML_w, XML_p);
+            std::optional<OUString> aParaId;
+            if (bNeedParaId)
+            {
+                data.lastParaId = m_nNextParaId++;
+                aParaId = NumberToHexBinary(data.lastParaId);
+            }
+            m_pSerializer->startElementNS(XML_w, XML_p, FSNS(XML_w14, XML_paraId), aParaId);
             m_pSerializer->startElementNS(XML_w, XML_r);
             RunText(f->GetText());
             m_pSerializer->endElementNS(XML_w, XML_r);
@@ -7988,6 +8017,19 @@ void DocxAttributeOutput::WritePostitFields()
 
         m_pSerializer->endElementNS( XML_w, XML_comment );
     }
+    return eResult;
+}
+
+void DocxAttributeOutput::WritePostItFieldsResolved()
+{
+    for (auto& [f, data] : m_postitFields)
+    {
+        if (!f->GetResolved())
+            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");
+    }
 }
 
 bool DocxAttributeOutput::DropdownField( const SwField* pField )
diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx
index de32f003a5df..2253dbec0b0a 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -128,7 +128,8 @@ public:
     virtual void RTLAndCJKState( bool bIsRTL, sal_uInt16 nScript ) override;
 
     /// Start of the paragraph.
-    virtual void StartParagraph( ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo ) override;
+    virtual sal_Int32 StartParagraph(ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo,
+                                     bool bGenerateParaId) override;
 
     /// End of the paragraph.
     virtual void EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) override;
@@ -792,6 +793,9 @@ private:
     sal_Int32 m_nNextBookmarkId;
     sal_Int32 m_nNextAnnotationMarkId;
 
+    /// [MS-DOCX] section 2.6.2.3
+    sal_Int32 m_nNextParaId = 1; // MUST be greater than 0
+
     OUString m_sRawText;
 
     /// Bookmarks to output
@@ -925,8 +929,13 @@ private:
     std::vector<const SdrObject*> m_aPostponedFormControls;
     std::vector<PostponedDrawing> m_aPostponedActiveXControls;
     const SwField* pendingPlaceholder;
+
+    struct PostItDOCXData{
+        sal_Int32 id;
+        sal_Int32 lastParaId = 0; // [MS-DOCX] 2.5.3.1 CT_CommentEx needs paraId attribute
+    };
     /// Maps postit fields to ID's, used in commentRangeStart/End, commentReference and comment.xml.
-    std::vector< std::pair<const SwPostItField*, sal_Int32> > m_postitFields;
+    std::vector<std::pair<const SwPostItField*, PostItDOCXData>> m_postitFields;
     /// Number of postit fields which already have a commentReference written.
     unsigned int m_postitFieldsMaxId;
     int m_anchorId;
@@ -1017,7 +1026,9 @@ public:
     static void WriteFootnoteEndnotePr( ::sax_fastparser::FSHelperPtr const & fs, int tag, const SwEndNoteInfo& info, int listtag );
 
     bool HasPostitFields() const;
-    void WritePostitFields();
+    enum class hasResolved { no, yes };
+    hasResolved WritePostitFields();
+    void WritePostItFieldsResolved();
 
     /// VMLTextExport
     virtual void WriteOutliner(const OutlinerParaObject& rParaObj) override;
diff --git a/sw/source/filter/ww8/docxexport.cxx b/sw/source/filter/ww8/docxexport.cxx
index 04db5fceda4c..d46693f3e10f 100644
--- a/sw/source/filter/ww8/docxexport.cxx
+++ b/sw/source/filter/ww8/docxexport.cxx
@@ -736,9 +736,29 @@ void DocxExport::WritePostitFields()
 
     pPostitFS->startElementNS( XML_w, XML_comments, MainXmlNamespaces());
     m_pAttrOutput->SetSerializer( pPostitFS );
-    m_pAttrOutput->WritePostitFields();
+    const auto eHasResolved = m_pAttrOutput->WritePostitFields();
     m_pAttrOutput->SetSerializer( m_pDocumentFS );
     pPostitFS->endElementNS( XML_w, XML_comments );
+
+    if (eHasResolved != DocxAttributeOutput::hasResolved::yes)
+        return;
+
+    m_rFilter.addRelation(m_pDocumentFS->getOutputStream(),
+                          oox::getRelationship(Relationship::COMMENTSEXTENDED),
+                          "commentsExtended.xml");
+
+    pPostitFS = m_rFilter.openFragmentStreamWithSerializer(
+        "word/commentsExtended.xml",
+        "application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml");
+
+    pPostitFS->startElementNS(XML_w15, XML_commentsEx, // Add namespaces manually now
+                              FSNS(XML_xmlns, XML_mc), m_rFilter.getNamespaceURL(OOX_NS(mce)),
+                              FSNS(XML_xmlns, XML_w15), m_rFilter.getNamespaceURL(OOX_NS(w15)),
+                              FSNS(XML_mc, XML_Ignorable), "w15");
+    m_pAttrOutput->SetSerializer(pPostitFS);
+    m_pAttrOutput->WritePostItFieldsResolved();
+    m_pAttrOutput->SetSerializer(m_pDocumentFS);
+    pPostitFS->endElementNS(XML_w15, XML_commentsEx);
 }
 
 void DocxExport::WriteNumbering()
@@ -1715,18 +1735,21 @@ bool DocxExport::ignoreAttributeForStyleDefaults( sal_uInt16 nWhich ) const
     return MSWordExportBase::ignoreAttributeForStyleDefaults( nWhich );
 }
 
-void DocxExport::WriteOutliner(const OutlinerParaObject& rParaObj, sal_uInt8 nTyp)
+sal_Int32 DocxExport::WriteOutliner(const OutlinerParaObject& rParaObj, sal_uInt8 nTyp,
+                                    bool bNeedsLastParaId)
 {
     const EditTextObject& rEditObj = rParaObj.GetTextObject();
     MSWord_SdrAttrIter aAttrIter( *this, rEditObj, nTyp );
 
     sal_Int32 nPara = rEditObj.GetParagraphCount();
+    sal_Int32 nParaId = 0;
     for( sal_Int32 n = 0; n < nPara; ++n )
     {
         if( n )
             aAttrIter.NextPara( n );
 
-        AttrOutput().StartParagraph( ww8::WW8TableNodeInfo::Pointer_t());
+        nParaId = AttrOutput().StartParagraph(ww8::WW8TableNodeInfo::Pointer_t(),
+                                              bNeedsLastParaId && n == nPara - 1);
         rtl_TextEncoding eChrSet = aAttrIter.GetNodeCharSet();
         OUString aStr( rEditObj.GetText( n ));
         sal_Int32 nCurrentPos = 0;
@@ -1761,6 +1784,7 @@ void DocxExport::WriteOutliner(const OutlinerParaObject& rParaObj, sal_uInt8 nTy
 //        aAttrIter.OutParaAttr(false);
         AttrOutput().EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t());
     }
+    return nParaId;
 }
 
 void DocxExport::SetFS( ::sax_fastparser::FSHelperPtr const & pFS )
diff --git a/sw/source/filter/ww8/docxexport.hxx b/sw/source/filter/ww8/docxexport.hxx
index 5091b8f64979..c89c60dbda13 100644
--- a/sw/source/filter/ww8/docxexport.hxx
+++ b/sw/source/filter/ww8/docxexport.hxx
@@ -190,7 +190,7 @@ public:
     /// Writes the shape using drawingML syntax.
     void OutputDML( css::uno::Reference< css::drawing::XShape > const & xShape );
 
-    void WriteOutliner(const OutlinerParaObject& rOutliner, sal_uInt8 nTyp);
+    sal_Int32 WriteOutliner(const OutlinerParaObject& rOutliner, sal_uInt8 nTyp, bool bNeedsLastParaId);
 
     virtual ExportFormat GetExportFormat() const override { return ExportFormat::DOCX; }
 
diff --git a/sw/source/filter/ww8/rtfattributeoutput.cxx b/sw/source/filter/ww8/rtfattributeoutput.cxx
index dd4434c2593f..b39eac143f1c 100644
--- a/sw/source/filter/ww8/rtfattributeoutput.cxx
+++ b/sw/source/filter/ww8/rtfattributeoutput.cxx
@@ -210,7 +210,8 @@ void RtfAttributeOutput::RTLAndCJKState(bool bIsRTL, sal_uInt16 nScript)
     m_bControlLtrRtl = true;
 }
 
-void RtfAttributeOutput::StartParagraph(ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo)
+sal_Int32 RtfAttributeOutput::StartParagraph(ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo,
+                                             bool /*bGenerateParaId*/)
 {
     if (m_bIsBeforeFirstParagraph && m_rExport.m_nTextTyp != TXT_HDFT)
         m_bIsBeforeFirstParagraph = false;
@@ -266,6 +267,7 @@ void RtfAttributeOutput::StartParagraph(ww8::WW8TableNodeInfo::Pointer_t pTextNo
     }
 
     OSL_ENSURE(m_aRun.getLength() == 0, "m_aRun is not empty");
+    return 0;
 }
 
 void RtfAttributeOutput::EndParagraph(ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner)
diff --git a/sw/source/filter/ww8/rtfattributeoutput.hxx b/sw/source/filter/ww8/rtfattributeoutput.hxx
index 6c1ccdf135fd..2bf1def4b8d2 100644
--- a/sw/source/filter/ww8/rtfattributeoutput.hxx
+++ b/sw/source/filter/ww8/rtfattributeoutput.hxx
@@ -48,7 +48,8 @@ public:
     void RTLAndCJKState(bool bIsRTL, sal_uInt16 nScript) override;
 
     /// Start of the paragraph.
-    void StartParagraph(ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo) override;
+    sal_Int32 StartParagraph(ww8::WW8TableNodeInfo::Pointer_t pTextNodeInfo,
+                             bool bGenerateParaId) override;
 
     /// End of the paragraph.
     void EndParagraph(ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner) override;
diff --git a/sw/source/filter/ww8/wrtw8nds.cxx b/sw/source/filter/ww8/wrtw8nds.cxx
index 1404aff20f3b..c8b65cf1004d 100644
--- a/sw/source/filter/ww8/wrtw8nds.cxx
+++ b/sw/source/filter/ww8/wrtw8nds.cxx
@@ -2300,7 +2300,7 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode )
                 ++aBreakIt;
         }
 
-        AttrOutput().StartParagraph( pTextNodeInfo );
+        AttrOutput().StartParagraph(pTextNodeInfo, false);
 
         const SwSection* pTOXSect = nullptr;
         if( m_bInWriteTOX )
diff --git a/sw/source/filter/ww8/ww8attributeoutput.hxx b/sw/source/filter/ww8/ww8attributeoutput.hxx
index ac4e931ecf0d..643c982bb29c 100644
--- a/sw/source/filter/ww8/ww8attributeoutput.hxx
+++ b/sw/source/filter/ww8/ww8attributeoutput.hxx
@@ -32,7 +32,7 @@ public:
     virtual void RTLAndCJKState( bool bIsRTL, sal_uInt16 nScript ) override;
 
     /// Start of the paragraph.
-    virtual void StartParagraph( ww8::WW8TableNodeInfo::Pointer_t /*pTextNodeInfo*/ ) override {}
+    virtual sal_Int32 StartParagraph( ww8::WW8TableNodeInfo::Pointer_t /*pTextNodeInfo*/, bool /*bGenerateParaId*/ ) override { return 0; }
 
     /// End of the paragraph.
     virtual void EndParagraph( ww8::WW8TableNodeInfoInner::Pointer_t pTextNodeInfoInner ) override;


More information about the Libreoffice-commits mailing list