sw/qa/core/layout/data/redline-table.docx |binary
 sw/qa/core/layout/paintfrm.cxx            |   48 ++++++++++++++++++++++++++++++
 sw/qa/core/text/data/redline-move.docx    |binary
 sw/qa/core/text/itrpaint.cxx              |   44 +++++++++++++++++++++++++++
 sw/source/core/layout/paintfrm.cxx        |   14 ++++++--
 sw/source/core/text/redlnitr.cxx          |   29 ++++++++++--------
 sw/source/core/text/redlnitr.hxx          |    4 +-
 vcl/source/gdi/mtfxmldump.cxx             |    1 
 8 files changed, 122 insertions(+), 18 deletions(-)

New commits:
commit 965e5e7c9085b6dda431dd6166b3a4b8e95b3ace
Author:     Miklos Vajna <[email protected]>
AuthorDate: Tue Feb 3 16:23:59 2026 +0100
Commit:     Xisco Fauli <[email protected]>
CommitDate: Mon Feb 9 09:34:11 2026 +0100

    cool#13988 sw redline render mode: table row redlines
    
    The bugdoc has some table row-level insert and delete redlines.
    Switching to omit insert/delete redline render mode is expected to just
    color existing text as gray/red/green, and we get an unexpected table
    background here.
    
    The table background was added in commit
    f348440e17debacbcba9153e238e010e8c020bdc (tdf#146120 sw: show tracked
    table changes with different color, 2021-12-08), and is wanted with
    standard redline render mode, where you already underline/strikethrough
    modified text.
    
    Fix the problem by rendering table background similar to "hide changes"
    when we're in a non-standard redline render mode.
    
    In the future, we may want to draw some red/green border around these
    table row or cell frames, similar to what we do for images. But for now,
    just make sure the unwanted background is not painted.
    
    Change-Id: I42f8ec3acacb697fb59ad9ee4d5d9a62efade76a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198695
    Reviewed-by: Miklos Vajna <[email protected]>
    Tested-by: Jenkins
    Signed-off-by: Xisco Fauli <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198819

diff --git a/sw/qa/core/layout/data/redline-table.docx 
b/sw/qa/core/layout/data/redline-table.docx
new file mode 100644
index 000000000000..2af033fa151e
Binary files /dev/null and b/sw/qa/core/layout/data/redline-table.docx differ
diff --git a/sw/qa/core/layout/paintfrm.cxx b/sw/qa/core/layout/paintfrm.cxx
index cddb43f4b80d..b3d48ed1e808 100644
--- a/sw/qa/core/layout/paintfrm.cxx
+++ b/sw/qa/core/layout/paintfrm.cxx
@@ -11,9 +11,12 @@
 
 #include <o3tl/string_view.hxx>
 #include <svtools/DocumentToGraphicRenderer.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/metaact.hxx>
 
 #include <docsh.hxx>
 #include <unotxdoc.hxx>
+#include <wrtsh.hxx>
 
 namespace
 {
@@ -212,6 +215,51 @@ CPPUNIT_TEST_FIXTURE(Test, testEndnoteContSeparator)
     // i.e. the separator was too short vs Word.
     CPPUNIT_ASSERT_EQUAL(static_cast<sal_Int32>(9360), 
nEndnoteSeparatorLength);
 }
