From 117d9624a3b861ead575394f6dfc974b4f4eeee6 Mon Sep 17 00:00:00 2001 From: Neil Roberts Date: Wed, 5 Nov 2025 11:08:25 +0100 Subject: [PATCH] Consolidate calls to GetLocalizedChar into one helper function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The three places that call GetLocalizedChar were all functions that were doing the same thing, ie, localizing the digits in a string. Instead of having three separate copies of this similar code, this patch stops exporting GetLocalizedChar and instead exports a utility function that operates on a string. One of the three versions of the code avoided allocating a new string if nothing changes. This new consolidated helper uses the same approach so that the other two places can take advantage of this very likely optimisation. The main motivation to do this was to avoid truncating the sal_UCS4 returned by GetLocalizedChar into a sal_Unicode. The consolidated implementation adds the converted characters with appendUtf32 instead so that if GetLocalizedChar ever returns characters outside of the BMP then it would work. In practice this doesn’t currently happen so it’s not an actual problem. However the commit below makes the compiler warn when sal_UCS4 is truncated, so this patch fixes a truncation that was marked as a TODO in that commit. commit 82f055e5cb35501db06b4996909759f686de9631 Author: Noel Grandin Date: Tue Nov 4 10:12:18 2025 +0200 new loplugin:narrow Change-Id: I628a29520a0896cd0f4c7c2ff131ac763e07925b Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193460 Tested-by: Jenkins Reviewed-by: Noel Grandin --- cppcanvas/source/mtfrenderer/implrenderer.cxx | 20 ++------ editeng/source/editeng/impedit.hxx | 1 - editeng/source/editeng/impedit3.cxx | 18 ++----- include/vcl/svapp.hxx | 8 +++- vcl/source/gdi/sallayout.cxx | 47 +++++++++++++++++++ vcl/source/outdev/text.cxx | 26 ++-------- 6 files changed, 63 insertions(+), 57 deletions(-) diff --git a/cppcanvas/source/mtfrenderer/implrenderer.cxx b/cppcanvas/source/mtfrenderer/implrenderer.cxx index d11f9cdada40..e7b04397507b 100644 --- a/cppcanvas/source/mtfrenderer/implrenderer.cxx +++ b/cppcanvas/source/mtfrenderer/implrenderer.cxx @@ -215,20 +215,6 @@ namespace return Bitmap( aSolid, aMask ); } - - OUString convertToLocalizedNumerals(std::u16string_view rStr, - LanguageType eTextLanguage) - { - OUStringBuffer aBuf(rStr); - for (sal_Int32 i = 0; i < aBuf.getLength(); ++i) - { - sal_Unicode nChar = aBuf[i]; - if (nChar >= '0' && nChar <= '9') - // TODO: are the localized digit surrogates? - aBuf[i] = static_cast(GetLocalizedChar(nChar, eTextLanguage)); - } - return aBuf.makeStringAndClear(); - } } namespace cppcanvas::internal @@ -2438,7 +2424,7 @@ namespace cppcanvas::internal OUString sText = pAct->GetText(); if (rVDev.GetDigitLanguage()) - sText = convertToLocalizedNumerals(sText, rVDev.GetDigitLanguage()); + sText = LocalizeDigitsInString(sText, rVDev.GetDigitLanguage()); const sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex()); @@ -2460,7 +2446,7 @@ namespace cppcanvas::internal OUString sText = pAct->GetText(); if (rVDev.GetDigitLanguage()) - sText = convertToLocalizedNumerals(sText, rVDev.GetDigitLanguage()); + sText = LocalizeDigitsInString(sText, rVDev.GetDigitLanguage()); const sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex()); @@ -2540,7 +2526,7 @@ namespace cppcanvas::internal OUString sText = pAct->GetText(); if (rVDev.GetDigitLanguage()) - sText = convertToLocalizedNumerals(sText, rVDev.GetDigitLanguage()); + sText = LocalizeDigitsInString(sText, rVDev.GetDigitLanguage()); const sal_Int32 nLen = std::min(pAct->GetLen(), pAct->GetText().getLength() - pAct->GetIndex()); diff --git a/editeng/source/editeng/impedit.hxx b/editeng/source/editeng/impedit.hxx index 338145db8973..72eadc1b79ac 100644 --- a/editeng/source/editeng/impedit.hxx +++ b/editeng/source/editeng/impedit.hxx @@ -771,7 +771,6 @@ private: void ImplInitLayoutMode(OutputDevice& rOutDev, sal_Int32 nPara, sal_Int32 nIndex); static LanguageType ImplCalcDigitLang(LanguageType eCurLang); static void ImplInitDigitMode(OutputDevice& rOutDev, LanguageType eLang); - static OUString convertDigits(std::u16string_view rString, sal_Int32 nStt, sal_Int32 nLen, LanguageType eDigitLang); EditPaM ReadText( SvStream& rInput, EditSelection aSel ); EditPaM ReadRTF( SvStream& rInput, EditSelection aSel ); diff --git a/editeng/source/editeng/impedit3.cxx b/editeng/source/editeng/impedit3.cxx index 1298eb314b91..000d959c7b89 100644 --- a/editeng/source/editeng/impedit3.cxx +++ b/editeng/source/editeng/impedit3.cxx @@ -3906,8 +3906,9 @@ void ImpEditEngine::StripAllPortions( OutputDevice& rOutDev, tools::Rectangle aC const Color aTextLineColor(rOutDev.GetTextLineColor()); // Unicode code points conversion according to ctl text numeral setting - aText = convertDigits(aText, nTextStart, nTextLen, - ImplCalcDigitLang(aTmpFont.GetLanguage())); + aText = LocalizeDigitsInString(aText, + ImplCalcDigitLang(aTmpFont.GetLanguage()), + nTextStart, nTextLen); // StripPortions() data callback const DrawPortionInfo aInfo( @@ -4668,19 +4669,6 @@ LanguageType ImpEditEngine::ImplCalcDigitLang(LanguageType eCurLang) return eLang; } -OUString ImpEditEngine::convertDigits(std::u16string_view rString, sal_Int32 nStt, sal_Int32 nLen, LanguageType eDigitLang) -{ - OUStringBuffer aBuf(rString); - for (sal_Int32 nIdx = nStt, nEnd = nStt + nLen; nIdx < nEnd; ++nIdx) - { - sal_Unicode cChar = aBuf[nIdx]; - if (cChar >= '0' && cChar <= '9') - // TODO: are the localized digit surrogates? - aBuf[nIdx] = static_cast(GetLocalizedChar(cChar, eDigitLang)); - } - return aBuf.makeStringAndClear(); -} - // Either sets the digit mode at the output device void ImpEditEngine::ImplInitDigitMode(OutputDevice& rOutDev, LanguageType eCurLang) { diff --git a/include/vcl/svapp.hxx b/include/vcl/svapp.hxx index ef908f61931b..43cb3f9535b0 100644 --- a/include/vcl/svapp.hxx +++ b/include/vcl/svapp.hxx @@ -84,7 +84,13 @@ namespace com::sun::star::awt { class XWindow; } -VCL_DLLPUBLIC sal_UCS4 GetLocalizedChar( sal_UCS4, LanguageType ); +VCL_DLLPUBLIC OUString LocalizeDigitsInString( const OUString&, LanguageType, + sal_Int32 nStart, sal_Int32& nLen ); +inline OUString LocalizeDigitsInString( const OUString& sStr, LanguageType eTextLanguage ) +{ + sal_Int32 nLen = sStr.getLength(); + return LocalizeDigitsInString(sStr, eTextLanguage, 0, nLen); +} enum class SystemWindowFlags { NOAUTOMODE = 0x0001, diff --git a/vcl/source/gdi/sallayout.cxx b/vcl/source/gdi/sallayout.cxx index aa9dca1d6e06..67b52fba8d39 100644 --- a/vcl/source/gdi/sallayout.cxx +++ b/vcl/source/gdi/sallayout.cxx @@ -47,6 +47,8 @@ #define GF_FONTMASK 0xF0000000 #define GF_FONTSHIFT 28 +namespace +{ sal_UCS4 GetLocalizedChar( sal_UCS4 nChar, LanguageType eLang ) { @@ -121,6 +123,51 @@ sal_UCS4 GetLocalizedChar( sal_UCS4 nChar, LanguageType eLang ) return nChar; } +} + +OUString LocalizeDigitsInString( const OUString& sStr, LanguageType eTextLanguage, + sal_Int32 nStart, sal_Int32& nLen ) +{ + sal_Int32 nextPos, nEnd = nStart + nLen; + + for (sal_Int32 i = nStart; i < nEnd; i = nextPos) + { + nextPos = i; + sal_uInt32 nChar = sStr.iterateCodePoints(&nextPos); + + sal_UCS4 nReplacementChar = GetLocalizedChar(nChar, eTextLanguage); + + // The first time we encounter a character that needs to change we’ll make a copy of the + // string so we can return a new modified one + if (nReplacementChar != nChar) + { + // The new string is very likely to have the same length as the old one + OUStringBuffer xTmpStr(sStr.getLength()); + xTmpStr.append(sStr.subView(0, i)); + xTmpStr.appendUtf32(nReplacementChar); + + // Convert the remainder of the range + for (i = nextPos; i < nEnd;) + { + nReplacementChar = GetLocalizedChar(sStr.iterateCodePoints(&i), eTextLanguage); + xTmpStr.appendUtf32(nReplacementChar); + } + + // Add the rest of the string outside of the range + xTmpStr.append(sStr.subView(nEnd)); + + // The length of the string might have changed if GetLocalizedChar converts between BMP + // and surrogate pairs + nLen += xTmpStr.getLength() - sStr.getLength(); + + return xTmpStr.makeStringAndClear(); + } + } + + // Nothing changed so we can just return the original string + return sStr; +} + SalLayout::SalLayout() : mnMinCharPos( -1 ), mnEndCharPos( -1 ), diff --git a/vcl/source/outdev/text.cxx b/vcl/source/outdev/text.cxx index ba190b047cfe..4b1afddcfc14 100644 --- a/vcl/source/outdev/text.cxx +++ b/vcl/source/outdev/text.cxx @@ -994,29 +994,9 @@ vcl::text::ImplLayoutArgs OutputDevice::ImplPrepareLayoutArgs( OUString& rStr, if( meTextLanguage ) //TODO: (mnTextLayoutMode & vcl::text::ComplexTextLayoutFlags::SubstituteDigits) { - // disable character localization when no digits used - const sal_Unicode* pBase = rStr.getStr(); - const sal_Unicode* pStr = pBase + nMinIndex; - const sal_Unicode* pEnd = pBase + nEndIndex; - std::optional xTmpStr; - for( ; pStr < pEnd; ++pStr ) - { - // TODO: are there non-digit localizations? - if( (*pStr >= '0') && (*pStr <= '9') ) - { - // translate characters to local preference - sal_UCS4 cChar = GetLocalizedChar( *pStr, meTextLanguage ); - if( cChar != *pStr ) - { - if (!xTmpStr) - xTmpStr = OUStringBuffer(rStr); - // TODO: are the localized digit surrogates? - (*xTmpStr)[pStr - pBase] = static_cast(cChar); - } - } - } - if (xTmpStr) - rStr = (*xTmpStr).makeStringAndClear(); + sal_Int32 nSubstringLen = nEndIndex - nMinIndex; + rStr = LocalizeDigitsInString(rStr, meTextLanguage, nMinIndex, nSubstringLen); + nEndIndex = nMinIndex + nSubstringLen; } // right align for RTL text, DRAWPOS_REVERSED, RTL window style