sw/inc/redline.hxx                                  |    3 -
 sw/qa/extras/layout/data/CT-formatted-deletion.docx |binary
 sw/qa/extras/layout/layout2.cxx                     |   19 ++++++++
 sw/qa/extras/ooxmlexport/ooxmlexport12.cxx          |    2 
 sw/source/core/doc/DocumentRedlineManager.cxx       |   30 +++++++++++++
 sw/source/core/doc/docredln.cxx                     |    5 +-
 sw/source/core/text/redlnitr.cxx                    |   46 +++++++++++++++-----
 vcl/source/gdi/mtfxmldump.cxx                       |    1 
 8 files changed, 93 insertions(+), 13 deletions(-)

New commits:
commit c1af17366dfa586549009c8c423aed7abdc1cbcc
Author:     Michael Stahl <michael.st...@allotropia.de>
AuthorDate: Mon Feb 24 16:02:33 2025 +0100
Commit:     Thorsten Behrens <thorsten.behr...@allotropia.de>
CommitDate: Tue Feb 25 17:47:37 2025 +0100

    tdf#165322 sw: GetRedline() binary search doesn't work with overlaps
    
    Change-Id: I6d7a271eb96f55abdd6d53a153b277083868109b
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182117
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>
    Tested-by: Jenkins
    (cherry picked from commit c5f7619d6eebe478332ae4dcb27fde85df6278a9)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182150
    Reviewed-by: Thorsten Behrens <thorsten.behr...@allotropia.de>

diff --git a/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx 
b/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx
index 902d2f5f33eb..17b09796db41 100644
--- a/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx
+++ b/sw/qa/extras/ooxmlexport/ooxmlexport12.cxx
@@ -1608,7 +1608,7 @@ CPPUNIT_TEST_FIXTURE(Test, testTdf125546)
     loadAndSave("tdf125546.docx");
     xmlDocUniquePtr pXmlDoc = parseExport(u"word/document.xml"_ustr);
     // compress redlines (it was 15)
-    assertXPath(pXmlDoc, "//w:rPrChange", 2);
+    assertXPath(pXmlDoc, "//w:rPrChange", 3);
 }
 
 CPPUNIT_TEST_FIXTURE(Test, testLabelWidthAndPosition_Left_FirstLineIndent)