+
+int CountPolyPolygons(const GDIMetaFile& rMetaFile)
+{
+    int nCount = 0;
+    for (size_t nAction = 0; nAction < rMetaFile.GetActionSize(); ++nAction)
+    {
+        MetaAction* pAction = rMetaFile.GetAction(nAction);
+        if (pAction->GetType() != MetaActionType::POLYPOLYGON)
+        {
+            continue;
+        }
+
+        ++nCount;
+    }
+    return nCount;
+}
+
+CPPUNIT_TEST_FIXTURE(Test, testTableRedlineRenderMode)
+{
+    // Given a document with table redlines, standard redline render mode:
+    createSwDoc("redline-table.docx");
+
+    // When painting those redlines:
+    SwDocShell* pDocShell = getSwDocShell();
+    std::shared_ptr<GDIMetaFile> xMetaFile = pDocShell->GetPreviewMetaFile();
+
+    // Make sure we have the filled polygons for the rows:
+    CPPUNIT_ASSERT_EQUAL(2, CountPolyPolygons(*xMetaFile));
+
+    // And given a document with 'omit inserts' redline render mode:
+    SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+    SwViewOption aOpt(*pWrtShell->GetViewOptions());
+    aOpt.SetRedlineRenderMode(SwRedlineRenderMode::OmitInserts);
+    pWrtShell->ApplyViewOptions(aOpt);
+
+    // When painting those redlines:
+    xMetaFile = pDocShell->GetPreviewMetaFile();
+
+    // Make sure we have no filled polygons for the rows:
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 0
+    // - Actual  : 2
+    // i.e. we had unexpected filled polygons where just colored text was 
wanted.
+    CPPUNIT_ASSERT_EQUAL(0, CountPolyPolygons(*xMetaFile));
+}
 }
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sw/source/core/layout/paintfrm.cxx 
b/sw/source/core/layout/paintfrm.cxx
index 73bec434fb24..3af59db24d89 100644
--- a/sw/source/core/layout/paintfrm.cxx
+++ b/sw/source/core/layout/paintfrm.cxx
@@ -6804,7 +6804,13 @@ void SwFrame::PaintSwFrameBackground( const SwRect 
&rRect, const SwPageFrame *pP
     bool bBack = GetBackgroundBrush( aFillAttributes, pItem, pCol, 
aOrigBackRect, bLowerMode, /*bConsiderTextBox=*/false );
 
     // show track changes of table row
-    if( IsRowFrame() && !getRootFrame()->IsHideRedlines() )
+    const SwViewOption* pViewOptions = pSh->GetViewOptions();
+    SwRedlineRenderMode eRedlineRenderMode = 
pViewOptions->GetRedlineRenderMode();
+    // Non-standard redline render mode means we don't paint a custom 
background color for table
+    // redlines.
+    bool bHideTableRedlines
+        = getRootFrame()->IsHideRedlines() || eRedlineRenderMode != 
SwRedlineRenderMode::Standard;
+    if( IsRowFrame() && !bHideTableRedlines )
     {
         RedlineType eType = static_cast<const 
SwRowFrame*>(this)->GetTabLine()->GetRedlineType();
         if ( RedlineType::Delete == eType || RedlineType::Insert == eType )
@@ -6813,7 +6819,7 @@ void SwFrame::PaintSwFrameBackground( const SwRect 
&rRect, const SwPageFrame *pP
             bBack = true;
         }
     }
-    else if ( IsCellFrame() && !getRootFrame()->IsHideRedlines() )
+    else if ( IsCellFrame() && !bHideTableRedlines )
     {
         RedlineType eType = static_cast<const 
SwCellFrame*>(this)->GetTabBox()->GetRedlineType();
         if ( RedlineType::Delete == eType || RedlineType::Insert == eType )
@@ -6823,7 +6829,7 @@ void SwFrame::PaintSwFrameBackground( const SwRect 
&rRect, const SwPageFrame *pP
         }
     }
 
-    if ( bBack && IsCellFrame() && !getRootFrame()->IsHideRedlines() &&
+    if ( bBack && IsCellFrame() && !bHideTableRedlines &&
         // skip cell background to show the row colored according to its 
tracked change
         RedlineType::None != static_cast<const 
SwRowFrame*>(GetUpper())->GetTabLine()->GetRedlineType() )
     {
@@ -6849,7 +6855,7 @@ void SwFrame::PaintSwFrameBackground( const SwRect 
&rRect, const SwPageFrame *pP
             //  #i6467# - on print output, pdf output and in embedded mode not 
editing color COL_WHITE is used
             // instead of the global retouche color.
             if ( pSh->GetOut()->GetOutDevType() == OUTDEV_PRINTER ||
-                 pSh->GetViewOptions()->IsPDFExport() ||
+                 pViewOptions->IsPDFExport() ||
                  ( pSh->GetDoc()->GetDocShell()->GetCreateMode() == 
SfxObjectCreateMode::EMBEDDED &&
                    !pSh->GetDoc()->GetDocShell()->IsInPlaceActive()
                  )
commit 2bf07093434c58f6c626d06b6387a573826d3d0d
Author:     Miklos Vajna <[email protected]>
AuthorDate: Mon Feb 2 13:31:04 2026 +0100
Commit:     Xisco Fauli <[email protected]>
CommitDate: Mon Feb 9 09:34:02 2026 +0100

    cool#13988 sw redline render mode: handle moves
    
    Open the bugdoc, switch to a non-standard redline render mode (omit
    inserts or omit deletes), it's expected to see red-gray or gray-green
    pairs of text (for delete and insert), but instead double underline was
    shown for some inserts.
    
    This comes from the insert part of moves, which is wanted for the
    stanard redline render mode, where an author-specific color is used,
    then green (on top of that) means a move.
    
    Fix the problem by avoiding the move-specific rendering for the
    non-standard redline render mode case, similar to what the
    officecfg::Office::Writer::Comparison::DisplayMovedTextInGreen setting
    does.
    
    This required extending the metafile dumper a bit, since underlines were
    not exposed in the XML dump.
    
    Change-Id: I717eb0925c1959f787701ef9778ddb704b28031a
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198583
    Reviewed-by: Miklos Vajna <[email protected]>
    Tested-by: Jenkins
    Signed-off-by: Xisco Fauli <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198818

diff --git a/sw/qa/core/text/data/redline-move.docx 
b/sw/qa/core/text/data/redline-move.docx
new file mode 100644
index 000000000000..7377bed2f299
Binary files /dev/null and b/sw/qa/core/text/data/redline-move.docx differ
diff --git a/sw/qa/core/text/itrpaint.cxx b/sw/qa/core/text/itrpaint.cxx
index b5debdfa8f83..710f359ac0fc 100644
--- a/sw/qa/core/text/itrpaint.cxx
+++ b/sw/qa/core/text/itrpaint.cxx
@@ -150,6 +150,50 @@ CPPUNIT_TEST_FIXTURE(Test, 
testRedlineRenderModeOmitInsertDelete)
     CPPUNIT_ASSERT_EQUAL(120, GetColorHue(aColor3));
 }
 
+CPPUNIT_TEST_FIXTURE(Test, testMoveRedlineRenderModeOmitDelete)
+{
+    // Given a <from>move it</from>baseline<to>move it</to> bugdoc:
+    createSwDoc("redline-move.docx");
+    SwDocShell* pDocShell = getSwDocShell();
+    SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+    SwViewOption aOpt(*pWrtShell->GetViewOptions());
+    aOpt.SetRedlineRenderMode(SwRedlineRenderMode::OmitDeletes);
+    pWrtShell->ApplyViewOptions(aOpt);
+
+    // When rendering that while omitting deletes:
+    std::shared_ptr<GDIMetaFile> xMetaFile = pDocShell->GetPreviewMetaFile();
+
+    // Then make sure "from" has no strikethrough and "to" has no underline:
+    MetafileXmlDump dumper;
+    xmlDocUniquePtr pXmlDoc = dumpAndParse(dumper, *xMetaFile);
+    assertXPath(pXmlDoc, "//textarray", 3);
+    OUString aContent = getXPathContent(pXmlDoc, "(//textarray)[1]/text");
+    sal_Int32 nIndex1 = getXPath(pXmlDoc, "(//textarray)[1]", 
"index").toInt32();
+    sal_Int32 nLength1 = getXPath(pXmlDoc, "(//textarray)[1]", 
"length").toInt32();
+    CPPUNIT_ASSERT_EQUAL(u"move it"_ustr, aContent.copy(nIndex1, nLength1));
+    OUString aFontStrikeout
+        = getXPath(pXmlDoc, "(//textarray)[1]/preceding-sibling::font[1]", 
"strikeout");
+    CPPUNIT_ASSERT_EQUAL(u"0"_ustr, aFontStrikeout);
+    OUString aFontUnderline
+        = getXPath(pXmlDoc, "(//textarray)[1]/preceding-sibling::font[1]", 
"underline");
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 0
+    // - Actual  : 2
+    // i.e. there was an unexpected underline, while only coloring is expected 
for moves when
+    // non-standard redline render mode is used.
+    CPPUNIT_ASSERT_EQUAL(u"0"_ustr, aFontUnderline);
+    sal_Int32 nIndex2 = getXPath(pXmlDoc, "(//textarray)[2]", 
"index").toInt32();
+    sal_Int32 nLength2 = getXPath(pXmlDoc, "(//textarray)[2]", 
"length").toInt32();
+    CPPUNIT_ASSERT_EQUAL(u"baseline"_ustr, aContent.copy(nIndex2, nLength2));
+    sal_Int32 nIndex3 = getXPath(pXmlDoc, "(//textarray)[3]", 
"index").toInt32();
+    sal_Int32 nLength3 = getXPath(pXmlDoc, "(//textarray)[3]", 
"length").toInt32();
+    CPPUNIT_ASSERT_EQUAL(u"move it"_ustr, aContent.copy(nIndex3, nLength3));
+    aFontStrikeout = getXPath(pXmlDoc, 
"(//textarray)[3]/preceding-sibling::font[1]", "strikeout");
+    CPPUNIT_ASSERT_EQUAL(u"0"_ustr, aFontStrikeout);
+    aFontUnderline = getXPath(pXmlDoc, 
"(//textarray)[3]/preceding-sibling::font[1]", "underline");
+    CPPUNIT_ASSERT_EQUAL(u"0"_ustr, aFontUnderline);
+}
+
 struct ImageInfo
 {
     Bitmap m_aBitmap;
diff --git a/sw/source/core/text/redlnitr.cxx b/sw/source/core/text/redlnitr.cxx
index ce7988b64d84..0cebaa0a83f9 100644
--- a/sw/source/core/text/redlnitr.cxx
+++ b/sw/source/core/text/redlnitr.cxx
@@ -891,6 +891,15 @@ short SwRedlineItr::Seek(SwFont& rFnt,
         const SwRedlineTable& rTable = 
m_rDoc.getIDocumentRedlineAccess().GetRedlineTable();
         ::std::optional<decltype(m_nAct)> oFirstMatch;
 
+        const SwDocShell* pDocShell = m_rDoc.GetDocShell();
+        const SwWrtShell* pWrtShell = pDocShell ? pDocShell->GetWrtShell() : 
nullptr;
+        SwRedlineRenderMode eRenderMode = SwRedlineRenderMode::Standard;
+        if (pWrtShell)
+        {
+            const SwViewOption* pOptions = pWrtShell->GetViewOptions();
+            eRenderMode = pOptions->GetRedlineRenderMode();
+        }
+
         for ( ; m_nAct < rTable.size() ; ++m_nAct)
         {
             decltype(m_nStart) nStart;
@@ -930,15 +939,18 @@ short SwRedlineItr::Seek(SwFont& rFnt,
                     }
 
                     if( 1 < pRed->GetStackCount() )
-                        FillHints( pRed->GetAuthor( 1 ), pRed->GetType( 1 ) );
-                    FillHints( pRed->GetAuthor(), pRed->GetType() );
+                        FillHints(pRed->GetAuthor(1), pRed->GetType(1), 
eRenderMode);
+                    FillHints(pRed->GetAuthor(), pRed->GetType(), eRenderMode);
 
                     SfxWhichIter aIter( *m_pSet );
 
                     // moved text: dark green with double underline or 
strikethrough
                     bool bDisplayMovedTextInGreen = 
officecfg::Office::Writer::Comparison::DisplayMovedTextInGreen::get();
-                    if ( bDisplayMovedTextInGreen && pRed->IsMoved() )
+                    if (bDisplayMovedTextInGreen && pRed->IsMoved()
+                        && eRenderMode == SwRedlineRenderMode::Standard)
                     {
+                        // Standard redline render mode, so move is more than 
just insert and
+                        // delete.
                         m_pSet->Put(SvxColorItem( COL_GREEN, RES_CHRATR_COLOR 
));
                         if (SfxItemState::SET == 
m_pSet->GetItemState(RES_CHRATR_CROSSEDOUT, true))
                             m_pSet->Put(SvxCrossedOutItem( STRIKEOUT_DOUBLE, 
RES_CHRATR_CROSSEDOUT ));
@@ -1010,17 +1022,8 @@ short SwRedlineItr::Seek(SwFont& rFnt,
     return nRet + EnterExtend(rFnt, nNode, nNew);
 }
 
-void SwRedlineItr::FillHints( std::size_t nAuthor, RedlineType eType )
+void SwRedlineItr::FillHints( std::size_t nAuthor, RedlineType eType, 
SwRedlineRenderMode eRenderMode )
 {
-    const SwDocShell* pDocShell = m_rDoc.GetDocShell();
-    const SwWrtShell* pWrtShell = pDocShell ? pDocShell->GetWrtShell() : 
nullptr;
-    SwRedlineRenderMode eRenderMode = SwRedlineRenderMode::Standard;
-    if (pWrtShell)
-    {
-        const SwViewOption* pOptions = pWrtShell->GetViewOptions();
-        eRenderMode = pOptions->GetRedlineRenderMode();
-    }
-
     switch ( eType )
     {
         case RedlineType::Insert:
diff --git a/sw/source/core/text/redlnitr.hxx b/sw/source/core/text/redlnitr.hxx
index 5c301312640e..3132af3053c8 100644
--- a/sw/source/core/text/redlnitr.hxx
+++ b/sw/source/core/text/redlnitr.hxx
@@ -67,6 +67,8 @@ public:
     void UpdateFont(SwFont &rFont) { ActualizeFont(rFont, m_rArr[m_nPos - 
m_nStart]); }
 };
 
+enum class SwRedlineRenderMode;
+
 class SwRedlineItr
 {
     std::deque<SwTextAttr *> m_Hints;
@@ -88,7 +90,7 @@ private:
 
     void Clear_( SwFont* pFnt );
     bool ChkSpecialUnderline_() const;
-    void FillHints( std::size_t nAuthor, RedlineType eType );
+    void FillHints(std::size_t nAuthor, RedlineType eType, SwRedlineRenderMode 
eRenderMode);
     short EnterExtend(SwFont& rFnt, SwNodeOffset const nNode, sal_Int32 const 
nNew)
     {
         if (m_pExt) return m_pExt->Enter(rFnt, nNode, nNew);
diff --git a/vcl/source/gdi/mtfxmldump.cxx b/vcl/source/gdi/mtfxmldump.cxx
index 6f8cdc1e7d5c..8b1dfabb072d 100644
--- a/vcl/source/gdi/mtfxmldump.cxx
+++ b/vcl/source/gdi/mtfxmldump.cxx
@@ -1352,6 +1352,7 @@ void MetafileXmlDump::writeXml(const GDIMetaFile& 
rMetaFile, tools::XmlWriter& r
                 rWriter.attribute("wordunderline", aFont.IsWordLineMode() ? 
"true" : "false");
                 rWriter.attribute("outline", aFont.IsOutline() ? "true" : 
"false");
                 rWriter.attribute("strikeout", aFont.GetStrikeout());
+                rWriter.attribute("underline", aFont.GetUnderline());
 
                 rWriter.endElement();
             }

Reply via email to