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>
(cherry picked from commit 9e1e88ad5cf2dc0e9b188c60930445652a6c7519,
commit bbb09ebda08fec0702de0fb50dbe630acf73af2f and
commit 3efde47ec9ee091479c04129696f99dc934c3f64)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/126290
Tested-by: Jenkins
diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx b/sw/qa/extras/ooxmlexport/ooxmlexport11.cxx
index 8cb43ff..5fae2da 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 86473c1..6600928 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 51a8ac1..0aa0336 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();
@@ -10134,6 +10184,7 @@ DocxAttributeOutput::DocxAttributeOutput( DocxExport &rExport, const FSHelperPtr
m_nNextBookmarkId( 0 ),
m_nNextAnnotationMarkId( 0 ),
m_nEmbedFlyLevel(0),
m_pMoveRedlineData(nullptr),
m_pCurrentFrame( nullptr ),
m_bParagraphOpened( false ),
m_bParagraphFrameOpen( false ),
diff --git a/sw/source/filter/ww8/docxattributeoutput.hxx b/sw/source/filter/ww8/docxattributeoutput.hxx
index 3897073..f053326 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 3d5fc3f..e2fc8f5 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 91ca7c8..cdd18c9 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 2be381a..68fd898 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 48b3379..36a735e 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 f33a527..4f3ca7f 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 2c2f40d..d179858 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 1051c8b..72e5a8d 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 b247e01..2958f00 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 97cb351..befa25a 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 7d58fe6..fb89fb7 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,8 @@ public:
void SetBookmarkName( const OUString& rBookmarkName );
void StartOrEndBookmark( const OUString& rId );
void SetMoveBookmark( bool IsFrom );
void setPermissionRangeEd(const OUString& user);
void setPermissionRangeEdGrp(const OUString& group);
void startOrEndPermissionRange(sal_Int32 permissinId);