include/svx/ctredlin.hxx                        |    2 +
 svx/source/dialog/ctredlin.cxx                  |   34 ++++++++++++++++++++++++
 sw/qa/filter/xml/data/insert-then-format.odt    |binary
 sw/qa/filter/xml/xml.cxx                        |   25 +++++++++++++++++
 sw/source/filter/xml/XMLRedlineImportHelper.cxx |   34 +++++++++++++++++++++---
 5 files changed, 92 insertions(+), 3 deletions(-)

New commits:
commit 4dffb84822b02b8e53cbe9ad0a2ab9ca5757af2c
Author:     Miklos Vajna <[email protected]>
AuthorDate: Mon May 19 08:30:01 2025 +0200
Commit:     Caolán McNamara <[email protected]>
CommitDate: Mon May 19 09:30:47 2025 +0200

    tdf#166319 sw interdependent redlines: handle ODF import of insert under 
format
    
    Open the bugdoc, the ODT file has an insert with a format on top of the
    middle, but the format is lost in Writer.
    
    It seems what happens is that the XML parser correctly populates the
    RedlineInfo structure, but then XMLRedlineImportHelper::ConvertRedline()
    decides to throw away the underlying insert using an explicit condition
    that was added in commit 52d244dee88b111631680d8cd4c8b922f9640c15 (-
    added: redline import, 2001-01-10).
    
    Fix this by extending it similar to the accept redline's
    CanCombineTypesForAcceptReject() by adding a CanCombineTypesForImport()
    that now accepts insert-then-format, too.
    
    This is similar to what was fixed for DOCX in commit
    ed8257a67d26083c2c6cd60ecac331c9e0df15ed (tdf#166319 sw interdependent
    redlines: handle format on top of insert, 2025-04-29). The ODT export
    was working already as-is.
    
    Change-Id: I2163b0350ca56264ebed5abd54735ef4d91ee2ad
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/185494
    Reviewed-by: Caolán McNamara <[email protected]>
    Tested-by: Jenkins CollaboraOffice <[email protected]>

diff --git a/include/svx/ctredlin.hxx b/include/svx/ctredlin.hxx
index c098efa6a11b..6bc9780b55b0 100644
--- a/include/svx/ctredlin.hxx
+++ b/include/svx/ctredlin.hxx
@@ -63,6 +63,8 @@ enum class RedlineType : sal_uInt16
     Any = USHRT_MAX // special value to indicate any redline type in some 
method calls
 };
 
+SVX_DLLPUBLIC std::ostream& operator<<(std::ostream& rStream, const 
RedlineType& eType);
+
 /// Struct for sorting data.
 class SAL_WARN_UNUSED SVX_DLLPUBLIC RedlinData
 {
diff --git a/svx/source/dialog/ctredlin.cxx b/svx/source/dialog/ctredlin.cxx
index cdcdeed1ff2a..fb011732aecb 100644
--- a/svx/source/dialog/ctredlin.cxx
+++ b/svx/source/dialog/ctredlin.cxx
@@ -30,6 +30,7 @@
 #include <helpids.h>
 
 #include <svx/ctredlin.hxx>
+#include <o3tl/unreachable.hxx>
 
 #define WRITER_DATE     2
 #define CALC_DATE       3
@@ -1009,4 +1010,37 @@ IMPL_LINK(SvxAcceptChgCtr, DeactivatePageHdl, const 
OUString&, rPage, bool)
     return true;
 }
 
+std::ostream& operator<<(std::ostream& rStream, const RedlineType& eType)
+{
+    switch (eType)
+    {
+        case RedlineType::Insert:
+            return rStream << "RedlineType::Insert";
+        case RedlineType::Delete:
+            return rStream << "RedlineType::Delete";
+        case RedlineType::Format:
+            return rStream << "RedlineType::Format";
+        case RedlineType::Table:
+            return rStream << "RedlineType::Table";
+        case RedlineType::FmtColl:
+            return rStream << "RedlineType::FmtColl";
+        case RedlineType::ParagraphFormat:
+            return rStream << "RedlineType::ParagraphFormat";
+        case RedlineType::TableRowInsert:
+            return rStream << "RedlineType::TableRowInsert";
+        case RedlineType::TableRowDelete:
+            return rStream << "RedlineType::TableRowDelete";
+        case RedlineType::TableCellInsert:
+            return rStream << "RedlineType::TableCellInsert";
+        case RedlineType::TableCellDelete:
+            return rStream << "RedlineType::TableCellDelete";
+        case RedlineType::None:
+            return rStream << "RedlineType::None";
+        case RedlineType::Any:
+            return rStream << "RedlineType::Any";
+        default:
+            O3TL_UNREACHABLE;
+    }
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/qa/filter/xml/data/insert-then-format.odt 
b/sw/qa/filter/xml/data/insert-then-format.odt
new file mode 100644
index 000000000000..f6ebb21bcda9
Binary files /dev/null and b/sw/qa/filter/xml/data/insert-then-format.odt differ
diff --git a/sw/qa/filter/xml/xml.cxx b/sw/qa/filter/xml/xml.cxx
index 9a0d9c1ff59b..472f01bbc410 100644
--- a/sw/qa/filter/xml/xml.cxx
+++ b/sw/qa/filter/xml/xml.cxx
@@ -12,6 +12,9 @@
 #include <frameformats.hxx>
 #include <frmatr.hxx>
 #include <swtable.hxx>
+#include <docsh.hxx>
+#include <IDocumentRedlineAccess.hxx>
+#include <redline.hxx>
 
 namespace
 {
@@ -74,6 +77,28 @@ CPPUNIT_TEST_FIXTURE(Test, testRedlineRecordFlatExport)
         pDoc, "/office:document/office:body/office:text/text:tracked-changes", 
"track-changes");
     CPPUNIT_ASSERT_EQUAL(u"true"_ustr, aValue);
 }