diff --git a/sw/source/core/doc/DocumentRedlineManager.cxx 
b/sw/source/core/doc/DocumentRedlineManager.cxx
index 7a15b19c47c1..ace91c6579b6 100644
--- a/sw/source/core/doc/DocumentRedlineManager.cxx
+++ b/sw/source/core/doc/DocumentRedlineManager.cxx
@@ -2929,6 +2929,36 @@ bool DocumentRedlineManager::HasRedline( const SwPaM& 
rPam, RedlineType nType, b
 const SwRangeRedline* DocumentRedlineManager::GetRedline( const SwPosition& 
rPos,
                                     SwRedlineTable::size_type* pFndPos ) const
 {
+    if (maRedlineTable.HasOverlappingElements())
+    {
+        for (auto it = maRedlineTable.begin(), itEnd = maRedlineTable.end(); 
it != itEnd; ++it)
+        {
+            auto [pStart, pEnd] = (**it).StartEnd();
+            if (rPos < *pStart)
+            {
+                if (pFndPos)
+                {
+                    *pFndPos = std::distance(maRedlineTable.begin(), it);
+                }
+                return nullptr;
+            }
+            if (pEnd == pStart
+                    ? *pStart == rPos
+                    : (*pStart <= rPos && rPos < *pEnd))
+            {
+                if (pFndPos)
+                {
+                    *pFndPos = std::distance(maRedlineTable.begin(), it);
+                }
+                return *it;
+            }
+        }
+        if (pFndPos)
+        {
+            *pFndPos = maRedlineTable.size();
+        }
+        return nullptr;
+    }
     SwRedlineTable::size_type nO = maRedlineTable.size(), nM, nU = 0;
     if( nO > 0 )
     {
commit 9c95c12f12f6c20f0611497d86e92b66081878ed
Author:     Michael Stahl <michael.st...@allotropia.de>
AuthorDate: Thu Feb 20 18:08:33 2025 +0100
Commit:     Thorsten Behrens <thorsten.behr...@allotropia.de>
CommitDate: Tue Feb 25 17:46:51 2025 +0100

    tdf#165322 sw: apply formatting for multiple redlines in SwRedlineItr
    
    SwRedlineItr::Seek() assumes that redlines don't overlap, but sometimes
    they do, at least a ParagraphFormat and a Delete redline can overlap
    (unsure if that is a good idea) when imported from DOCX.
    
    Change-Id: Ic13080bfe8c9910ffef44f3e48d11c7f852427ba
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/181961
    Tested-by: Jenkins
    Reviewed-by: Michael Stahl <michael.st...@allotropia.de>
    (cherry picked from commit b3567d7e1f08df7c050d3cec86cbb200af558172)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182164
    Reviewed-by: Thorsten Behrens <thorsten.behr...@allotropia.de>

diff --git a/sw/inc/redline.hxx b/sw/inc/redline.hxx
index 20befbaa43d7..6a88a0a3ccd1 100644
--- a/sw/inc/redline.hxx
+++ b/sw/inc/redline.hxx
@@ -252,7 +252,8 @@ public:
     void ShowOriginal(sal_uInt16 nLoop, size_t nMyPos, bool bForced = false);
 
     /// Calculates the intersection with text node number nNdIdx.
-    void CalcStartEnd(SwNodeOffset nNdIdx, sal_Int32& rStart, sal_Int32& rEnd) 
const;
+    /// @return true if the entire redline precedes nNdIdx
+    bool CalcStartEnd(SwNodeOffset nNdIdx, sal_Int32& rStart, sal_Int32& rEnd) 
const;
 
     enum class Invalidation { Add, Remove };
     /// Initiate the layout.
diff --git a/sw/qa/extras/layout/data/CT-formatted-deletion.docx 
b/sw/qa/extras/layout/data/CT-formatted-deletion.docx
new file mode 100644
index 000000000000..585d7ba27cab
Binary files /dev/null and 
b/sw/qa/extras/layout/data/CT-formatted-deletion.docx differ
diff --git a/sw/qa/extras/layout/layout2.cxx b/sw/qa/extras/layout/layout2.cxx
index 9364add9ecaf..ae4c53826379 100644
--- a/sw/qa/extras/layout/layout2.cxx
+++ b/sw/qa/extras/layout/layout2.cxx
@@ -522,6 +522,25 @@ CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testTdf129357)
     assertXPathContent(pXmlDoc, 
"/metafile/push/push/push/push/push/textarray[2]/text", u"-");
 }
 
+CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testTdf165322)
+{
+    createSwDoc("CT-formatted-deletion.docx");
+    SwDocShell* pShell = getSwDocShell();
+
+    // Dump the rendering of the first page as an XML file.
+    std::shared_ptr<GDIMetaFile> xMetaFile = pShell->GetPreviewMetaFile();
+    MetafileXmlDump dumper;
+
+    xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
+    CPPUNIT_ASSERT(pXmlDoc);
+
+    // paragraph with 2 redlines was not marked as deleted
+    assertXPath(pXmlDoc,
+                "//text[text() = 'Nunc viverra imperdiet enim. Fusce est. 
Vivamus a "
+                "tellus.']/parent::textarray/preceding-sibling::font[1]",
+                "strikeout", u"1");
+}
+
 CPPUNIT_TEST_FIXTURE(SwLayoutWriter2, testRedlineNumberInNumbering)
 {
     createSwDoc("tdf42748.fodt");
diff --git a/sw/source/core/doc/docredln.cxx b/sw/source/core/doc/docredln.cxx
index cadc6eacfae3..6956e601dcbc 100644
--- a/sw/source/core/doc/docredln.cxx
+++ b/sw/source/core/doc/docredln.cxx
@@ -1750,7 +1750,8 @@ void SwRangeRedline::InvalidateRange(Invalidation const 
eWhy)
 
 /** Calculates the start and end position of the intersection rTmp and
     text node nNdIdx */
-void SwRangeRedline::CalcStartEnd( SwNodeOffset nNdIdx, sal_Int32& rStart, 
sal_Int32& rEnd ) const
+bool SwRangeRedline::CalcStartEnd(SwNodeOffset const nNdIdx,
+        sal_Int32 & rStart, sal_Int32 & rEnd) const
 {
     auto [pRStt, pREnd] = StartEnd(); // SwPosition*
     if( pRStt->GetNodeIndex() < nNdIdx )
@@ -1769,6 +1770,7 @@ void SwRangeRedline::CalcStartEnd( SwNodeOffset nNdIdx, 
sal_Int32& rStart, sal_I
         {
             rStart = COMPLETE_STRING;
             rEnd = COMPLETE_STRING;
+            return true;
         }
     }
     else if( pRStt->GetNodeIndex() == nNdIdx )
@@ -1784,6 +1786,7 @@ void SwRangeRedline::CalcStartEnd( SwNodeOffset nNdIdx, 
sal_Int32& rStart, sal_I
         rStart = COMPLETE_STRING;
         rEnd = COMPLETE_STRING;
     }
+    return false;
 }
 
 static void lcl_storeAnnotationMarks(SwDoc& rDoc, const SwPosition* pStt, 
const SwPosition* pEnd)
diff --git a/sw/source/core/text/redlnitr.cxx b/sw/source/core/text/redlnitr.cxx
index ab9dfb59cea0..7de60b24c406 100644
--- a/sw/source/core/text/redlnitr.cxx
+++ b/sw/source/core/text/redlnitr.cxx
@@ -790,7 +790,7 @@ short SwRedlineItr::Seek(SwFont& rFnt,
             {
                 --nRet;
                 Clear_( &rFnt );    // We go behind the current section
-                ++m_nAct;             // and check the next one
+//                ++m_nAct; // don't increment, could be in next range too if 
overlap
             }
             else if (nNew < m_nStart)
             {
@@ -810,16 +810,35 @@ short SwRedlineItr::Seek(SwFont& rFnt,
         m_nStart = COMPLETE_STRING;
         m_nEnd = COMPLETE_STRING;
         const SwRedlineTable& rTable = 
m_rDoc.getIDocumentRedlineAccess().GetRedlineTable();
+        ::std::optional<decltype(m_nAct)> oFirstMatch;
 
         for ( ; m_nAct < rTable.size() ; ++m_nAct)
         {
-            rTable[ m_nAct ]->CalcStartEnd(nNode, m_nStart, m_nEnd);
+            decltype(m_nStart) nStart;
+            decltype(m_nEnd) nEnd;
+            if (rTable[m_nAct]->CalcStartEnd(nNode, nStart, nEnd))
+            { // previous redline intersected nNode but this one precedes it
+                continue;
+            }
 
-            if (nNew < m_nEnd)
+            // redline table is sorted, but here it's not the complete redlines
+            assert(m_nStart == COMPLETE_STRING || m_nStart <= nStart);
+            assert(m_nStart == COMPLETE_STRING || m_nStart <= nEnd);
+            if (oFirstMatch && nNew < nStart)
             {
-                if (nNew >= m_nStart) // only possible candidate
+                m_nEnd = std::min(m_nEnd, nStart);
+                break;
+            }
+            if (nNew < nEnd)
+            {
+                m_nStart = nStart;
+                m_nEnd = std::min(m_nEnd, nEnd);
+                if (nStart <= nNew) // there can be a format and another 
redline...
                 {
-                    m_bOn = true;
+                    if (!oFirstMatch)
+                    {
+                        oFirstMatch.emplace(m_nAct);
+                    }
                     const SwRangeRedline *pRed = rTable[ m_nAct ];
 
                     if (m_pSet)
@@ -864,13 +883,19 @@ short SwRedlineItr::Seek(SwFont& rFnt,
                         }
                         nWhich = aIter.NextWhich();
                     }
-
-                    ++nRet;
                 }
-                break;
+                else
+                {
+                    break;
+                }
             }
-            m_nStart = COMPLETE_STRING;
-            m_nEnd = COMPLETE_STRING;
+        }
+
+        if (oFirstMatch)
+        {
+            m_bOn = true;
+            m_nAct = *oFirstMatch; // rewind
+            ++nRet; // increment only once per m_nStart/m_nEnd range
         }
     }
     else if (m_eMode == Mode::Hide)
@@ -918,6 +943,7 @@ void SwRedlineItr::FillHints( std::size_t nAuthor, 
RedlineType eType )
             break;
         case RedlineType::Format:
         case RedlineType::FmtColl:
+        case RedlineType::ParagraphFormat:
             SwModule::get()->GetFormatAuthorAttr(nAuthor, *m_pSet);
             break;
         default:
diff --git a/vcl/source/gdi/mtfxmldump.cxx b/vcl/source/gdi/mtfxmldump.cxx
index dee6bb098e5e..eb022fe17367 100644
--- a/vcl/source/gdi/mtfxmldump.cxx
+++ b/vcl/source/gdi/mtfxmldump.cxx
@@ -1337,6 +1337,7 @@ void MetafileXmlDump::writeXml(const GDIMetaFile& 
rMetaFile, tools::XmlWriter& r
                 rWriter.attribute("shadow", aFont.IsShadow() ? "true" : 
"false");
                 rWriter.attribute("wordunderline", aFont.IsWordLineMode() ? 
"true" : "false");
                 rWriter.attribute("outline", aFont.IsOutline() ? "true" : 
"false");
+                rWriter.attribute("strikeout", aFont.GetStrikeout());
 
                 rWriter.endElement();
             }

Reply via email to