sw/inc/redline.hxx                                  |    3 -
 sw/qa/extras/layout/data/CT-formatted-deletion.docx |binary
 sw/qa/extras/layout/layout2.cxx                     |   19 ++++++++
 sw/source/core/doc/docredln.cxx                     |    5 +-
 sw/source/core/text/redlnitr.cxx                    |   46 +++++++++++++++-----
 vcl/source/gdi/mtfxmldump.cxx                       |    1 
 6 files changed, 62 insertions(+), 12 deletions(-)

New commits:
commit b3567d7e1f08df7c050d3cec86cbb200af558172
Author:     Michael Stahl <michael.st...@allotropia.de>
AuthorDate: Thu Feb 20 18:08:33 2025 +0100
Commit:     Michael Stahl <michael.st...@allotropia.de>
CommitDate: Tue Feb 25 12:54:22 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>

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 1e73f97af75e..a9cd34f17432 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 2434d4863fb1..27307315724d 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* pStart, 
const SwPosition* pEnd)
diff --git a/sw/source/core/text/redlnitr.cxx b/sw/source/core/text/redlnitr.cxx
index 0058064e1513..36eedcf86134 100644
--- a/sw/source/core/text/redlnitr.cxx
+++ b/sw/source/core/text/redlnitr.cxx
@@ -800,7 +800,7 @@ short SwRedlineItr::Seek(SwFont& rFnt,
             {
                 --nRet;
                 Clear_( &rFnt );    // We go behind the current range
-                ++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)
             {
@@ -820,16 +820,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)
@@ -874,13 +893,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)
@@ -928,6 +953,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 2fa6052567f7..84fb0019e9d1 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