sw/qa/filter/xml/data/delete-then-format.odt    |binary
 sw/qa/filter/xml/xml.cxx                        |   24 ++++++++++++++++++++
 sw/source/filter/xml/XMLRedlineImportHelper.cxx |   28 ++++++++++++++++++++++--
 3 files changed, 50 insertions(+), 2 deletions(-)

New commits:
commit 277ec361442413cfc50e73ab134e31f067d9cfb3
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Wed May 21 09:16:38 2025 +0200
Commit:     Caolán McNamara <caolan.mcnam...@collabora.com>
CommitDate: Wed May 21 09:57:00 2025 +0200

    tdf#166319 sw interdependent redlines: handle ODF import of delete under 
format
    
    The bugdoc has <del>AA<format>BB</format>CC</del> in it, ODT import
    created a model that contains <del>AACC</del> instead, i.e. the
    format-on-delete part was lost.
    
    The first problem was that XMLRedlineImportHelper::ConvertRedline()
    rejected the format-on-delete, fixing the used
    CanCombineTypesForImport() resulted in a format-on-delete redline that
    was in the document, but the order was wrong: BBAACC (matching the order
    in the file, since deletions are listed separately, before the body
    text).
    
    The second problem was that XMLRedlineImportHelper::InsertIntoDocument()
    didn't consider format-on-delete when it looked for deletions: fixing
    that results in the wanted AABBCC string in the document model.
    
    With this, the ODF import/export for ins-then-del, ins-then-fmt &
    del-then-fmt should work.
    
    Change-Id: I7bcdd4d7452d9bca0ff31ce99cbc29795eb63fa5
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/185588
    Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com>
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>

diff --git a/sw/qa/filter/xml/data/delete-then-format.odt 
b/sw/qa/filter/xml/data/delete-then-format.odt
new file mode 100644
index 000000000000..70d531912b58
Binary files /dev/null and b/sw/qa/filter/xml/data/delete-then-format.odt differ
diff --git a/sw/qa/filter/xml/xml.cxx b/sw/qa/filter/xml/xml.cxx
index 472f01bbc410..de6d19aafd47 100644
--- a/sw/qa/filter/xml/xml.cxx
+++ b/sw/qa/filter/xml/xml.cxx
@@ -99,6 +99,30 @@ CPPUNIT_TEST_FIXTURE(Test, testInsertThenFormatOdtImport)
     CPPUNIT_ASSERT_EQUAL(RedlineType::Insert, rInnerRedlineData.GetType());
     CPPUNIT_ASSERT_EQUAL(RedlineType::Insert, rRedlines[2]->GetType());
 }
+
+CPPUNIT_TEST_FIXTURE(Test, testDeleteThenFormatOdtImport)
+{
+    // Given a document with <del>A<format>B</format>C</del> style redlines:
+    // When importing that document:
+    createSwDoc("delete-then-format.odt");
+
+    // Then make sure that both the delete and the format on top of it is in 
the model:
+    SwDoc* pDoc = getSwDocShell()->GetDoc();
+    IDocumentRedlineAccess& rIDRA = pDoc->getIDocumentRedlineAccess();
+    SwRedlineTable& rRedlines = rIDRA.GetRedlineTable();
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 3
+    // - Actual  : 2
+    // i.e. there was an empty format redline at doc start and a delete 
redline for "AC".
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), rRedlines.size());
+    CPPUNIT_ASSERT_EQUAL(RedlineType::Delete, rRedlines[0]->GetType());
+    const SwRedlineData& rRedlineData1 = rRedlines[1]->GetRedlineData(0);
+    CPPUNIT_ASSERT_EQUAL(RedlineType::Format, rRedlineData1.GetType());
+    CPPUNIT_ASSERT(rRedlineData1.Next());
+    const SwRedlineData& rInnerRedlineData = *rRedlineData1.Next();
+    CPPUNIT_ASSERT_EQUAL(RedlineType::Delete, rInnerRedlineData.GetType());
+    CPPUNIT_ASSERT_EQUAL(RedlineType::Delete, rRedlines[2]->GetType());
+}
 }
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/filter/xml/XMLRedlineImportHelper.cxx 
b/sw/source/filter/xml/XMLRedlineImportHelper.cxx
index 7850e61ce881..5eac6501fe25 100644
--- a/sw/source/filter/xml/XMLRedlineImportHelper.cxx
+++ b/sw/source/filter/xml/XMLRedlineImportHelper.cxx
@@ -640,6 +640,23 @@ static auto RecursiveContains(SwStartNode const& 
rRedlineSection, SwNode const&
     return false;
 }
 
+namespace
+{
+/// Similar to GetRedlineTypeIgnoringAdditonalFormat(), but for import 
purposes.
+RedlineType GetRedlineTypeIgnoringAdditonalFormatForImport(RedlineInfo& 
rRedlineInfo)
+{
+    RedlineType eType = rRedlineInfo.eType;
+
+    if (eType == RedlineType::Format && rRedlineInfo.pNextRedline
+        && rRedlineInfo.pNextRedline->eType == RedlineType::Delete)
+    {
+        eType = RedlineType::Delete;
+    }
+
+    return eType;
+}
+}
+
 void XMLRedlineImportHelper::InsertIntoDocument(RedlineInfo* pRedlineInfo)
 {
     assert(pRedlineInfo && "need redline info");
@@ -741,7 +758,8 @@ void 
XMLRedlineImportHelper::InsertIntoDocument(RedlineInfo* pRedlineInfo)
         // the already inserted redlines temporarily and inserting them back 
in reverse
         // order after inserting pRedline
         std::vector<const SwRangeRedline*> aSwapRedlines;
-        if ( RedlineType::Delete == pRedlineInfo->eType )
+        // Move both delete and format-on-delete redlines to aSwapRedlines.
+        if (GetRedlineTypeIgnoringAdditonalFormatForImport(*pRedlineInfo) == 
RedlineType::Delete)
         {
             SwRedlineTable::size_type n = 0;
             while ( const SwRangeRedline* pRedline2 =
@@ -793,13 +811,19 @@ bool CanCombineTypesForImport(RedlineInfo* pRedlineInfo)
         return false;
     }
 
+    RedlineType eOuterType = pRedlineInfo->eType;
     RedlineType eInnerType = pRedlineInfo->pNextRedline->eType;
+    if (eInnerType == RedlineType::Delete)
+    {
+        // Delete can only have format on it.
+        return eOuterType == RedlineType::Format;
+    }
+
     if (eInnerType != RedlineType::Insert)
     {
         return false;
     }
 
-    RedlineType eOuterType = pRedlineInfo->eType;
     switch (eOuterType)
     {
         case RedlineType::Delete:

Reply via email to