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

Mike Kaganski (via logerrit) logerrit at kemper.freedesktop.org
Tue Apr 20 13:02:49 UTC 2021


 include/oox/token/relationship.hxx             |    1 
 include/sax/tools/converter.hxx                |   14 +++++
 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/inc/swmodeltestbase.hxx                  |    1 
 sw/source/filter/ww8/attributeoutputbase.hxx   |    2 
 sw/source/filter/ww8/docxattributeoutput.cxx   |   69 +++++++++++++++++++++----
 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 
 18 files changed, 147 insertions(+), 23 deletions(-)

New commits:
commit 4fc84b0ee4e696a1010b1ba4b8bcf7ed14454569
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: Tue Apr 20 15:02:10 2021 +0200

    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>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/114328
    Reviewed-by: Miklos Vajna <vmiklos at collabora.com>
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice at gmail.com>

diff --git a/include/oox/token/relationship.hxx b/include/oox/token/relationship.hxx
index adc25f4a73b6..40253558c799 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 b15288e51062..6cbeffd2f770 100644
--- a/include/sax/tools/converter.hxx
+++ b/include/sax/tools/converter.hxx
@@ -20,6 +20,10 @@
 #ifndef INCLUDED_SAX_TOOLS_CONVERTER_HXX
 #define INCLUDED_SAX_TOOLS_CONVERTER_HXX
 
+#include <sal/config.h>
+
+#include <type_traits>
+
 #include <sax/saxdllapi.h>
 
 #include <sal/types.h>
@@ -213,6 +217,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 d6990e5bdf91..36841732372e 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
 
diff --git a/oox/source/token/namespaces.txt b/oox/source/token/namespaces.txt
index 604541129469..20d283415069 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
 
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 3929d8c93808..9f7b815e53c2 100644
--- a/oox/source/token/tokens.txt
+++ b/oox/source/token/tokens.txt
@@ -1314,12 +1314,14 @@ comma
 command
 commandType
 comment
+commentEx
 commentList
 commentPr
 commentRangeEnd
 commentRangeStart
 commentReference
 comments
+commentsEx
 comp
 compact
 compactData
@@ -1901,6 +1903,7 @@ doNotValidateAgainstSchema
 doNotVertAlignCellWithSp
 doNotVertAlignInTxbx
 doNotWrapTextWithPunct
+done
 doc
 docDefaults
 docEnd
@@ -3850,6 +3853,7 @@ parTrans
 parTransId
 parTxLTRAlign
 parTxRTLAlign
+paraId
 paragraph
 parallel
 parallelogram
@@ -5672,6 +5676,7 @@ vt
 w
 w10
 w14
+w15
 wAfter
 wArH
 wBefore
diff --git a/sax/source/tools/converter.cxx b/sax/source/tools/converter.cxx
index 8ee00c63be1f..b73aef090e13 100644
--- a/sax/source/tools/converter.cxx
+++ b/sax/source/tools/converter.cxx
@@ -2439,6 +2439,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/inc/swmodeltestbase.hxx b/sw/qa/inc/swmodeltestbase.hxx
index 077a6e0a4205..e92b75aadce5 100644
--- a/sw/qa/inc/swmodeltestbase.hxx
+++ b/sw/qa/inc/swmodeltestbase.hxx
@@ -964,6 +964,7 @@ protected:
         xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("pic"), BAD_CAST("http://schemas.openxmlformats.org/drawingml/2006/picture"));
         xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("rels"), 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"), BAD_CAST("http://schemas.openxmlformats.org/package/2006/content-types"));
         xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("lc"), BAD_CAST("http://schemas.openxmlformats.org/drawingml/2006/lockedCanvas"));
diff --git a/sw/source/filter/ww8/attributeoutputbase.hxx b/sw/source/filter/ww8/attributeoutputbase.hxx
index 3d8daa633daa..da23f8ba3a7c 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 c2776d461c89..7310e7d5f395 100644
--- a/sw/source/filter/ww8/docxattributeoutput.cxx
+++ b/sw/source/filter/ww8/docxattributeoutput.cxx
@@ -89,6 +89,7 @@
 #include <editeng/charhiddenitem.hxx>
 #include <editeng/editobj.hxx>
 #include <editeng/keepitem.hxx>
+#include <sax/tools/converter.hxx>
 #include <svx/xfillit0.hxx>
 #include <svx/xflgrit.hxx>
 #include <svx/fmglob.hxx>
@@ -290,6 +291,15 @@ class FieldMarkParamsHelper
         return bResult;
     }
 };
