Consolidate calls to GetLocalizedChar into one helper function

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 82f055e5cb
    Author: Noel Grandin <noel.grandin@collabora.co.uk>
    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 <noel.grandin@collabora.co.uk>
This commit is contained in:
Neil Roberts
2025-11-05 19:21:07 +01:00
committed by Noel Grandin
parent 8278f03f9e
commit 117d9624a3
6 changed files with 63 additions and 57 deletions
+3 -17
View File
@@ -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<sal_Unicode>(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());
-1
View File
@@ -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 );
+3 -15
View File
@@ -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<sal_Unicode>(GetLocalizedChar(cChar, eDigitLang));
}
return aBuf.makeStringAndClear();
}
// Either sets the digit mode at the output device
void ImpEditEngine::ImplInitDigitMode(OutputDevice& rOutDev, LanguageType eCurLang)
{
+7 -1
View File
@@ -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,
+47
View File
@@ -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 ),
+3 -23
View File
@@ -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<OUStringBuffer> 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<sal_Unicode>(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