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( )