basic/qa/basic_coverage/test_format_function.bas | 17 ++++ basic/source/sbx/sbxscan.cxx | 72 +++++++++--------- include/rtl/string.hxx | 1 include/rtl/stringconcat.hxx | 15 +++ include/rtl/ustring.hxx | 1 sw/source/core/crsr/crstrvl.cxx | 11 +- sw/source/core/fields/authfld.cxx | 4 - sw/source/core/text/porglue.cxx | 8 -- sw/source/core/text/txttab.cxx | 8 -- sw/source/filter/ww8/ww8graf.cxx | 6 - sw/source/filter/ww8/ww8par2.cxx | 5 - sw/source/uibase/docvw/edtwin.cxx | 5 - sw/source/writerfilter/dmapper/GraphicImport.cxx | 7 - sw/source/writerfilter/dmapper/StyleSheetTable.cxx | 7 - sw/source/writerfilter/dmapper/TextEffectsHandler.cxx | 7 - 15 files changed, 93 insertions(+), 81 deletions(-)
New commits: commit d16697fbf6a6653dc631ebd887147afe2bfba37d Author: Mike Kaganski <[email protected]> AuthorDate: Fri Aug 29 03:48:15 2025 +0500 Commit: Xisco Fauli <[email protected]> CommitDate: Mon Sep 1 10:00:16 2025 +0200 tdf#143182: fix string handling in SbxValue::Format When a value is convertible to number, check if the format string is for numbers of text (in SbxValue::Format). If the value is not convertible to number, handle it according to the format string in printfmtstr. Change-Id: I6e11aeffd8dd8581ebc868762944e9d6d9393514 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190348 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> (cherry picked from commit a3597edb52b671e6095f96c8705cd458a50d8799) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190386 Reviewed-by: Xisco Fauli <[email protected]> diff --git a/basic/qa/basic_coverage/test_format_function.bas b/basic/qa/basic_coverage/test_format_function.bas index d1c51fe791a5..f72e504a2379 100644 --- a/basic/qa/basic_coverage/test_format_function.bas +++ b/basic/qa/basic_coverage/test_format_function.bas @@ -21,6 +21,23 @@ Sub verify_testFormat d = "2024-09-16 17:03:30" TestUtil.AssertEqual(Format(d, "YYYY-MM-DD"), "2024-09-16", "Format(d, ""YYYY-MM-DD"")") TestUtil.AssertEqual(Format("2024-09-16 05:03:30 PM", "hh-mm-ss"), "17-03-30", "Format(""2024-09-16 05:03:30 PM"", ""hh-mm-ss"")") + ' A string that can be converted to number, with a text-format string + TestUtil.AssertEqual(Format("001", "foo @ bar"), "foo 001 bar", "Format(""001"", ""foo @ bar"")") + ' A string that cannot be converted to number, with a text-format string + TestUtil.AssertEqual(Format("baz", "foo @ bar"), "foo baz bar", "Format(""baz"", ""foo @ bar"")") + ' Legacy format strings + ' leading '!': get only the first character of the source string + TestUtil.AssertEqual(Format("abc", "! @"), "a", "Format(""abc"", ""! @"")") + ' leading '\': get as many characters from source string, as in format string until the next '\', padding with spaces as needed + TestUtil.AssertEqual(Format("abcdefgh", "S%"), "abcde", "Format(""abcdefgh"", ""S%"")") + TestUtil.AssertEqual(Format("abcdefgh", "S45"), "abcdef", "Format(""abcdefgh"", ""S45"")") + TestUtil.AssertEqual(Format("abc", "S45\"), "abc ", "Format(""abc"", ""S45\"")") + ' leading '&': get the whole source string unmodified + TestUtil.AssertEqual(Format("abc", "& @"), "abc", "Format(""abc"", ""& @"")") + ' non-leading positions + TestUtil.AssertEqual(Format("abc", "@ !"), "abc !", "Format(""abc"", ""@ !"")") + TestUtil.AssertEqual(Format("abc", "1�5"), "abc", "Format(""abc"", ""1�5"")") + TestUtil.AssertEqual(Format("abc", "@ &"), "abc &", "Format(""abc"", ""@ &"")") Exit Sub errorHandler: diff --git a/basic/source/sbx/sbxscan.cxx b/basic/source/sbx/sbxscan.cxx index 546366af5698..df6e704299b8 100644 --- a/basic/source/sbx/sbxscan.cxx +++ b/basic/source/sbx/sbxscan.cxx @@ -299,39 +299,6 @@ void ImpCvtNum( double nNum, short nPrec, OUString& rRes, bool bCoreString ) rRes = rtl::math::doubleToUString(nNum, rtl_math_StringFormat_Automatic, nPrec, cDecimalSep, true); } -// formatted number output - -static void printfmtstr(std::u16string_view rStr, OUString& rRes, std::u16string_view rFmt) -{ - if (rFmt.empty()) - rFmt = u"&"; - - OUStringBuffer aTemp; - auto pStr = rStr.begin(); - auto pFmt = rFmt.begin(); - - switch( *pFmt ) - { - case '!': - if (pStr != rStr.end()) - aTemp.append(*pStr); - break; - case '\': - do - { - aTemp.append(pStr != rStr.end() ? *pStr++ : u' '); - } while (++pFmt != rFmt.end() && *pFmt != '\'); - aTemp.append(pStr != rStr.end() ? *pStr : u' '); - break; - case '&': - default: - aTemp = rStr; - break; - } - rRes = aTemp.makeStringAndClear(); -} - - bool SbxValue::Scan(std::u16string_view rSrc, sal_Int32* pLen) { ErrCode eRes = ERRCODE_NONE; @@ -527,6 +494,40 @@ std::optional<double> GetNumberIntl(const SbxValue& val, OUString& rStrVal, : std::nullopt; } } + +void printfmtstr(const OUString& rStr, OUString& rRes, const OUString& rFmt) +{ + rRes = rStr; + + switch (rFmt.isEmpty() ? '&' : rFmt[0]) + { + case '!': + if (!rStr.isEmpty()) + rRes = rStr.copy(0, 1); + break; + case '\': + if (auto i = rFmt.indexOf('\', 1) + 1, l = i ? i : rFmt.getLength(); + l < rStr.getLength()) + rRes = rStr.copy(0, l); + else if (l > rStr.getLength()) + rRes += OUString::Concat(RepeatedUChar(' ', l - rStr.getLength())); + break; + case '&': + break; // Already assigned the correct value + default: + if (auto formatter = GetFormatter()) + { + sal_uInt32 nIndex; + formatter->PutandConvertEntry( + // [-loplugin:redundantfcast] a temporary is required here + o3tl::temporary(OUString(rFmt)), o3tl::temporary(sal_Int32()), + o3tl::temporary(SvNumFormatType()), nIndex, LANGUAGE_ENGLISH_US, + Application::GetSettings().GetLanguageTag().getLanguageType(), true); + formatter->GetOutputString(rStr, nIndex, rRes, &o3tl::temporary<const Color*>({})); + } + break; + } +} } // namespace void SbxValue::Format( OUString& rRes, const OUString* pFmt ) const @@ -658,7 +659,10 @@ void SbxValue::Format( OUString& rRes, const OUString* pFmt ) const else { pFormatter->PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH_US, eLangType, true); - pFormatter->GetOutputString(*number, nIndex, rRes, &pCol); + if (nType == SvNumFormatType::TEXT && IsString()) + pFormatter->GetOutputString(GetOUString(), nIndex, rRes, &pCol); + else + pFormatter->GetOutputString(*number, nIndex, rRes, &pCol); } #endif } commit 0a5a25ffd4855c418a6c285b1b28b7e5fe504eb2 Author: Mike Kaganski <[email protected]> AuthorDate: Fri Aug 29 01:49:22 2025 +0500 Commit: Xisco Fauli <[email protected]> CommitDate: Mon Sep 1 10:00:08 2025 +0200 Introduce Repeated(U)Char for concatenation of N repeated characters ... and use in sw. Should replace padToLength, where the number of added characters is known in advance. Avoids creation of a string buffer for the task. Change-Id: I90450e37926335d74421c6e38385e4c441e535d9 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190341 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> Signed-off-by: Xisco Fauli <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/190392 diff --git a/include/rtl/string.hxx b/include/rtl/string.hxx index 43a31e92303a..71b91f0f4bbc 100644 --- a/include/rtl/string.hxx +++ b/include/rtl/string.hxx @@ -2722,6 +2722,7 @@ using ::rtl::OStringChar; using ::rtl::Concat2View; using ::rtl::OStringHash; using ::rtl::OStringLiteral; +using RepeatedChar = ::rtl::RepeatedChar_t<char>; #endif #if defined LIBO_INTERNAL_ONLY && !(defined _MSC_VER && _MSC_VER <= 1929 && defined _MANAGED) diff --git a/include/rtl/stringconcat.hxx b/include/rtl/stringconcat.hxx index 216670e0d635..874a370e73f6 100644 --- a/include/rtl/stringconcat.hxx +++ b/include/rtl/stringconcat.hxx @@ -170,6 +170,21 @@ struct ToStringHelper<OUStringChar_> { return addDataHelper(buffer, &literal.c, 1); } }; +// Output a character specified number of times. If the number is less than 1, do nothing. +template <typename C> struct RepeatedChar_t +{ + C c; + sal_Int32 n; + constexpr RepeatedChar_t(C ch, sal_Int32 num) : c(ch), n(std::max(num, sal_Int32(0))) {} +}; + +template <typename C> struct ToStringHelper<RepeatedChar_t<C>> +{ + static std::size_t length(const RepeatedChar_t<C>& str) { return str.n; } + C* operator()(C* buffer, const RepeatedChar_t<C>& str) const + { return std::fill_n(buffer, str.n, str.c); } +}; + /** @internal diff --git a/include/rtl/ustring.hxx b/include/rtl/ustring.hxx index b03978c7d625..bbf0e07fbdbf 100644 --- a/include/rtl/ustring.hxx +++ b/include/rtl/ustring.hxx @@ -3940,6 +3940,7 @@ using ::rtl::OUStringToOString; using ::rtl::OUStringLiteral; using ::rtl::OUStringChar; using ::rtl::Concat2View; +using RepeatedUChar = ::rtl::RepeatedChar_t<sal_Unicode>; #endif #if defined LIBO_INTERNAL_ONLY && !(defined _MSC_VER && _MSC_VER <= 1929 && defined _MANAGED) diff --git a/sw/source/core/crsr/crstrvl.cxx b/sw/source/core/crsr/crstrvl.cxx index 8515e71e6980..169e637b3dbe 100644 --- a/sw/source/core/crsr/crstrvl.cxx +++ b/sw/source/core/crsr/crstrvl.cxx @@ -2545,20 +2545,17 @@ bool SwCursorShell::SetShadowCursorPos( const Point& rPt, SwFillMode eFillMode ) case SwFillMode::TabSpace: case SwFillMode::Space: { - OUStringBuffer sInsert; + OUString sInsert; if (aFPos.eMode == SwFillMode::Space) { - comphelper::string::padToLength(sInsert, sInsert.getLength() + aFPos.nSpaceOnlyCnt, ' '); + sInsert = OUString::Concat(RepeatedUChar(' ', aFPos.nSpaceOnlyCnt)); } else { - if (aFPos.nTabCnt) - comphelper::string::padToLength(sInsert, aFPos.nTabCnt, ' '); - if (aFPos.nSpaceCnt) - comphelper::string::padToLength(sInsert, sInsert.getLength() + aFPos.nSpaceCnt, ' '); + sInsert = RepeatedUChar(' ', aFPos.nTabCnt) + RepeatedUChar(' ', aFPos.nSpaceCnt); } if (!sInsert.isEmpty()) - GetDoc()->getIDocumentContentOperations().InsertString( *m_pCurrentCursor, sInsert.makeStringAndClear()); + GetDoc()->getIDocumentContentOperations().InsertString(*m_pCurrentCursor, sInsert); } [[fallthrough]]; // still need to set orientation case SwFillMode::Margin: diff --git a/sw/source/core/fields/authfld.cxx b/sw/source/core/fields/authfld.cxx index 0a3de0e91bb5..b3cb0a2a4147 100644 --- a/sw/source/core/fields/authfld.cxx +++ b/sw/source/core/fields/authfld.cxx @@ -817,9 +817,7 @@ bool SwAuthorityField::PutValue( const Any& rAny, sal_uInt16 /*nWhichId*/ ) if(!(rAny >>= aParam)) return false; - OUStringBuffer sBuf(+(AUTH_FIELD_END - 1)); - comphelper::string::padToLength(sBuf, (AUTH_FIELD_END - 1), TOX_STYLE_DELIMITER); - OUString sToSet(sBuf.makeStringAndClear()); + OUString sToSet = OUString::Concat(RepeatedUChar(TOX_STYLE_DELIMITER, AUTH_FIELD_END - 1)); for (const PropertyValue& rParam : aParam) { const sal_Int32 nFound = lcl_Find(rParam.Name); diff --git a/sw/source/core/text/porglue.cxx b/sw/source/core/text/porglue.cxx index cd5eaf2643b5..3c2ff67a4f3d 100644 --- a/sw/source/core/text/porglue.cxx +++ b/sw/source/core/text/porglue.cxx @@ -54,9 +54,7 @@ bool SwGluePortion::GetExpText( const SwTextSizeInfo &rInf, OUString &rText ) co if( GetLen() && rInf.OnWin() && rInf.GetOpt().IsBlank() && rInf.IsNoSymbol() ) { - OUStringBuffer aBuf(GetLen().get()); - comphelper::string::padToLength(aBuf, sal_Int32(GetLen()), CH_BULLET); - rText = aBuf.makeStringAndClear(); + rText = OUString::Concat(RepeatedUChar(CH_BULLET, GetLen().get())); return true; } return false; @@ -70,9 +68,7 @@ void SwGluePortion::Paint( const SwTextPaintInfo &rInf ) const if( rInf.GetFont()->IsPaintBlank() ) { const sal_Int32 nCount = GetFixWidth() / sal_Int32(GetLen()); - OUStringBuffer aBuf(nCount); - comphelper::string::padToLength(aBuf, nCount, ' '); - OUString aText(aBuf.makeStringAndClear()); + OUString aText(OUString::Concat(RepeatedUChar(' ', nCount))); SwTextPaintInfo aInf( rInf, &aText ); aInf.DrawText(*this, TextFrameIndex(aText.getLength()), true); } diff --git a/sw/source/core/text/txttab.cxx b/sw/source/core/text/txttab.cxx index 8511571ac753..22502f918a6d 100644 --- a/sw/source/core/text/txttab.cxx +++ b/sw/source/core/text/txttab.cxx @@ -628,9 +628,7 @@ void SwTabPortion::Paint( const SwTextPaintInfo &rInf ) const { // Always with kerning, also on printer! sal_Int32 nChar = Width() / nCharWidth; - OUStringBuffer aBuf(nChar); - comphelper::string::padToLength(aBuf, nChar, ' '); - rInf.DrawText(aBuf.makeStringAndClear(), *this, TextFrameIndex(0), + rInf.DrawText(OUString::Concat(RepeatedUChar(' ', nChar)), *this, TextFrameIndex(0), TextFrameIndex(nChar), true); } } @@ -650,9 +648,7 @@ void SwTabPortion::Paint( const SwTextPaintInfo &rInf ) const sal_Int32 nChar = Width() / nCharWidth; if ( m_cFill == '_' ) ++nChar; // to avoid gaps - OUStringBuffer aBuf(nChar); - comphelper::string::padToLength(aBuf, nChar, m_cFill); - rInf.DrawText(aBuf.makeStringAndClear(), *this, TextFrameIndex(0), + rInf.DrawText(OUString::Concat(RepeatedUChar(m_cFill, nChar)), *this, TextFrameIndex(0), TextFrameIndex(nChar), true); } } diff --git a/sw/source/filter/ww8/ww8graf.cxx b/sw/source/filter/ww8/ww8graf.cxx index 377bf099aa98..18d16e4ea5b0 100644 --- a/sw/source/filter/ww8/ww8graf.cxx +++ b/sw/source/filter/ww8/ww8graf.cxx @@ -692,9 +692,9 @@ void SwWW8ImplReader::InsertAttrsAsDrawingAttrs(WW8_CP nStartCp, WW8_CP nEndCp, if (!bBadSelection) { sal_Int32 nCount = nTextStart - nStartReplace; - OUStringBuffer sTemp(nCount); - comphelper::string::padToLength(sTemp, nCount, cReplaceSymbol); - m_pDrawEditEngine->QuickInsertText(sTemp.makeStringAndClear(), aReplaceSel); + m_pDrawEditEngine->QuickInsertText( + OUString::Concat(RepeatedUChar(cReplaceSymbol, nCount)), + aReplaceSel); } } } diff --git a/sw/source/filter/ww8/ww8par2.cxx b/sw/source/filter/ww8/ww8par2.cxx index 9708826f9773..46c493b84425 100644 --- a/sw/source/filter/ww8/ww8par2.cxx +++ b/sw/source/filter/ww8/ww8par2.cxx @@ -624,9 +624,8 @@ void SwWW8ImplReader::SetAnlvStrings(SwNumFormat &rNum, int nLevel, WW8_ANLV con if( bListSymbol ) { // use cBulletChar for correct mapping on MAC - sText.setLength(0); - comphelper::string::padToLength(sText, rAV.cbTextBefore - + rAV.cbTextAfter, cBulletChar); + sText = OUString::Concat( + RepeatedUChar(cBulletChar, rAV.cbTextBefore + rAV.cbTextAfter)); } } } diff --git a/sw/source/uibase/docvw/edtwin.cxx b/sw/source/uibase/docvw/edtwin.cxx index 8ae5d8a38e41..ea4681c94c31 100644 --- a/sw/source/uibase/docvw/edtwin.cxx +++ b/sw/source/uibase/docvw/edtwin.cxx @@ -2687,10 +2687,7 @@ KEYINPUT_CHECKTABLE_INSDEL: } else { - OUStringBuffer aBuf(m_aInBuffer); - comphelper::string::padToLength(aBuf, - m_aInBuffer.getLength() + aKeyEvent.GetRepeat() + 1, aCh); - m_aInBuffer = aBuf.makeStringAndClear(); + m_aInBuffer += OUString::Concat(RepeatedUChar(aCh, aKeyEvent.GetRepeat() + 1)); bool delayFlush = Application::AnyInput( VclInputFlags::KEYBOARD ); bFlushBuffer = !delayFlush; if( delayFlush ) diff --git a/sw/source/writerfilter/dmapper/GraphicImport.cxx b/sw/source/writerfilter/dmapper/GraphicImport.cxx index 5b2f02c4b15c..b26ba56f717e 100644 --- a/sw/source/writerfilter/dmapper/GraphicImport.cxx +++ b/sw/source/writerfilter/dmapper/GraphicImport.cxx @@ -787,11 +787,8 @@ void GraphicImport::lcl_attribute(Id nName, const Value& rValue) case NS_ooxml::LN_CT_Anchor_wp14_anchorId: case NS_ooxml::LN_CT_Inline_wp14_anchorId: { - OUStringBuffer aBuffer = OUString::number(nIntValue, 16); - OUStringBuffer aString; - comphelper::string::padToLength(aString, 8 - aBuffer.getLength(), '0'); - aString.append(aBuffer.getStr()); - m_pImpl->m_sAnchorId = aString.makeStringAndClear().toAsciiUpperCase(); + auto aBuffer = OUString::number(nIntValue, 16).toAsciiUpperCase(); + m_pImpl->m_sAnchorId = RepeatedUChar('0', 8 - aBuffer.length) + aBuffer; } break; case NS_ooxml::LN_CT_Point2D_x: diff --git a/sw/source/writerfilter/dmapper/StyleSheetTable.cxx b/sw/source/writerfilter/dmapper/StyleSheetTable.cxx index 9f5fe3dd9955..10c271e4fae6 100644 --- a/sw/source/writerfilter/dmapper/StyleSheetTable.cxx +++ b/sw/source/writerfilter/dmapper/StyleSheetTable.cxx @@ -626,13 +626,10 @@ void StyleSheetTable::lcl_sprm(Sprm & rSprm) case NS_ooxml::LN_CT_Style_rsid: { // We want the rsid as a hex string, but always with the length of 8. - OUStringBuffer aBuf = OUString::number(nIntValue, 16); - OUStringBuffer aStr; - comphelper::string::padToLength(aStr, 8 - aBuf.getLength(), '0'); - aStr.append(aBuf.getStr()); + auto aBuf = OUString::number(nIntValue, 16); aValue.Name = "rsid"; - aValue.Value <<= aStr.makeStringAndClear(); + aValue.Value <<= OUString(RepeatedUChar('0', 8 - aBuf.length) + aBuf); } break; case NS_ooxml::LN_CT_Style_qFormat: diff --git a/sw/source/writerfilter/dmapper/TextEffectsHandler.cxx b/sw/source/writerfilter/dmapper/TextEffectsHandler.cxx index 38fddf9ce006..c6b5c250d23f 100644 --- a/sw/source/writerfilter/dmapper/TextEffectsHandler.cxx +++ b/sw/source/writerfilter/dmapper/TextEffectsHandler.cxx @@ -558,11 +558,8 @@ void TextEffectsHandler::lcl_attribute(Id aName, const Value& aValue) break; case NS_ooxml::LN_CT_SRgbColor_val: { - OUString aBuffer = OUString::number(aValue.getInt(), 16); - OUStringBuffer aString; - comphelper::string::padToLength(aString, 6 - aBuffer.getLength(), '0'); - aString.append(aBuffer.getStr()); - mpGrabBagStack->addString(u"val"_ustr, aString.makeStringAndClear().toAsciiUpperCase()); + auto num = OUString::number(aValue.getInt(), 16).toAsciiUpperCase(); + mpGrabBagStack->addString(u"val"_ustr, RepeatedUChar('0', 6 - num.length) + num); } break; case NS_ooxml::LN_CT_Shadow_blurRad:
