sw/CppunitTest_sw_core_text.mk | 1 sw/qa/core/text/data/redline-number-portion.docx |binary sw/qa/core/text/porfld.cxx | 71 +++++++++++++++++++++++ sw/source/core/text/itrform2.cxx | 2 sw/source/core/text/porfld.cxx | 36 +++++++++-- sw/source/core/text/porfld.hxx | 4 - sw/source/core/text/porftn.hxx | 2 sw/source/core/text/txtfld.cxx | 10 ++- sw/source/core/txtnode/swfont.cxx | 5 + 9 files changed, 120 insertions(+), 11 deletions(-)
New commits: commit e609c44293ca7ba5658459aec15b6b084cb1bfd1 Author: Miklos Vajna <[email protected]> AuthorDate: Thu Feb 5 15:13:12 2026 +0100 Commit: Miklos Vajna <[email protected]> CommitDate: Fri Feb 6 15:10:41 2026 +0100 cool#13988 sw redline render mode: handle number portions Open the bugdoc, switch to non-standard redline render mode (e.g. dispatch .uno:RedlineRenderMode), the "2." number portion is still underlined, while this mode doesn't underline insertions otherwise. The reason this happens is because number portions don't decide redline coloring/markup at render time, like SwTextPainter::DrawTextLine() does, instead the font to be used is decided earlier at layout time in SwTextFormatter::NewNumberPortion(). Fix the problem by creating two fonts in SwTextFormatter::NewNumberPortion(), one without the redline markup and one with it. And then later in SwNumberPortion::Paint() use the font that matches the current rendline render mode. This works, because changing the redline render mode means SwNumberPortion::Paint() will be invoked, but the layout's portions won't be re-created for performance reasons. Note that similar to table redlines, this just disables the unwanted standard redline render decoration. That is probably fine, since the surrounding "real" text is still colored gray/red/green. But we possibly this can be improved in the future to apply similar coloring even for the number portion. Change-Id: I16b4123d20bf0c14c76bb23e574eb21a719d15df Reviewed-on: https://gerrit.libreoffice.org/c/core/+/198822 Reviewed-by: Miklos Vajna <[email protected]> Tested-by: Jenkins diff --git a/sw/CppunitTest_sw_core_text.mk b/sw/CppunitTest_sw_core_text.mk index 42be7e727b0c..e90c77e5f38f 100644 --- a/sw/CppunitTest_sw_core_text.mk +++ b/sw/CppunitTest_sw_core_text.mk @@ -18,6 +18,7 @@ $(eval $(call gb_CppunitTest_add_exception_objects,sw_core_text, \ sw/qa/core/text/itratr \ sw/qa/core/text/itrpaint \ sw/qa/core/text/itrform2 \ + sw/qa/core/text/porfld \ sw/qa/core/text/porlay \ sw/qa/core/text/porrst \ sw/qa/core/text/text \ diff --git a/sw/qa/core/text/data/redline-number-portion.docx b/sw/qa/core/text/data/redline-number-portion.docx new file mode 100644 index 000000000000..26ba473e8085 Binary files /dev/null and b/sw/qa/core/text/data/redline-number-portion.docx differ diff --git a/sw/qa/core/text/porfld.cxx b/sw/qa/core/text/porfld.cxx new file mode 100644 index 000000000000..6ab3d3f3c550 --- /dev/null +++ b/sw/qa/core/text/porfld.cxx @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <swmodeltestbase.hxx> + +#include <memory> + +#include <vcl/gdimtf.hxx> + +#include <docsh.hxx> +#include <wrtsh.hxx> + +namespace +{ +/// Covers sw/source/core/text/porfld.cxx fixes. +class Test : public SwModelTestBase +{ +public: + Test() + : SwModelTestBase(u"/sw/qa/core/text/data/"_ustr) + { + } +}; + +CPPUNIT_TEST_FIXTURE(Test, testNumberPortionRedlineRenderMode) +{ + // Given a document with redlines, the "2." number portion is inserted: + createSwDoc("redline-number-portion.docx"); + + // When redline render mode is standard: + SwDocShell* pDocShell = getSwDocShell(); + std::shared_ptr<GDIMetaFile> xMetaFile = pDocShell->GetPreviewMetaFile(); + + // Then make sure we paint an underline: + MetafileXmlDump aDumper; + xmlDocUniquePtr pXmlDoc = dumpAndParse(aDumper, *xMetaFile); + OUString aContent = getXPathContent(pXmlDoc, "(//textarray)[3]/text"); + CPPUNIT_ASSERT_EQUAL(u"2."_ustr, aContent); + OUString aUnderline + = getXPath(pXmlDoc, "(//textarray)[3]/preceding-sibling::font[1]", "underline"); + CPPUNIT_ASSERT_EQUAL(u"1"_ustr, aUnderline); + + // And given "omit inserts" redline render mode: + SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); + SwViewOption aOpt(*pWrtShell->GetViewOptions()); + aOpt.SetRedlineRenderMode(SwRedlineRenderMode::OmitInserts); + pWrtShell->ApplyViewOptions(aOpt); + + // When rendering: + xMetaFile = pDocShell->GetPreviewMetaFile(); + + // Then make sure we don't paint an underline: + pXmlDoc = dumpAndParse(aDumper, *xMetaFile); + aContent = getXPathContent(pXmlDoc, "(//textarray)[3]/text"); + CPPUNIT_ASSERT_EQUAL(u"2."_ustr, aContent); + aUnderline = getXPath(pXmlDoc, "(//textarray)[3]/preceding-sibling::font[1]", "underline"); + // Without the accompanying fix in place, this test would have failed with: + // - Expected: 0 + // - Actual : 1 + // i.e. there was an unexpected underline. + CPPUNIT_ASSERT_EQUAL(u"0"_ustr, aUnderline); +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/core/text/itrform2.cxx b/sw/source/core/text/itrform2.cxx index cca47ce36a9a..52578639a83c 100644 --- a/sw/source/core/text/itrform2.cxx +++ b/sw/source/core/text/itrform2.cxx @@ -1268,7 +1268,7 @@ SwTextPortion *SwTextFormatter::WhichTextPor( SwTextFormatInfo &rInf ) const } } assert(2 <= sal_Int32(nFieldLen)); - pPor = new SwFieldPortion(SwFieldType::GetTypeStr(SwFieldTypesEnum::Input), nullptr, nFieldLen); + pPor = new SwFieldPortion(SwFieldType::GetTypeStr(SwFieldTypesEnum::Input), nullptr, nullptr, nFieldLen); } else { diff --git a/sw/source/core/text/porfld.cxx b/sw/source/core/text/porfld.cxx index 899db3f0a785..7eb0cf6391fd 100644 --- a/sw/source/core/text/porfld.cxx +++ b/sw/source/core/text/porfld.cxx @@ -79,8 +79,8 @@ void SwFieldPortion::TakeNextOffset( const SwFieldPortion* pField ) m_bFollow = true; } -SwFieldPortion::SwFieldPortion(OUString aExpand, std::unique_ptr<SwFont> pFont, TextFrameIndex const nFieldLen) - : m_aExpand(std::move(aExpand)), m_pFont(std::move(pFont)), m_nNextOffset(0) +SwFieldPortion::SwFieldPortion(OUString aExpand, std::unique_ptr<SwFont> pFont, std::unique_ptr<SwFont> pRedlineRenderModeFont, TextFrameIndex const nFieldLen) + : m_aExpand(std::move(aExpand)), m_pFont(std::move(pFont)), m_pRedlineRenderModeFont(std::move(pRedlineRenderModeFont)), m_nNextOffset(0) , m_nNextScriptChg(COMPLETE_STRING), m_nFieldLen(nFieldLen), m_nViewWidth(0) , m_bFollow( false ), m_bLeft( false), m_bHide( false) , m_bCenter (false), m_bHasFollow( false ) @@ -110,6 +110,8 @@ SwFieldPortion::SwFieldPortion( const SwFieldPortion& rField ) { if ( rField.HasFont() ) m_pFont.reset( new SwFont( *rField.GetFont() ) ); + if (rField.m_pRedlineRenderModeFont) + m_pRedlineRenderModeFont.reset(new SwFont(*rField.m_pRedlineRenderModeFont)); SetWhichPor( PortionType::Field ); } @@ -520,6 +522,12 @@ void SwFieldPortion::dumpAsXml(xmlTextWriterPtr pWriter, const OUString& rText, { m_pFont->dumpAsXml(pWriter); } + if (m_pRedlineRenderModeFont) + { + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("redline-render-mode-font")); + m_pRedlineRenderModeFont->dumpAsXml(pWriter); + (void)xmlTextWriterEndElement(pWriter); + } (void)xmlTextWriterEndElement(pWriter); } @@ -557,10 +565,11 @@ bool SwHiddenPortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) SwNumberPortion::SwNumberPortion( const OUString &rExpand, std::unique_ptr<SwFont> pFont, + std::unique_ptr<SwFont> pRedlineRenderModeFont, const bool bLft, const bool bCntr, const SwTwips nMinDst, const bool bLabelAlignmentPosAndSpaceModeActive ) - : SwFieldPortion(rExpand, std::move(pFont), TextFrameIndex(0)) + : SwFieldPortion(rExpand, std::move(pFont), std::move(pRedlineRenderModeFont), TextFrameIndex(0)) , m_nFixWidth(0) , m_nMinDist(nMinDst) , mbLabelAlignmentPosAndSpaceModeActive(bLabelAlignmentPosAndSpaceModeActive) @@ -581,8 +590,11 @@ SwFieldPortion *SwNumberPortion::Clone( const OUString &rExpand ) const std::unique_ptr<SwFont> pNewFnt; if( m_pFont ) pNewFnt.reset(new SwFont( *m_pFont )); + std::unique_ptr<SwFont> pNewRedlineRenderModeFont; + if (m_pRedlineRenderModeFont) + pNewRedlineRenderModeFont.reset(new SwFont(*m_pRedlineRenderModeFont)); - return new SwNumberPortion( rExpand, std::move(pNewFnt), IsLeft(), IsCenter(), + return new SwNumberPortion( rExpand, std::move(pNewFnt), std::move(pNewRedlineRenderModeFont), IsLeft(), IsCenter(), m_nMinDist, mbLabelAlignmentPosAndSpaceModeActive ); } @@ -741,7 +753,17 @@ void SwNumberPortion::Paint( const SwTextPaintInfo &rInf ) const STRIKEOUT_NONE != m_pFont->GetStrikeout() ) && !m_pFont->IsWordLineMode(); - SwFontSave aSave( rInf, m_pFont.get() ); + const SwViewShell* pViewShell = rInf.GetVsh(); + const SwViewOption* pViewOptions = pViewShell ? pViewShell->GetViewOptions() : nullptr; + SwRedlineRenderMode eRedlineRenderMode = pViewOptions ? pViewOptions->GetRedlineRenderMode() : SwRedlineRenderMode::Standard; + SwFont* pFont = m_pFont.get(); + if (eRedlineRenderMode != SwRedlineRenderMode::Standard) + { + // Non-standard redline render mode: avoid using the font which is decorated with + // underline/strike-through. + pFont = m_pRedlineRenderModeFont.get(); + } + SwFontSave aSave(rInf, pFont); if( m_nFixWidth == Width() && ! HasFollow() ) SwExpandPortion::Paint( rInf ); @@ -809,7 +831,7 @@ SwBulletPortion::SwBulletPortion( const sal_UCS4 cBullet, const SwTwips nMinDst, const bool bLabelAlignmentPosAndSpaceModeActive ) : SwNumberPortion( OUString(&cBullet, 1) + rBulletFollowedBy, - std::move(pFont), bLft, bCntr, nMinDst, + std::move(pFont), nullptr, bLft, bCntr, nMinDst, bLabelAlignmentPosAndSpaceModeActive ) { SetWhichPor( PortionType::Bullet ); @@ -823,7 +845,7 @@ SwGrfNumPortion::SwGrfNumPortion( const SwFormatVertOrient* pGrfOrient, const Size& rGrfSize, const bool bLft, const bool bCntr, const SwTwips nMinDst, const bool bLabelAlignmentPosAndSpaceModeActive ) : - SwNumberPortion( rGraphicFollowedBy, nullptr, bLft, bCntr, nMinDst, + SwNumberPortion( rGraphicFollowedBy, nullptr, nullptr, bLft, bCntr, nMinDst, bLabelAlignmentPosAndSpaceModeActive ), m_pBrush( new SvxBrushItem(RES_BACKGROUND) ), m_nId( 0 ) { diff --git a/sw/source/core/text/porfld.hxx b/sw/source/core/text/porfld.hxx index da9624ffd854..8d31c000487b 100644 --- a/sw/source/core/text/porfld.hxx +++ b/sw/source/core/text/porfld.hxx @@ -39,6 +39,7 @@ class SwFieldPortion : public SwExpandPortion protected: OUString m_aExpand; // The expanded field std::unique_ptr<SwFont> m_pFont; // For multi-line fields + std::unique_ptr<SwFont> m_pRedlineRenderModeFont; TextFrameIndex m_nNextOffset; // Offset of the follow in the original string TextFrameIndex m_nNextScriptChg; TextFrameIndex m_nFieldLen; //< Length of field text, 1 for normal fields, any number for input fields @@ -61,7 +62,7 @@ protected: public: SwFieldPortion( const SwFieldPortion& rField ); - SwFieldPortion(OUString aExpand, std::unique_ptr<SwFont> pFnt = nullptr, TextFrameIndex nLen = TextFrameIndex(1)); + SwFieldPortion(OUString aExpand, std::unique_ptr<SwFont> pFnt = nullptr, std::unique_ptr<SwFont> pRedlineRenderModeFont = nullptr, TextFrameIndex nLen = TextFrameIndex(1)); virtual ~SwFieldPortion() override; void TakeNextOffset( const SwFieldPortion* pField ); @@ -141,6 +142,7 @@ protected: public: SwNumberPortion( const OUString &rExpand, std::unique_ptr<SwFont> pFnt, + std::unique_ptr<SwFont> pRedlineRenderModeFont, const bool bLeft, const bool bCenter, const SwTwips nMinDst, const bool bLabelAlignmentPosAndSpaceModeActive ); diff --git a/sw/source/core/text/porftn.hxx b/sw/source/core/text/porftn.hxx index b22deb6ca4a2..32ae1ab4159b 100644 --- a/sw/source/core/text/porftn.hxx +++ b/sw/source/core/text/porftn.hxx @@ -54,7 +54,7 @@ class SwFootnoteNumPortion : public SwNumberPortion { public: SwFootnoteNumPortion( const OUString &rExpand, std::unique_ptr<SwFont> pFntL ) - : SwNumberPortion( rExpand, std::move(pFntL), true, false, 0, false ) + : SwNumberPortion( rExpand, std::move(pFntL), nullptr, true, false, 0, false ) { SetWhichPor( PortionType::FootnoteNum ); } }; diff --git a/sw/source/core/text/txtfld.cxx b/sw/source/core/text/txtfld.cxx index dbb178c66d10..4eb0934bd979 100644 --- a/sw/source/core/text/txtfld.cxx +++ b/sw/source/core/text/txtfld.cxx @@ -692,13 +692,21 @@ SwNumberPortion *SwTextFormatter::NewNumberPortion( SwTextFormatInfo &rInf ) con checkApplyParagraphMarkFormatToNumbering(pNumFnt.get(), rInf, pIDSA, pFormat); + auto pNumRedlineRenderModeFont = std::make_unique<SwFont>(*pNumFnt); + if ( !lcl_setRedlineAttr( rInf, *pTextNd, pNumFnt ) && bHasHiddenNum ) + { pNumFnt->SetColor(SwViewOption::GetCurrentViewOptions().GetNonPrintingCharacterColor()); + pNumRedlineRenderModeFont->SetColor( + SwViewOption::GetCurrentViewOptions().GetNonPrintingCharacterColor()); + } // we do not allow a vertical font pNumFnt->SetVertical( pNumFnt->GetOrientation(), m_pFrame->IsVertical() ); + pNumRedlineRenderModeFont->SetVertical( + pNumRedlineRenderModeFont->GetOrientation(), m_pFrame->IsVertical()); - pRet = new SwNumberPortion( aTextNow, std::move(pNumFnt), + pRet = new SwNumberPortion( aTextNow, std::move(pNumFnt), std::move(pNumRedlineRenderModeFont), bLeft, bCenter, nMinDist, bLabelAlignmentPosAndSpaceModeActive ); } diff --git a/sw/source/core/txtnode/swfont.cxx b/sw/source/core/txtnode/swfont.cxx index 07dfef5d43c7..ef33a4e6bae8 100644 --- a/sw/source/core/txtnode/swfont.cxx +++ b/sw/source/core/txtnode/swfont.cxx @@ -365,6 +365,11 @@ void SwFont::dumpAsXml(xmlTextWriterPtr writer) const ss << GetWeight(); (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("weight"), BAD_CAST(ss.str().c_str())); } + { + std::stringstream ss; + ss << GetUnderline(); + (void)xmlTextWriterWriteAttribute(writer, BAD_CAST("underline"), BAD_CAST(ss.str().c_str())); + } (void)xmlTextWriterEndElement(writer); }