+
+CPPUNIT_TEST_FIXTURE(Test, testInsertThenFormatOdtImport)
+{
+    // Given a document with <ins>A<format>B</format>C</ins> style redlines:
+    // When importing that document:
+    createSwDoc("insert-then-format.odt");
+
+    // Then make sure that both the insert and the format on top of it is in 
the model:
+    SwDoc* pDoc = getSwDocShell()->GetDoc();
+    IDocumentRedlineAccess& rIDRA = pDoc->getIDocumentRedlineAccess();
+    SwRedlineTable& rRedlines = rIDRA.GetRedlineTable();
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), rRedlines.size());
+    CPPUNIT_ASSERT_EQUAL(RedlineType::Insert, rRedlines[0]->GetType());
+    const SwRedlineData& rRedlineData1 = rRedlines[1]->GetRedlineData(0);
+    CPPUNIT_ASSERT_EQUAL(RedlineType::Format, rRedlineData1.GetType());
+    // Without the accompanying fix in place, this test would have failed, 
i.e. the insert under the
+    // format redline was lost.
+    CPPUNIT_ASSERT(rRedlineData1.Next());
+    const SwRedlineData& rInnerRedlineData = *rRedlineData1.Next();
+    CPPUNIT_ASSERT_EQUAL(RedlineType::Insert, rInnerRedlineData.GetType());
+    CPPUNIT_ASSERT_EQUAL(RedlineType::Insert, rRedlines[2]->GetType());
+}
 }
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/filter/xml/XMLRedlineImportHelper.cxx 
b/sw/source/filter/xml/XMLRedlineImportHelper.cxx
index e8ed0c459d86..7850e61ce881 100644
--- a/sw/source/filter/xml/XMLRedlineImportHelper.cxx
+++ b/sw/source/filter/xml/XMLRedlineImportHelper.cxx
@@ -783,6 +783,36 @@ void 
XMLRedlineImportHelper::InsertIntoDocument(RedlineInfo* pRedlineInfo)
     }
 }
 
+namespace
+{
+/// Similar to CanCombineTypesForAcceptReject(), but for import purposes.
+bool CanCombineTypesForImport(RedlineInfo* pRedlineInfo)
+{
+    if (!pRedlineInfo->pNextRedline)
+    {
+        return false;
+    }
+
+    RedlineType eInnerType = pRedlineInfo->pNextRedline->eType;
+    if (eInnerType != RedlineType::Insert)
+    {
+        return false;
+    }
+
+    RedlineType eOuterType = pRedlineInfo->eType;
+    switch (eOuterType)
+    {
+        case RedlineType::Delete:
+        case RedlineType::Format:
+            break;
+        default:
+            return false;
+    }
+
+    return true;
+}
+}
+
 SwRedlineData* XMLRedlineImportHelper::ConvertRedline(
     RedlineInfo* pRedlineInfo,
     SwDoc* pDoc)
@@ -807,9 +837,7 @@ SwRedlineData* XMLRedlineImportHelper::ConvertRedline(
     // 3) recursively convert next redline
     //    ( check presence and sanity of hierarchical redline info )
     SwRedlineData* pNext = nullptr;
-    if ( (nullptr != pRedlineInfo->pNextRedline) &&
-         (RedlineType::Delete == pRedlineInfo->eType) &&
-         (RedlineType::Insert == pRedlineInfo->pNextRedline->eType) )
+    if (CanCombineTypesForImport(pRedlineInfo))
     {
         pNext = ConvertRedline(pRedlineInfo->pNextRedline, pDoc);
     }

Reply via email to