summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLászló Németh <nemeth@numbertext.org>2021-12-02 17:45:46 +0100
committerLászló Németh <nemeth@numbertext.org>2021-12-03 13:10:57 +0100
commit9e1e88ad5cf2dc0e9b188c60930445652a6c7519 (patch)
tree13a4fc3c4fdb1ee9283d218495e2c53edf6eeb27
parent7563216425c1a43166db083d809073b268f7884f (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.cxx86
-rw-r--r--sw/qa/extras/ooxmlexport/ooxmlexport13.cxx25
-rw-r--r--sw/source/filter/ww8/docxattributeoutput.cxx78
-rw-r--r--sw/source/filter/ww8/docxattributeoutput.hxx9
-rw-r--r--sw/source/filter/ww8/docxexport.cxx5
-rw-r--r--sw/source/filter/ww8/docxexport.hxx2
-rw-r--r--sw/source/filter/ww8/rtfexport.cxx3
-rw-r--r--sw/source/filter/ww8/rtfexport.hxx3
-rw-r--r--sw/source/filter/ww8/wrtw8nds.cxx24
-rw-r--r--sw/source/filter/ww8/wrtww8.cxx2
-rw-r--r--sw/source/filter/ww8/wrtww8.hxx6
-rw-r--r--writerfilter/source/dmapper/DomainMapper.cxx2
-rw-r--r--writerfilter/source/dmapper/DomainMapper_Impl.cxx19
-rw-r--r--writerfilter/source/dmapper/DomainMapper_Impl.hxx4
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);