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:

Reply via email to