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); }
