sw/qa/core/doc/data/ins-then-format.docx             |binary
 sw/qa/core/doc/doc.cxx                               |   25 +++++++++++++++++++
 sw/source/core/doc/DocumentRedlineManager.cxx        |   14 +++++++++-
 sw/source/core/docnode/ndtbl.cxx                     |    8 ++++++
 sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx |    7 ++---
 5 files changed, 49 insertions(+), 5 deletions(-)

New commits:
commit ca7a6a57ad1e8d7aadffbbf3fe28d9aabed31286
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Tue Apr 29 08:50:17 2025 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Tue Apr 29 12:49:01 2025 +0200

    tdf#166319 sw interdependent redlines: handle format on top of insert
    
    Open the bugdoc, the DOCX file has an insert with a format on top of the
    middle, but the format is lost in Writer.
    
    The first problem is that we tried to create an insert on top of format
    (while in DOCX the format is always inside the insert/delete), fix that
    by changing the order of redline creation in
    DomainMapper_Impl::CheckRedline(). The original motivation is no longer
    relevant after commit b3567d7e1f08df7c050d3cec86cbb200af558172
    (tdf#165322 sw: apply formatting for multiple redlines in SwRedlineItr,
    2025-02-25) Writer core to handle the rendering of format on top of
    insert/delete.
    
    The other problem is that in case we try to append a format redline on
    top of insert ("AB" is an insert and the format would affect "B"), then
    we simply deleted the (candidate) format redline -- change this to
    handle this similar to delete on top of insert.
    
    CppunitTest_sw_ooxmlexport8's testN830205 would now create an unsorted
    redline table on file open, so make sure SwDoc::TextToTable() resorts
    the table after splitting the redlines.
    
    Change-Id: Iada13204892ffccfe09bc2c28bf0c563db6e1ab9
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/184752
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/sw/qa/core/doc/data/ins-then-format.docx 
b/sw/qa/core/doc/data/ins-then-format.docx
new file mode 100644
index 000000000000..ce4110546e06
Binary files /dev/null and b/sw/qa/core/doc/data/ins-then-format.docx differ
diff --git a/sw/qa/core/doc/doc.cxx b/sw/qa/core/doc/doc.cxx
index 73df7c6158f3..226e972ec103 100644
--- a/sw/qa/core/doc/doc.cxx
+++ b/sw/qa/core/doc/doc.cxx
@@ -42,6 +42,7 @@
 #include <pagefrm.hxx>
 #include <sortedobjs.hxx>
 #include <itabenum.hxx>
+#include <redline.hxx>
 
 /// Covers sw/source/core/doc/ fixes.
 class SwCoreDocTest : public SwModelTestBase
@@ -739,6 +740,30 @@ CPPUNIT_TEST_FIXTURE(SwCoreDocTest, 
testInsThenDelRejectUndo)
     CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), rRedlines.size());
 }
 
+CPPUNIT_TEST_FIXTURE(SwCoreDocTest, testInsThenFormat)
+{
+    // Given a document with <ins>A<format>B</format>C</ins> style redlines:
+    // When importing that document:
+    createSwDoc("ins-then-format.docx");
+
+    // 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();
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 3
+    // - Actual  : 1
+    // i.e. a single insert redline was created, format redline was lost on 
import.
+    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());
+    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();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/doc/DocumentRedlineManager.cxx 
b/sw/source/core/doc/DocumentRedlineManager.cxx
index 1cd09175208f..736db017b8cd 100644
--- a/sw/source/core/doc/DocumentRedlineManager.cxx
+++ b/sw/source/core/doc/DocumentRedlineManager.cxx
@@ -2242,8 +2242,20 @@ DocumentRedlineManager::AppendRedline(SwRangeRedline* 
pNewRedl, bool const bCall
                     pNewRedl->SetStart( *pREnd, pStart );
                     break;
 
-                case SwComparePosition::Equal:
                 case SwComparePosition::Inside:
+                    if (*pRStt < *pStart && *pREnd == *pEnd)
+                    {
+                        // pRedl start is before pNewRedl start, the ends 
match: then create the
+                        // format on top of insert/delete & reduce the end of 
the original
+                        // insert/delete to avoid an overlap.
+                        pNewRedl->PushData(*pRedl, false);
+                        pRedl->SetEnd(*pStart);
+                        n = 0;
+                        bDec = true;
+                        break;
+                    }
+                    [[fallthrough]];
+                case SwComparePosition::Equal:
                     delete pNewRedl;
                     pNewRedl = nullptr;
 
diff --git a/sw/source/core/docnode/ndtbl.cxx b/sw/source/core/docnode/ndtbl.cxx
index 603eafc170f3..c561c7475190 100644
--- a/sw/source/core/docnode/ndtbl.cxx
+++ b/sw/source/core/docnode/ndtbl.cxx
@@ -1251,6 +1251,14 @@ const SwTable* SwDoc::TextToTable( const std::vector< 
std::vector<SwNodeRange> >
                     (pPrev->GetNode().IsContentNode())
                         ? pPrev->GetNode().GetContentNode()->Len() : 0);
         rIDRA.SplitRedline(pam);
+
+        // Paragraph formatting results in overlapping elements, split of 
redlines then can result
+        // in an unsorted redline table, fix it up.
+        SwRedlineTable& rRedlineTable = rIDRA.GetRedlineTable();
+        if (rRedlineTable.HasOverlappingElements())
+        {
+            rIDRA.GetRedlineTable().Resort();
+        }
     }
 
     // We always use Upper to insert the Table
diff --git a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx 
b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
index fd958abc424b..54bd39cb0f5f 100644
--- a/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
+++ b/sw/source/writerfilter/dmapper/DomainMapper_Impl.cxx
@@ -4371,8 +4371,6 @@ void DomainMapper_Impl::CheckRedline( uno::Reference< 
text::XTextRange > const&
     // is a better representation of how the changes happened. If this will 
ever become a problem, overlapping redlines
     // will need to be merged into one, just like doing the changes in the UI 
does, which will lose some information
     // (and so if that happens, it may be better to fix Writer).
-    // Create the redlines here from lowest (formats) to highest 
(inserts/removals) priority, since the last one is
-    // what Writer presents graphically, so this will show deletes as deleted 
text and not as just formatted text being there.
     bool bUsedRange = m_aRedlines.top().size() > 0 || 
(GetTopContextOfType(CONTEXT_CHARACTER) &&
         GetTopContextOfType(CONTEXT_CHARACTER)->Redlines().size() > 0);
 
@@ -4385,14 +4383,15 @@ void DomainMapper_Impl::CheckRedline( uno::Reference< 
text::XTextRange > const&
         for( const auto& rRedline : avRedLines )
             CreateRedline( xRange, rRedline );
     }
+    for (const auto& rRedline : m_aRedlines.top())
+        CreateRedline(xRange, rRedline);
+    // Create format redlines after insert/delete, so format can be "on top" 
of insert/delete.
     if( GetTopContextOfType(CONTEXT_CHARACTER) )
     {
         std::vector<RedlineParamsPtr>& avRedLines = 
GetTopContextOfType(CONTEXT_CHARACTER)->Redlines();
         for( const auto& rRedline : avRedLines )
             CreateRedline( xRange, rRedline );
     }
-    for (const auto& rRedline : m_aRedlines.top() )
-        CreateRedline( xRange, rRedline );
 }
 
 void DomainMapper_Impl::StartParaMarkerChange( )

Reply via email to