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 18:41:21 +0200 |
commit | e61caf4d2719ebf5f696df39c41497a452c9d606 (patch) | |
tree | 5b31b950aa374cf127ba3ead4c91ead729e7611c | |
parent | lok: force writer web-view off. (diff) | |
download | core-e61caf4d2719ebf5f696df39c41497a452c9d606.tar.gz core-e61caf4d2719ebf5f696df39c41497a452c9d606.zip |
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 fbba99768711..3f798b1b2529 100644 --- a/include/oox/token/relationship.hxx +++ b/include/oox/token/relationship.hxx @@ -23,6 +23,7 @@ enum class Relationship CHARTUSERSHAPES, COMMENTS, COMMENTAUTHORS, + COMMENTSEXTENDED, CONTROL, CTRLPROP, CUSTOMXML, diff --git a/include/sax/tools/converter.hxx b/include/sax/tools/converter.hxx index a01ee6e68170..6f330fa58400 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> @@ -284,6 +285,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 e0b282c94848..f54962715fab 100644 --- a/oox/source/token/relationship.inc +++ b/oox/source/token/relationship.inc @@ -3,6 +3,7 @@ {Relationship::CHARTUSERSHAPES, "http://schemas.openxmlformats.org/officeDocument/2006/relationships/chartUserShapes"}, {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 e81ff50a217c..4d2fb881f0bc 100644 --- a/oox/source/token/tokens.txt +++ b/oox/source/token/tokens.txt @@ -1316,12 +1316,14 @@ comma command commandType comment +commentEx commentList commentPr commentRangeEnd commentRangeStart commentReference comments +commentsEx comp compact compactData @@ -1903,6 +1905,7 @@ doNotValidateAgainstSchema doNotVertAlignCellWithSp doNotVertAlignInTxbx doNotWrapTextWithPunct +done doc docDefaults docEnd @@ -3852,6 +3855,7 @@ parTrans parTransId parTxLTRAlign parTxRTLAlign +paraId paragraph parallel parallelogram @@ -5674,6 +5678,7 @@ vt w w10 w14 +w15 wAfter wArH wBefore diff --git a/sax/source/tools/converter.cxx b/sax/source/tools/converter.cxx index f4f6dccb8ddc..7a66b89ac22d 100644 --- a/sax/source/tools/converter.cxx +++ b/sax/source/tools/converter.cxx @@ -2328,6 +2328,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 ee982194ff19..1c4bb106a0ec 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport16.cxx @@ -23,6 +23,7 @@ #include <com/sun/star/text/XTextTablesSupplier.hpp> #include <com/sun/star/packages/zip/ZipFileAccess.hpp> #include <comphelper/configuration.hxx> +#include <comphelper/propertysequence.hxx> #include <editeng/escapementitem.hxx> #include <unotools/fltrcfg.hxx> @@ -311,6 +312,29 @@ DECLARE_OOXMLEXPORT_TEST(testTdf140137, "tdf140137.docx") // Don't throw exception during load } +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 60cd329fbbc9..632441524eb3 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 a34848e6df7e..34920374537c 100644 --- a/sw/source/filter/ww8/attributeoutputbase.hxx +++ b/sw/source/filter/ww8/attributeoutputbase.hxx @@ -153,7 +153,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 e3ad3f917942..7850fca4a6a2 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -90,6 +90,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> @@ -290,6 +291,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*/ ) @@ -380,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) { // Paragraphs (in headers/footers/comments/frames etc) can start before another finishes. // So a stack is needed to keep track of each paragraph's status separately. @@ -475,7 +485,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) @@ -486,6 +503,8 @@ void DocxAttributeOutput::StartParagraph( ww8::WW8TableNodeInfo::Pointer_t pText m_bParagraphOpened = true; m_bIsFirstParagraph = false; + + return nParaId; } static OString convertToOOXMLVertOrient(sal_Int16 nOrient) @@ -6191,7 +6210,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(); @@ -7971,14 +7990,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. @@ -7990,27 +8009,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); @@ -8019,6 +8048,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 8910c7eabf49..b0688183ab69 100644 --- a/sw/source/filter/ww8/docxattributeoutput.hxx +++ b/sw/source/filter/ww8/docxattributeoutput.hxx @@ -129,7 +129,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; @@ -796,6 +797,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 @@ -929,8 +933,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; @@ -1021,7 +1030,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 23e67cb66536..c3e5adc49331 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() @@ -1709,18 +1729,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; @@ -1755,6 +1778,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 f599764a45e6..08c5372460fd 100644 --- a/sw/source/filter/ww8/docxexport.hxx +++ b/sw/source/filter/ww8/docxexport.hxx @@ -189,7 +189,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 35f30f67aff0..1407f3ef0025 100644 --- a/sw/source/filter/ww8/rtfattributeoutput.cxx +++ b/sw/source/filter/ww8/rtfattributeoutput.cxx @@ -211,7 +211,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; @@ -267,6 +268,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 ae44869ea2c8..c3e20ddca49b 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 9dcc1fdacd0e..c278d57588ec 100644 --- a/sw/source/filter/ww8/wrtw8nds.cxx +++ b/sw/source/filter/ww8/wrtw8nds.cxx @@ -2313,7 +2313,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 b748abb6e1da..f601cd361303 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; |