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 901fb74eea08ca68cb3bc8dd54ead7954979fd2d Author: Miklos Vajna <[email protected]> AuthorDate: Thu Feb 5 15:13:12 2026 +0100 Commit: Caolán McNamara <[email protected]> CommitDate: Fri Feb 6 10:55:50 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/+/198758 Reviewed-by: Caolán McNamara <[email protected]> Tested-by: Jenkins CollaboraOffice <[email protected]> Tested-by: Caolán McNamara <[email protected]> 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 e65a6bd75b86..ddedfc4ac940 100644 --- a/sw/source/core/text/itrform2.cxx +++ b/sw/source/core/text/itrform2.cxx @@ -1243,7 +1243,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 98efbd700c3e..3fad80b27316 100644 --- a/sw/source/core/text/porfld.cxx +++ b/sw/source/core/text/porfld.cxx @@ -78,8 +78,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 ) @@ -109,6 +109,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 ); } @@ -508,6 +510,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); } @@ -545,10 +553,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) @@ -569,8 +578,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 ); } @@ -729,7 +741,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 ); @@ -797,7 +819,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 ); @@ -811,7 +833,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 02bd073eccf8..211f33f4d41a 100644 --- a/sw/source/core/text/porfld.hxx +++ b/sw/source/core/text/porfld.hxx @@ -37,6 +37,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 @@ -59,7 +60,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 ); @@ -139,6 +140,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 58ece78aab34..1ab948b2f034 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 bb0f6e254969..6eaa0809538d 100644 --- a/sw/source/core/text/txtfld.cxx +++ b/sw/source/core/text/txtfld.cxx @@ -686,13 +686,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 a029c2454ce1..d12d6afd5363 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); }
