diff options
author | Mike Kaganski <mike.kaganski@collabora.com> | 2021-04-13 09:06:52 +0300 |
---|---|---|
committer | Mike Kaganski <mike.kaganski@collabora.com> | 2021-04-16 23:02:32 +0300 |
commit | 21ac373fdfc7842a77f5cb1b5504dd73afac311b (patch) | |
tree | 3b3be3c27a8e336f0ce398fe8baf4743c9661068 | |
parent | aad248f5719231a8369014d52599c3a3a91ae3c3 (diff) |
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@collabora.com>
-rw-r--r-- | include/oox/token/relationship.hxx | 1 | ||||
-rw-r--r-- | include/sax/tools/converter.hxx | 11 | ||||
-rw-r--r-- | oox/source/token/namespaces-strict.txt | 1 | ||||
-rw-r--r-- | oox/source/token/namespaces.txt | 1 | ||||
-rw-r--r-- | oox/source/token/relationship.inc | 1 | ||||
-rw-r--r-- | oox/source/token/tokens.txt | 5 | ||||
-rw-r--r-- | sax/source/tools/converter.cxx | 15 | ||||
-rw-r--r-- | sw/qa/extras/ooxmlexport/data/CommentDone.docx | bin | 0 -> 20946 bytes | |||
-rw-r--r-- | sw/qa/extras/ooxmlexport/ooxmlexport16.cxx | 24 | ||||
-rw-r--r-- | sw/qa/unit/swmodeltestbase.cxx | 2 | ||||
-rw-r--r-- | sw/source/filter/ww8/attributeoutputbase.hxx | 2 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxattributeoutput.cxx | 64 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxattributeoutput.hxx | 17 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxexport.cxx | 30 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxexport.hxx | 2 | ||||
-rw-r--r-- | sw/source/filter/ww8/rtfattributeoutput.cxx | 4 | ||||
-rw-r--r-- | sw/source/filter/ww8/rtfattributeoutput.hxx | 3 | ||||
-rw-r--r-- | sw/source/filter/ww8/wrtw8nds.cxx | 2 | ||||
-rw-r--r-- | sw/source/filter/ww8/ww8attributeoutput.hxx | 2 |
19 files changed, 164 insertions, 23 deletions
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 Binary files differnew file mode 100644 index 000000000000..1ce5993d440b --- /dev/null +++ b/sw/qa/extras/ooxmlexport/data/CommentDone.docx 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; |