diff options
author | László Németh <nemeth@numbertext.org> | 2021-12-02 17:45:46 +0100 |
---|---|---|
committer | László Németh <nemeth@numbertext.org> | 2021-12-03 13:10:57 +0100 |
commit | 9e1e88ad5cf2dc0e9b188c60930445652a6c7519 (patch) | |
tree | 13a4fc3c4fdb1ee9283d218495e2c53edf6eeb27 | |
parent | 7563216425c1a43166db083d809073b268f7884f (diff) |
tdf#145720 DOCX export: fix loss of tracked moving
of documents created in MSO to keep interoperability.
Export moved redlines as moveFrom/moveTo instead of
del/ins elements (also for newly created tracked moving).
Export "MoveBookmark" elements moveFromRangeStart,
moveFromRangeEnd, moveToRangeStart, moveToRangeEnd, which
imported from DOCX documents created in MSO. Without them,
moveFrom/moveTo elements were imported as plain deletion
or insertion in MSO.
Note: MoveBookmark elements were imported and exported as
collapsed plain bookmarks. Now keep their ranges, also store
the information of moveFrom/moveTo for correct export.
In the export filter, mandatory author and date of the tracking
information restored from RedlineData of the first redline
within the MoveBookmark, if it's possible.
Follow-up to commit f51fa7534421a195a58b4a737a2e836d8c25ba81
"tdf#145718 sw, DOCX import: complete tracked text moving".
Change-Id: I54242453a7f7d8f73ea074fc74e8e7bc86d07d01
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/126258
Tested-by: László Németh <nemeth@numbertext.org>
Reviewed-by: László Németh <nemeth@numbertext.org>
-rw-r--r-- | sw/qa/extras/ooxmlexport/ooxmlexport11.cxx | 86 | ||||
-rw-r--r-- | sw/qa/extras/ooxmlexport/ooxmlexport13.cxx | 25 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxattributeoutput.cxx | 78 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxattributeoutput.hxx | 9 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxexport.cxx | 5 | ||||
-rw-r--r-- | sw/source/filter/ww8/docxexport.hxx | 2 | ||||
-rw-r--r-- | sw/source/filter/ww8/rtfexport.cxx | 3 | ||||
-rw-r--r-- | sw/source/filter/ww8/rtfexport.hxx | 3 | ||||
-rw-r--r-- | sw/source/filter/ww8/wrtw8nds.cxx | 24 | ||||
-rw-r--r-- | sw/source/filter/ww8/wrtww8.cxx | 2 | ||||
-rw-r--r-- | sw/source/filter/ww8/wrtww8.hxx | 6 | ||||
-rw-r--r-- | writerfilter/source/dmapper/DomainMapper.cxx | 2 | ||||
-rw-r--r-- | writerfilter/source/dmapper/DomainMapper_Impl.cxx | 19 | ||||
-rw-r--r-- | writerfilter/source/dmapper/DomainMapper_Impl.hxx | 4 |
14 files changed, 199 insertions, 69 deletions
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx index 8cb43ffe3e2c..5fae2dab136d 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx @@ -885,9 +885,9 @@ DECLARE_OOXMLEXPORT_TEST(testTdf104797, "tdf104797.docx") // check moveFrom and moveTo CPPUNIT_ASSERT_EQUAL( OUString( "Will this sentence be duplicated?" ), getParagraph( 1 )->getString()); CPPUNIT_ASSERT_EQUAL( OUString( "" ), getRun( getParagraph( 1 ), 1 )->getString()); - CPPUNIT_ASSERT(hasProperty(getRun(getParagraph(1), 2), "RedlineType")); - CPPUNIT_ASSERT_EQUAL(OUString("Delete"),getProperty<OUString>(getRun(getParagraph(1), 2), "RedlineType")); - CPPUNIT_ASSERT_EQUAL(true,getProperty<bool>(getRun(getParagraph(1), 2), "IsStart")); + CPPUNIT_ASSERT(hasProperty(getRun(getParagraph(1), 3), "RedlineType")); + CPPUNIT_ASSERT_EQUAL(OUString("Delete"),getProperty<OUString>(getRun(getParagraph(1), 3), "RedlineType")); + CPPUNIT_ASSERT_EQUAL(true,getProperty<bool>(getRun(getParagraph(1), 3), "IsStart")); CPPUNIT_ASSERT_EQUAL( OUString( "This is a filler sentence. Will this sentence be duplicated ADDED STUFF?" ), getParagraph( 2 )->getString()); CPPUNIT_ASSERT_EQUAL( OUString( "" ), getRun( getParagraph( 2 ), 1 )->getString()); @@ -897,23 +897,39 @@ DECLARE_OOXMLEXPORT_TEST(testTdf104797, "tdf104797.docx") CPPUNIT_ASSERT_EQUAL(OUString("Insert"),getProperty<OUString>(getRun(getParagraph(2), 3), "RedlineType")); CPPUNIT_ASSERT_EQUAL(true,getProperty<bool>(getRun(getParagraph(2), 3), "IsStart")); + CPPUNIT_ASSERT_EQUAL( OUString( " " ), getRun( getParagraph( 2 ), 4 )->getString()); + CPPUNIT_ASSERT_EQUAL( OUString( "" ), getRun( getParagraph( 2 ), 5 )->getString()); + CPPUNIT_ASSERT(hasProperty(getRun(getParagraph(2), 6), "RedlineType")); + CPPUNIT_ASSERT_EQUAL(OUString("Insert"),getProperty<OUString>(getRun(getParagraph(2), 6), "RedlineType")); + CPPUNIT_ASSERT_EQUAL( OUString( "" ), getRun( getParagraph( 2 ), 7 )->getString()); + CPPUNIT_ASSERT(hasProperty(getRun(getParagraph(2), 7), "RedlineType")); + CPPUNIT_ASSERT_EQUAL(true,getProperty<bool>(getRun(getParagraph(2), 7), "IsStart")); + CPPUNIT_ASSERT_EQUAL( OUString( "Will this sentence be duplicated" ), getRun( getParagraph( 2 ), 8 )->getString()); + CPPUNIT_ASSERT_EQUAL( OUString( " ADDED STUFF" ), getRun( getParagraph( 2 ), 11 )->getString()); + CPPUNIT_ASSERT_EQUAL( OUString( "?" ), getRun( getParagraph( 2 ), 14 )->getString()); +} + +DECLARE_OOXMLEXPORT_TEST(testTdf145720, "tdf104797.docx") +{ + // check moveFromRangeStart/End and moveToRangeStart/End (to keep tracked text moving) + xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); if (mbExported) { - // TODO fix export of moved text - CPPUNIT_ASSERT_EQUAL( OUString( " Will this sentence be duplicated ADDED STUFF?" ), getRun( getParagraph( 2 ), 4 )->getString()); - } - else - { - CPPUNIT_ASSERT_EQUAL( OUString( " " ), getRun( getParagraph( 2 ), 4 )->getString()); - CPPUNIT_ASSERT_EQUAL( OUString( "" ), getRun( getParagraph( 2 ), 5 )->getString()); - CPPUNIT_ASSERT(hasProperty(getRun(getParagraph(2), 5), "RedlineType")); - CPPUNIT_ASSERT_EQUAL(OUString("Insert"),getProperty<OUString>(getRun(getParagraph(2), 5), "RedlineType")); - CPPUNIT_ASSERT_EQUAL( OUString( "" ), getRun( getParagraph( 2 ), 6 )->getString()); - CPPUNIT_ASSERT(hasProperty(getRun(getParagraph(2), 6), "RedlineType")); - CPPUNIT_ASSERT_EQUAL(true,getProperty<bool>(getRun(getParagraph(2), 6), "IsStart")); - CPPUNIT_ASSERT_EQUAL( OUString( "Will this sentence be duplicated" ), getRun( getParagraph( 2 ), 7 )->getString()); - CPPUNIT_ASSERT_EQUAL( OUString( " ADDED STUFF" ), getRun( getParagraph( 2 ), 10 )->getString()); - CPPUNIT_ASSERT_EQUAL( OUString( "?" ), getRun( getParagraph( 2 ), 13 )->getString()); + // These were 0 (missing move*FromRange* elements) + assertXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:moveFrom/w:moveFromRangeStart", 1); + assertXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:moveFromRangeEnd", 1); + assertXPath(pXmlDoc, "/w:document/w:body/w:p[2]/w:moveTo/w:moveToRangeStart", 1); + assertXPath(pXmlDoc, "/w:document/w:body/w:p[2]/w:moveToRangeEnd", 1); + + // paired names + assertXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:moveFrom/w:moveFromRangeStart", "name", "move471382752"); + assertXPath(pXmlDoc, "/w:document/w:body/w:p[2]/w:moveTo/w:moveToRangeStart", "name", "move471382752"); + + // mandatory authors and dates + assertXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:moveFrom/w:moveFromRangeStart", "author", u"Tekijä"); + assertXPath(pXmlDoc, "/w:document/w:body/w:p[2]/w:moveTo/w:moveToRangeStart", "author", u"Tekijä"); + assertXPath(pXmlDoc, "/w:document/w:body/w:p[1]/w:moveFrom/w:moveFromRangeStart", "date", "0-00-00T00:00:00Z"); + assertXPath(pXmlDoc, "/w:document/w:body/w:p[2]/w:moveTo/w:moveToRangeStart", "date", "0-00-00T00:00:00Z"); } } @@ -1113,8 +1129,21 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf132271) loadAndSave("tdf132271.docx"); xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); // import change tracking in floating tables - assertXPath(pXmlDoc, "//w:del", 2); - assertXPath(pXmlDoc, "//w:ins", 2); + if (!mbExported) + { + assertXPath(pXmlDoc, "//w:del", 2); + assertXPath(pXmlDoc, "//w:ins", 2); + assertXPath(pXmlDoc, "//w:moveFrom", 0); + assertXPath(pXmlDoc, "//w:moveTo", 0); + } + else + { + assertXPath(pXmlDoc, "//w:del", 1); + assertXPath(pXmlDoc, "//w:ins", 1); + // tracked text moving recognized during the import + assertXPath(pXmlDoc, "//w:moveFrom", 1); + assertXPath(pXmlDoc, "//w:moveTo", 1); + } } CPPUNIT_TEST_FIXTURE(Test, testTdf136667) @@ -1122,8 +1151,21 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf136667) loadAndSave("tdf136667.docx"); xmlDocUniquePtr pXmlDoc = parseExport("word/document.xml"); // import change tracking in floating tables - assertXPath(pXmlDoc, "//w:del", 2); - assertXPath(pXmlDoc, "//w:ins", 4); + if (!mbExported) + { + assertXPath(pXmlDoc, "//w:del", 2); + assertXPath(pXmlDoc, "//w:ins", 4); + assertXPath(pXmlDoc, "//w:moveFrom", 0); + assertXPath(pXmlDoc, "//w:moveTo", 0); + } + else + { + assertXPath(pXmlDoc, "//w:del", 1); + assertXPath(pXmlDoc, "//w:ins", 3); + // tracked text moving recognized during the import + assertXPath(pXmlDoc, "//w:moveFrom", 1); + assertXPath(pXmlDoc, "//w:moveTo", 1); + } } CPPUNIT_TEST_FIXTURE(Test, testTdf136850) diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx index 86473c120cbb..6600928f37e9 100644 --- a/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx +++ b/sw/qa/extras/ooxmlexport/ooxmlexport13.cxx @@ -763,24 +763,13 @@ DECLARE_OOXMLEXPORT_TEST(testTdf123460, "tdf123460.docx") // check paragraph mark deletion at terminating moveFrom CPPUNIT_ASSERT_EQUAL(true,getParagraph( 2 )->getString().startsWith("Nunc")); CPPUNIT_ASSERT_EQUAL( OUString( "" ), getRun( getParagraph( 2 ), 1 )->getString()); - CPPUNIT_ASSERT(hasProperty(getRun(getParagraph(2), 1), "RedlineType")); - CPPUNIT_ASSERT_EQUAL(OUString("Delete"),getProperty<OUString>(getRun(getParagraph(2), 1), "RedlineType")); - CPPUNIT_ASSERT_EQUAL(true, getRun( getParagraph( 2 ), 2 )->getString().endsWith("tellus.")); - CPPUNIT_ASSERT_EQUAL( OUString( "" ), getRun( getParagraph( 2 ), 3 )->getString()); - if (mbExported) - { - // TODO fix export of moved text - bool bCaught = false; - try - { - getRun( getParagraph( 2 ), 4 ); - } - catch (container::NoSuchElementException&) - { - bCaught = true; - } - CPPUNIT_ASSERT_EQUAL(true, bCaught); - } + CPPUNIT_ASSERT(hasProperty(getRun(getParagraph(2), 2), "RedlineType")); + CPPUNIT_ASSERT_EQUAL(OUString("Delete"),getProperty<OUString>(getRun(getParagraph(2), 2), "RedlineType")); + CPPUNIT_ASSERT_EQUAL(true, getRun( getParagraph( 2 ), 3 )->getString().endsWith("tellus.")); + // deleted paragraph mark at the end of the second paragraph + CPPUNIT_ASSERT(hasProperty(getRun(getParagraph(2), 5), "RedlineType")); + CPPUNIT_ASSERT_EQUAL(OUString("Delete"),getProperty<OUString>(getRun(getParagraph(2), 5), "RedlineType")); + CPPUNIT_ASSERT_EQUAL( OUString( "" ), getRun( getParagraph( 2 ), 6 )->getString()); } //tdf#125298: fix charlimit restrictions in bookmarknames and field references if they contain non-ascii characters diff --git a/sw/source/filter/ww8/docxattributeoutput.cxx b/sw/source/filter/ww8/docxattributeoutput.cxx index 51a8ac155cad..3ec3d75732c9 100644 --- a/sw/source/filter/ww8/docxattributeoutput.cxx +++ b/sw/source/filter/ww8/docxattributeoutput.cxx @@ -1720,7 +1720,7 @@ void DocxAttributeOutput::EndRun(const SwTextNode* pNode, sal_Int32 nPos, bool / // XML_r node should be surrounded with bookmark-begin and bookmark-end nodes if it has bookmarks. // The same is applied for permission ranges. // But due to unit test "testFdo85542" let's output bookmark-begin with bookmark-end. - DoWriteBookmarksStart(m_rBookmarksStart); + DoWriteBookmarksStart(m_rBookmarksStart, m_pMoveRedlineData); DoWriteBookmarksEnd(m_rBookmarksEnd); DoWritePermissionsStart(); DoWriteAnnotationMarks(); @@ -1905,6 +1905,29 @@ void DocxAttributeOutput::DoWriteBookmarkTagEnd(sal_Int32 const nId) FSNS(XML_w, XML_id), OString::number(nId)); } +void DocxAttributeOutput::DoWriteMoveRangeTagStart(const OString & bookmarkName, + bool bFrom, const SwRedlineData* pRedlineData) +{ + const OUString &rAuthor( SW_MOD()->GetRedlineAuthor( pRedlineData->GetAuthor() ) ); + OString aDate( DateTimeToOString( pRedlineData->GetTimeStamp() ) ); + + m_pSerializer->singleElementNS(XML_w, bFrom + ? XML_moveFromRangeStart + : XML_moveToRangeStart, + FSNS(XML_w, XML_id), OString::number(m_nNextBookmarkId), + FSNS(XML_w, XML_author ), OUStringToOString(rAuthor, RTL_TEXTENCODING_UTF8), + FSNS(XML_w, XML_date ), aDate, + FSNS(XML_w, XML_name), bookmarkName); +} + +void DocxAttributeOutput::DoWriteMoveRangeTagEnd(sal_Int32 const nId, bool bFrom) +{ + m_pSerializer->singleElementNS(XML_w, bFrom + ? XML_moveFromRangeEnd + : XML_moveToRangeEnd, + FSNS(XML_w, XML_id), OString::number(nId)); +} + void DocxAttributeOutput::DoWriteBookmarkStartIfExist(sal_Int32 nRunPos) { auto aRange = m_aBookmarksOfParagraphStart.equal_range(nRunPos); @@ -1934,15 +1957,29 @@ void DocxAttributeOutput::DoWriteBookmarkEndIfExist(sal_Int32 nRunPos) } /// Write the start bookmarks -void DocxAttributeOutput::DoWriteBookmarksStart(std::vector<OUString>& rStarts) +void DocxAttributeOutput::DoWriteBookmarksStart(std::vector<OUString>& rStarts, const SwRedlineData* pRedlineData) { for (const OUString & bookmarkName : rStarts) { - // Output the bookmark - DoWriteBookmarkTagStart(bookmarkName); + // Output the bookmark (including MoveBookmark of the tracked moving) + bool bMove = false; + bool bFrom = false; + OString sBookmarkName = OUStringToOString( + BookmarkToWord(bookmarkName, &bMove, &bFrom), RTL_TEXTENCODING_UTF8); + if ( bMove ) + { + // TODO: redline data of MoveBookmark is restored from the first redline of the bookmark + // range. But a later deletion within a tracked moving is still imported as plain + // deletion, so check IsMoved() and skip the export of the tracked moving to avoid + // export with bad author or date + if ( pRedlineData && pRedlineData->IsMoved() ) + DoWriteMoveRangeTagStart(sBookmarkName, bFrom, pRedlineData); + } + else + DoWriteBookmarkTagStart(bookmarkName); m_rOpenedBookmarksIds[bookmarkName] = m_nNextBookmarkId; - m_sLastOpenedBookmark = OUStringToOString(BookmarkToWord(bookmarkName), RTL_TEXTENCODING_UTF8); + m_sLastOpenedBookmark = sBookmarkName; m_nNextBookmarkId++; } rStarts.clear(); @@ -1955,10 +1992,17 @@ void DocxAttributeOutput::DoWriteBookmarksEnd(std::vector<OUString>& rEnds) { // Get the id of the bookmark auto pPos = m_rOpenedBookmarksIds.find(bookmarkName); + if (pPos != m_rOpenedBookmarksIds.end()) { - // Output the bookmark - DoWriteBookmarkTagEnd(pPos->second); + bool bMove = false; + bool bFrom = false; + BookmarkToWord(bookmarkName, &bMove, &bFrom); + // Output the bookmark (including MoveBookmark of the tracked moving) + if ( bMove ) + DoWriteMoveRangeTagEnd(pPos->second, bFrom); + else + DoWriteBookmarkTagEnd(pPos->second); m_rOpenedBookmarksIds.erase(bookmarkName); } @@ -3109,10 +3153,13 @@ void DocxAttributeOutput::RunText( const OUString& rText, rtl_TextEncoding /*eCh const sal_Unicode *pBegin = rText.getStr(); const sal_Unicode *pEnd = pBegin + rText.getLength(); - // the text run is usually XML_t, with the exception of the deleted text + // the text run is usually XML_t, with the exception of the deleted (and not moved) text sal_Int32 nTextToken = XML_t; - if ( m_pRedlineData && m_pRedlineData->GetType() == RedlineType::Delete ) + if ( m_pRedlineData && !m_pRedlineData->IsMoved() && + m_pRedlineData->GetType() == RedlineType::Delete ) + { nTextToken = XML_delText; + } sal_Unicode prevUnicode = *pBegin; @@ -3503,17 +3550,18 @@ void DocxAttributeOutput::StartRedline( const SwRedlineData * pRedlineData ) ? DateTime(Date( 1, 1, 1970 )) // Epoch time : pRedlineData->GetTimeStamp() ) ); + bool bMoved = pRedlineData->IsMoved(); switch ( pRedlineData->GetType() ) { case RedlineType::Insert: - m_pSerializer->startElementNS( XML_w, XML_ins, + m_pSerializer->startElementNS( XML_w, bMoved ? XML_moveTo : XML_ins, FSNS( XML_w, XML_id ), aId, FSNS( XML_w, XML_author ), aAuthor, FSNS( XML_w, XML_date ), aDate ); break; case RedlineType::Delete: - m_pSerializer->startElementNS( XML_w, XML_del, + m_pSerializer->startElementNS( XML_w, bMoved ? XML_moveFrom : XML_del, FSNS( XML_w, XML_id ), aId, FSNS( XML_w, XML_author ), aAuthor, FSNS( XML_w, XML_date ), aDate ); @@ -3532,14 +3580,15 @@ void DocxAttributeOutput::EndRedline( const SwRedlineData * pRedlineData ) if ( !pRedlineData || m_bWritingField ) return; + bool bMoved = pRedlineData->IsMoved(); switch ( pRedlineData->GetType() ) { case RedlineType::Insert: - m_pSerializer->endElementNS( XML_w, XML_ins ); + m_pSerializer->endElementNS( XML_w, bMoved ? XML_moveTo : XML_ins ); break; case RedlineType::Delete: - m_pSerializer->endElementNS( XML_w, XML_del ); + m_pSerializer->endElementNS( XML_w, bMoved ? XML_moveFrom : XML_del ); break; case RedlineType::Format: @@ -8493,7 +8542,7 @@ void DocxAttributeOutput::WriteFormData_Impl( const ::sw::mark::IFieldmark& rFie m_Fields.begin()->pFieldmark = &rFieldmark; } -void DocxAttributeOutput::WriteBookmarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds ) +void DocxAttributeOutput::WriteBookmarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds, const SwRedlineData* pRedlineData ) { for ( const OUString & name : rStarts ) { @@ -8505,6 +8554,7 @@ void DocxAttributeOutput::WriteBookmarks_Impl( std::vector< OUString >& rStarts, else { m_rBookmarksStart.push_back(name); + m_pMoveRedlineData = const_cast<SwRedlineData*>(pRedlineData); } } rStarts.clear(); diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx index 3897073ffe1f..f05332612e00 100644 --- a/sw/source/filter/ww8/docxattributeoutput.hxx +++ b/sw/source/filter/ww8/docxattributeoutput.hxx @@ -28,6 +28,7 @@ #include <IMark.hxx> #include "docxexport.hxx" #include <wrtswtbl.hxx> +#include <redline.hxx> #include <editeng/boxitem.hxx> #include <sax/fshelper.hxx> @@ -393,7 +394,7 @@ public: OUString const* pBookmarkName = nullptr); void WriteFormData_Impl( const ::sw::mark::IFieldmark& rFieldmark ); - void WriteBookmarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds ); + void WriteBookmarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds, const SwRedlineData* pRedlineData = nullptr ); void WriteFinalBookmarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds ); void WriteAnnotationMarks_Impl( std::vector< OUString >& rStarts, std::vector< OUString >& rEnds ); void PushRelIdCache(); @@ -729,7 +730,10 @@ private: void DoWriteBookmarkTagStart(const OUString & bookmarkName); void DoWriteBookmarkTagEnd(sal_Int32 nId); - void DoWriteBookmarksStart(std::vector<OUString>& rStarts); + void DoWriteMoveRangeTagStart(const OString & bookmarkName, + bool bFrom, const SwRedlineData* pRedlineData); + void DoWriteMoveRangeTagEnd(sal_Int32 nId, bool bFrom); + void DoWriteBookmarksStart(std::vector<OUString>& rStarts, const SwRedlineData* pRedlineData = nullptr); void DoWriteBookmarksEnd(std::vector<OUString>& rEnds); void DoWriteBookmarkStartIfExist(sal_Int32 nRunPos); void DoWriteBookmarkEndIfExist(sal_Int32 nRunPos); @@ -829,6 +833,7 @@ private: /// Bookmarks to output std::vector<OUString> m_rBookmarksStart; std::vector<OUString> m_rBookmarksEnd; + SwRedlineData* m_pMoveRedlineData; /// Bookmarks to output at the end std::vector<OUString> m_rFinalBookmarksStart; diff --git a/sw/source/filter/ww8/docxexport.cxx b/sw/source/filter/ww8/docxexport.cxx index 1afc002f6e75..0f1ca6ddb969 100644 --- a/sw/source/filter/ww8/docxexport.cxx +++ b/sw/source/filter/ww8/docxexport.cxx @@ -66,6 +66,7 @@ #include <ftninfo.hxx> #include <pagedesc.hxx> #include <poolfmt.hxx> +#include <redline.hxx> #include <swdbdata.hxx> #include <editeng/unoprnms.hxx> @@ -145,7 +146,7 @@ bool DocxExport::CollapseScriptsforWordOk( sal_uInt16 nScript, sal_uInt16 nWhich return true; } -void DocxExport::AppendBookmarks( const SwTextNode& rNode, sal_Int32 nCurrentPos, sal_Int32 nLen ) +void DocxExport::AppendBookmarks( const SwTextNode& rNode, sal_Int32 nCurrentPos, sal_Int32 nLen, const SwRedlineData* pRedlineData ) { std::vector< OUString > aStarts; std::vector< OUString > aEnds; @@ -172,7 +173,7 @@ void DocxExport::AppendBookmarks( const SwTextNode& rNode, sal_Int32 nCurrentPos if ( nCurrentPos == nEnd ) m_pAttrOutput->WriteFinalBookmarks_Impl( aStarts, aEnds ); else - m_pAttrOutput->WriteBookmarks_Impl( aStarts, aEnds ); + m_pAttrOutput->WriteBookmarks_Impl( aStarts, aEnds, pRedlineData ); } void DocxExport::AppendBookmark( const OUString& rName ) diff --git a/sw/source/filter/ww8/docxexport.hxx b/sw/source/filter/ww8/docxexport.hxx index 91ca7c82d154..cdd18c9e4510 100644 --- a/sw/source/filter/ww8/docxexport.hxx +++ b/sw/source/filter/ww8/docxexport.hxx @@ -145,7 +145,7 @@ public: /// Guess the script (asian/western). virtual bool CollapseScriptsforWordOk( sal_uInt16 nScript, sal_uInt16 nWhich ) override; - virtual void AppendBookmarks( const SwTextNode& rNode, sal_Int32 nCurrentPos, sal_Int32 nLen ) override; + virtual void AppendBookmarks( const SwTextNode& rNode, sal_Int32 nCurrentPos, sal_Int32 nLen, const SwRedlineData* pRedlineData = nullptr ) override; virtual void AppendBookmark( const OUString& rName ) override; diff --git a/sw/source/filter/ww8/rtfexport.cxx b/sw/source/filter/ww8/rtfexport.cxx index 2be381a3dfb2..68fd898ab1ff 100644 --- a/sw/source/filter/ww8/rtfexport.cxx +++ b/sw/source/filter/ww8/rtfexport.cxx @@ -118,7 +118,8 @@ bool RtfExport::CollapseScriptsforWordOk(sal_uInt16 nScript, sal_uInt16 nWhich) return true; } -void RtfExport::AppendBookmarks(const SwTextNode& rNode, sal_Int32 nCurrentPos, sal_Int32 nLen) +void RtfExport::AppendBookmarks(const SwTextNode& rNode, sal_Int32 nCurrentPos, sal_Int32 nLen, + const SwRedlineData* /*pRedlineData*/) { std::vector<OUString> aStarts; std::vector<OUString> aEnds; diff --git a/sw/source/filter/ww8/rtfexport.hxx b/sw/source/filter/ww8/rtfexport.hxx index 48b3379d4aa3..36a735eb783a 100644 --- a/sw/source/filter/ww8/rtfexport.hxx +++ b/sw/source/filter/ww8/rtfexport.hxx @@ -67,7 +67,8 @@ public: /// Guess the script (asian/western). bool CollapseScriptsforWordOk(sal_uInt16 nScript, sal_uInt16 nWhich) override; - void AppendBookmarks(const SwTextNode& rNode, sal_Int32 nCurrentPos, sal_Int32 nLen) override; + void AppendBookmarks(const SwTextNode& rNode, sal_Int32 nCurrentPos, sal_Int32 nLen, + const SwRedlineData* pSwRedlineData = nullptr) override; void AppendBookmark(const OUString& rName) override; diff --git a/sw/source/filter/ww8/wrtw8nds.cxx b/sw/source/filter/ww8/wrtw8nds.cxx index f33a52708a57..4f3ca7f94305 100644 --- a/sw/source/filter/ww8/wrtw8nds.cxx +++ b/sw/source/filter/ww8/wrtw8nds.cxx @@ -1208,10 +1208,28 @@ bool WW8AttributeOutput::EndURL(bool const) return true; } -OUString BookmarkToWord(const OUString &rBookmark) +OUString BookmarkToWord(const OUString &rBookmark, bool* pIsMove, bool* pIsFrom) { + sal_Int32 nTrim = 0; // position to remove "__RefMoveRange" from bookmark names + if ( pIsMove ) + { + static constexpr OUStringLiteral MoveFrom_Bookmark_NamePrefix = u"__RefMoveFrom__"; + static constexpr OUStringLiteral MoveTo_Bookmark_NamePrefix = u"__RefMoveTo__"; + if ( rBookmark.startsWith(MoveFrom_Bookmark_NamePrefix) ) + { + *pIsMove = true; + *pIsFrom = true; + nTrim = MoveFrom_Bookmark_NamePrefix.getLength(); + } + else if ( rBookmark.startsWith(MoveTo_Bookmark_NamePrefix) ) + { + *pIsMove = true; + *pIsFrom = false; + nTrim = MoveTo_Bookmark_NamePrefix.getLength(); + } + } OUString sRet(INetURLObject::encode( - rBookmark.replace(' ', '_'), // Spaces are prohibited in bookmark name + rBookmark.copy(nTrim).replace(' ', '_'), // Spaces are prohibited in bookmark name INetURLObject::PART_REL_SEGMENT_EXTRA, INetURLObject::EncodeMechanism::All, RTL_TEXTENCODING_ASCII_US)); // Unicode letters are allowed @@ -2418,7 +2436,7 @@ void MSWordExportBase::OutputTextNode( SwTextNode& rNode ) AttrOutput().SetAnchorIsLinkedToNode( bPostponeWritingText && (FLY_POSTPONED != nStateOfFlyFrame) ); // Append bookmarks in this range after flys, exclusive of final // position of this range - AppendBookmarks( rNode, nCurrentPos, nNextAttr - nCurrentPos ); + AppendBookmarks( rNode, nCurrentPos, nNextAttr - nCurrentPos, pRedlineData ); // Sadly only possible for main or glossary document parts: ECMA-376 Part 1 sect. 11.3.2 if ( m_nTextTyp == TXT_MAINTEXT ) AppendAnnotationMarks(aAttrIter, nCurrentPos, nNextAttr - nCurrentPos); diff --git a/sw/source/filter/ww8/wrtww8.cxx b/sw/source/filter/ww8/wrtww8.cxx index 2c2f40db021e..d179858fd3f3 100644 --- a/sw/source/filter/ww8/wrtww8.cxx +++ b/sw/source/filter/ww8/wrtww8.cxx @@ -1415,7 +1415,7 @@ WW8_CP WW8_WrPct::Fc2Cp( sal_uLong nFc ) const return nFc + m_Pcts.back()->GetStartCp(); } -void WW8Export::AppendBookmarks( const SwTextNode& rNd, sal_Int32 nCurrentPos, sal_Int32 nLen ) +void WW8Export::AppendBookmarks( const SwTextNode& rNd, sal_Int32 nCurrentPos, sal_Int32 nLen, const SwRedlineData* /*pRedlineData*/ ) { std::vector< const ::sw::mark::IMark* > aArr; sal_uInt16 nContent; diff --git a/sw/source/filter/ww8/wrtww8.hxx b/sw/source/filter/ww8/wrtww8.hxx index 1051c8bf5fa4..72e5a8d65e1e 100644 --- a/sw/source/filter/ww8/wrtww8.hxx +++ b/sw/source/filter/ww8/wrtww8.hxx @@ -704,7 +704,7 @@ public: /// has two virtual bool CollapseScriptsforWordOk( sal_uInt16 nScript, sal_uInt16 nWhich ) = 0; - virtual void AppendBookmarks( const SwTextNode& rNd, sal_Int32 nCurrentPos, sal_Int32 nLen ) = 0; + virtual void AppendBookmarks( const SwTextNode& rNd, sal_Int32 nCurrentPos, sal_Int32 nLen, const SwRedlineData* pSwRedline = nullptr ) = 0; virtual void AppendBookmark( const OUString& rName ) = 0; @@ -1074,7 +1074,7 @@ public: const tools::SvRef<SotStorage>& xObjStg, OUString const& rStorageName, SwOLENode* pOLENd); - virtual void AppendBookmarks( const SwTextNode& rNd, sal_Int32 nCurrentPos, sal_Int32 nLen ) override; + virtual void AppendBookmarks( const SwTextNode& rNd, sal_Int32 nCurrentPos, sal_Int32 nLen, const SwRedlineData* pRedlineData = nullptr ) override; virtual void AppendBookmark( const OUString& rName ) override; void AppendBookmarkEndWithCorrection( const OUString& rName ); @@ -1633,7 +1633,7 @@ public: sal_Int16 GetWordFirstLineOffset(const SwNumFormat &rFormat); // A bit of a bag on the side for now OUString FieldString(ww::eField eIndex); -OUString BookmarkToWord(const OUString &rBookmark); +OUString BookmarkToWord(const OUString &rBookmark, bool* pIsMove = nullptr, bool* pIsFrom = nullptr); class WW8SHDLong { diff --git a/writerfilter/source/dmapper/DomainMapper.cxx b/writerfilter/source/dmapper/DomainMapper.cxx index b247e0156b38..2958f004553d 100644 --- a/writerfilter/source/dmapper/DomainMapper.cxx +++ b/writerfilter/source/dmapper/DomainMapper.cxx @@ -2500,10 +2500,12 @@ void DomainMapper::sprmWithProps( Sprm& rSprm, const PropertyMapPtr& rContext ) } break; case NS_ooxml::LN_EG_RangeMarkupElements_moveFromRangeStart: + m_pImpl->SetMoveBookmark(/*bIsFrom=*/true); if (m_pImpl->hasTableManager()) m_pImpl->getTableManager().setMoved( getPropertyName(PROP_TABLE_ROW_DELETE) ); break; case NS_ooxml::LN_EG_RangeMarkupElements_moveToRangeStart: + m_pImpl->SetMoveBookmark(/*bIsFrom=*/false); if (m_pImpl->hasTableManager()) m_pImpl->getTableManager().setMoved( getPropertyName(PROP_TABLE_ROW_INSERT) ); break; diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.cxx b/writerfilter/source/dmapper/DomainMapper_Impl.cxx index 97cb3511506b..befa25aed954 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.cxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.cxx @@ -7051,10 +7051,14 @@ void DomainMapper_Impl::SetBookmarkName( const OUString& rBookmarkName ) } } - aBookmarkIter->second.m_sBookmarkName = rBookmarkName; + aBookmarkIter->second.m_sBookmarkName = m_sCurrentBkmkPrefix + rBookmarkName; + m_sCurrentBkmkPrefix.clear(); } else + { m_sCurrentBkmkName = rBookmarkName; + m_sCurrentBkmkPrefix.clear(); + } } // This method was used as-is for DomainMapper_Impl::startOrEndPermissionRange() implementation. @@ -7101,7 +7105,10 @@ void DomainMapper_Impl::StartOrEndBookmark( const OUString& rId ) // then move the bookmark-End to the earlier paragraph if (IsOutsideAParagraph()) { + // keep bookmark range + uno::Reference< text::XTextRange > xStart = xCursor->getStart(); xCursor->goLeft( 1, false ); + xCursor->gotoRange(xStart, true ); } uno::Reference< container::XNamed > xBkmNamed( xBookmark, uno::UNO_QUERY_THROW ); SAL_WARN_IF(aBookmarkIter->second.m_sBookmarkName.isEmpty(), "writerfilter.dmapper", "anonymous bookmark"); @@ -7143,6 +7150,16 @@ void DomainMapper_Impl::StartOrEndBookmark( const OUString& rId ) } } +void DomainMapper_Impl::SetMoveBookmark( bool bIsFrom ) +{ + static constexpr OUStringLiteral MoveFrom_Bookmark_NamePrefix = u"__RefMoveFrom__"; + static constexpr OUStringLiteral MoveTo_Bookmark_NamePrefix = u"__RefMoveTo__"; + if ( bIsFrom ) + m_sCurrentBkmkPrefix = MoveFrom_Bookmark_NamePrefix; + else + m_sCurrentBkmkPrefix = MoveTo_Bookmark_NamePrefix; +} + void DomainMapper_Impl::setPermissionRangeEd(const OUString& user) { PermMap_t::iterator aPremIter = m_aPermMap.find(m_sCurrentPermId); diff --git a/writerfilter/source/dmapper/DomainMapper_Impl.hxx b/writerfilter/source/dmapper/DomainMapper_Impl.hxx index 7d58fe653716..c41787e09363 100644 --- a/writerfilter/source/dmapper/DomainMapper_Impl.hxx +++ b/writerfilter/source/dmapper/DomainMapper_Impl.hxx @@ -488,6 +488,7 @@ private: BookmarkMap_t m_aBookmarkMap; OUString m_sCurrentBkmkId; OUString m_sCurrentBkmkName; + OUString m_sCurrentBkmkPrefix; PermMap_t m_aPermMap; sal_Int32 m_sCurrentPermId; @@ -913,6 +914,9 @@ public: void SetBookmarkName( const OUString& rBookmarkName ); void StartOrEndBookmark( const OUString& rId ); + OUString m_sBookmarkPrefix; + void SetMoveBookmark( bool IsFrom ); + void setPermissionRangeEd(const OUString& user); void setPermissionRangeEdGrp(const OUString& group); void startOrEndPermissionRange(sal_Int32 permissinId); |