+
+// [ISO/IEC29500-1:2016] 17.18.50 ST_LongHexNumber (Eight Digit Hexadecimal Value)
+static OUString NumberToHexBinary(sal_Int32 n)
+{
+    OUStringBuffer aBuf;
+    sax::Converter::convertNumberToHexBinary(aBuf, n);
+    return aBuf.makeStringAndClear();
+}
+
 void DocxAttributeOutput::RTLAndCJKState( bool bIsRTL, sal_uInt16 /*nScript*/ )
 {
     if (bIsRTL)
@@ -379,7 +389,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
@@ -468,7 +479,16 @@ 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);
+    const char* pParaId = nullptr;
+    OString aParaId;
+    sal_Int32 nParaId = 0;
+    if (bGenerateParaId)
+    {
+        nParaId = m_nNextParaId++;
+        aParaId = OUStringToOString(NumberToHexBinary(nParaId), RTL_TEXTENCODING_UTF8);
+        pParaId = aParaId.getStr();
+    }
+    m_pSerializer->startElementNS(XML_w, XML_p, FSNS(XML_w14, XML_paraId), pParaId);
 
     // postpone the output of the run (we get it before the paragraph
     // properties, but must write it after them)
@@ -479,6 +499,8 @@ void DocxAttributeOutput::StartParagraph( ww8::WW8TableNodeInfo::Pointer_t pText
 
     m_bParagraphOpened = true;
     m_bIsFirstParagraph = false;
+
+    return nParaId;
 }
 
 static OString convertToOOXMLVertOrient(sal_Int16 nOrient)
@@ -5794,7 +5816,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();
@@ -7515,14 +7537,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.
@@ -7534,27 +7556,39 @@ 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().toUtf8(),
             FSNS( XML_w, XML_date ), DateTimeToOString(f->GetDateTime()),
             FSNS( XML_w, XML_initials ), f->GetInitials().toUtf8() );
 
+        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);
+            const char* pParaId = nullptr;
+            OString aParaId;
+            if (bNeedParaId)
+            {
+                data.lastParaId = m_nNextParaId++;
+                aParaId = OUStringToOString(NumberToHexBinary(data.lastParaId), RTL_TEXTENCODING_UTF8);
+                pParaId = aParaId.getStr();
+            }
+            m_pSerializer->startElementNS(XML_w, XML_p, FSNS(XML_w14, XML_paraId), pParaId);
             m_pSerializer->startElementNS(XML_w, XML_r);
             RunText(f->GetText());
             m_pSerializer->endElementNS(XML_w, XML_r);
@@ -7563,6 +7597,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;
+        OString idstr = OUStringToOString(NumberToHexBinary(data.lastParaId), RTL_TEXTENCODING_UTF8);
+        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 6655b180bb2b..dd91f1668e10 100644
--- a/sw/source/filter/ww8/docxattributeoutput.hxx
+++ b/sw/source/filter/ww8/docxattributeoutput.hxx
@@ -127,7 +127,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;
@@ -787,6 +788,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
@@ -915,8 +919,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;
@@ -1009,7 +1018,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 ad5104b6dd7f..4e26362f6ed3 100644
--- a/sw/source/filter/ww8/docxexport.cxx
+++ b/sw/source/filter/ww8/docxexport.cxx
@@ -737,9 +737,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_pFilter->addRelation(m_pDocumentFS->getOutputStream(),
+                               oox::getRelationship(Relationship::COMMENTSEXTENDED),
+                               "commentsExtended.xml");
+
+        pPostitFS = m_pFilter->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), OUStringToOString(m_pFilter->getNamespaceURL(OOX_NS(mce)), RTL_TEXTENCODING_UTF8),
+                                  FSNS(XML_xmlns, XML_w15), OUStringToOString(m_pFilter->getNamespaceURL(OOX_NS(w15)), RTL_TEXTENCODING_UTF8),
+                                  FSNS(XML_mc, XML_Ignorable), "w15");
+        m_pAttrOutput->SetSerializer(pPostitFS);
+        m_pAttrOutput->WritePostItFieldsResolved();
+        m_pAttrOutput->SetSerializer(m_pDocumentFS);
+        pPostitFS->endElementNS(XML_w15, XML_commentsEx);
     }
 }
 
@@ -1619,18 +1639,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;
@@ -1665,6 +1688,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 f659cd1244f4..93482cc8344d 100644
--- a/sw/source/filter/ww8/docxexport.hxx
+++ b/sw/source/filter/ww8/docxexport.hxx
@@ -191,7 +191,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 53fffd032a17..b8a2fdcaf680 100644
--- a/sw/source/filter/ww8/rtfattributeoutput.cxx
+++ b/sw/source/filter/ww8/rtfattributeoutput.cxx
@@ -209,7 +209,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;
@@ -265,6 +266,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 791fdf8d2678..879ec751266c 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 2d31a2f52aed..f0d40dcb3ecc 100644
--- a/sw/source/filter/ww8/wrtw8nds.cxx
+++ b/sw/source/filter/ww8/wrtw8nds.cxx
@@ -2297,7 +2297,7 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode )
         if( softBreakList.size() > 1 ) // not for empty paragraph
             ++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 7e3f2a31ff20..9e50b953369b 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