include/o3tl/numeric.hxx                           |    8 +-
 sax/source/tools/fastserializer.cxx                |    2 
 scripting/source/stringresource/stringresource.cxx |   21 +------
 sdext/source/pdfimport/pdfparse/pdfentries.cxx     |   19 +------
 svgio/inc/svgtools.hxx                             |    1 
 svgio/source/svgreader/svgtools.cxx                |   57 ++++-----------------
 svtools/source/svhtml/parhtml.cxx                  |    8 +-
 svtools/source/svrtf/parrtf.cxx                    |   20 +------
 sw/source/filter/html/parcss1.cxx                  |   29 ++--------
 vcl/source/filter/idxf/dxfreprd.cxx                |   26 ++-------
 10 files changed, 49 insertions(+), 142 deletions(-)

New commits:
commit e18a815190140dd959ade58abf1b9311af10057e
Author:     Mike Kaganski <[email protected]>
AuthorDate: Sat Feb 14 14:30:23 2026 +0500
Commit:     Mike Kaganski <[email protected]>
CommitDate: Sat Feb 14 11:40:50 2026 +0100

    Use rtl::isAscii(Hex)Digit and o3tl::convertToHex
    
    Since some code needs flexibility in error handling, where hardcoded
    error value of -1 wasn't handy, o3tl::convertToHex was changed to
    accept the error value as its template parameter.
    
    Change-Id: Ifc771cc8fa29a801d4556dea969fc3ea7e620df5
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/199367
    Reviewed-by: Mike Kaganski <[email protected]>
    Tested-by: Jenkins

diff --git a/include/o3tl/numeric.hxx b/include/o3tl/numeric.hxx
index 3736d60b60d1..87da173329a4 100644
--- a/include/o3tl/numeric.hxx
+++ b/include/o3tl/numeric.hxx
@@ -23,7 +23,7 @@ struct divide_by_zero final : public std::runtime_error
     }
 };
 
-template <o3tl::integral T, o3tl::integral U>
+template <o3tl::integral T, T error = T(-1), o3tl::integral U>
 constexpr T convertToHex(U aChar)
 {
     if (aChar >= '0' && aChar <= '9')
@@ -32,13 +32,13 @@ constexpr T convertToHex(U aChar)
         return T(aChar - 'a' + 10);
     else if (aChar >= 'A' && aChar <= 'F')
         return T(aChar - 'A' + 10);
-    return T(-1);
+    return error;
 }
 
-template <o3tl::integral T, o3tl::integral U>
+template <o3tl::integral T, T error = T(-1), o3tl::integral U>
 constexpr T convertToHex(U cHigh, U cLow)
 {
-    return (o3tl::convertToHex<T>(cHigh) << 4) | o3tl::convertToHex<T>(cLow);
+    return (o3tl::convertToHex<T, error>(cHigh) << 4) | o3tl::convertToHex<T, 
error>(cLow);
 }
 
 template <o3tl::integral T>
diff --git a/sax/source/tools/fastserializer.cxx 
b/sax/source/tools/fastserializer.cxx
index dcd170aece6d..54039859539a 100644
--- a/sax/source/tools/fastserializer.cxx
+++ b/sax/source/tools/fastserializer.cxx
@@ -135,7 +135,7 @@ namespace sax_fastparser {
 
     static bool isHexDigit( char c )
     {
-        return ('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c 
&& c <= 'f');
+        return rtl::isAsciiHexDigit(static_cast<unsigned char>(c));
     }
 
     void FastSaxSerializer::write( const char* pStr, sal_Int32 nLen, bool 
bEscape )
diff --git a/scripting/source/stringresource/stringresource.cxx 
b/scripting/source/stringresource/stringresource.cxx
index ec304ffc1d0b..a403f59c1b6d 100644
--- a/scripting/source/stringresource/stringresource.cxx
+++ b/scripting/source/stringresource/stringresource.cxx
@@ -33,6 +33,7 @@
 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
 
 #include <osl/diagnose.h>
+#include <o3tl/numeric.hxx>
 #include <o3tl/string_view.hxx>
 #include <rtl/ref.hxx>
 #include <rtl/tencinfo.h>
@@ -1635,20 +1636,6 @@ static void skipWhites( const sal_Unicode* pBuf, 
sal_Int32 nLen, sal_Int32& ri )
     }
 }
 
-static bool isHexDigit( sal_Unicode c, sal_uInt16& nDigitVal )
-{
-    bool bRet = true;
-    if( c >= '0' && c <= '9' )
-        nDigitVal = c - '0';
-    else if( c >= 'a' && c <= 'f' )
-        nDigitVal = c - 'a' + 10;
-    else if( c >= 'A' && c <= 'F' )
-        nDigitVal = c - 'A' + 10;
-    else
-        bRet = false;
-    return bRet;
-}
-
 static sal_Unicode getEscapeChar( const sal_Unicode* pBuf, sal_Int32 nLen, 
sal_Int32& ri )
 {
     sal_Int32 i = ri;
@@ -1681,9 +1668,11 @@ static sal_Unicode getEscapeChar( const sal_Unicode* 
pBuf, sal_Int32 nLen, sal_I
 
             // Process hex digits
             sal_Int32 nDigitCount = 0;
-            sal_uInt16 nDigitVal;
-            while( i < nLen && isHexDigit( pBuf[i], nDigitVal ) )
+            while (i < nLen)
             {
+                int nDigitVal = o3tl::convertToHex<int>(pBuf[i]);
+                if (nDigitVal < 0)
+                    break;
                 cRet = 16 * cRet + nDigitVal;
 
                 nDigitCount++;
diff --git a/sdext/source/pdfimport/pdfparse/pdfentries.cxx 
b/sdext/source/pdfimport/pdfparse/pdfentries.cxx
index 6f781149c3e8..d000a18dcea3 100644
--- a/sdext/source/pdfimport/pdfparse/pdfentries.cxx
+++ b/sdext/source/pdfimport/pdfparse/pdfentries.cxx
@@ -22,6 +22,7 @@
 
 #include <comphelper/hash.hxx>
 
+#include <o3tl/numeric.hxx>
 #include <rtl/strbuf.hxx>
 #include <rtl/ustring.hxx>
 #include <rtl/ustrbuf.hxx>
@@ -163,22 +164,8 @@ OUString PDFName::getFilteredName() const
     {
         if( (i < nLen - 3) && pStr[i] == '#' )
         {
-            char rResult = 0;
-            i++;
-            if( pStr[i] >= '0' && pStr[i] <= '9' )
-                rResult = char( pStr[i]-'0' ) << 4;
-            else if( pStr[i] >= 'a' && pStr[i] <= 'f' )
-                rResult = char( pStr[i]-'a' + 10 ) << 4;
-            else if( pStr[i] >= 'A' && pStr[i] <= 'F' )
-                rResult = char( pStr[i]-'A' + 10 ) << 4;
-            i++;
-            if( pStr[i] >= '0' && pStr[i] <= '9' )
-                rResult |= char( pStr[i]-'0' );
-            else if( pStr[i] >= 'a' && pStr[i] <= 'f' )
-                rResult |= char( pStr[i]-'a' + 10 );
-            else if( pStr[i] >= 'A' && pStr[i] <= 'F' )
-                rResult |= char( pStr[i]-'A' + 10 );
-            aFilter.append( rResult );
+            aFilter.append(o3tl::convertToHex<char, 0>(pStr[i + 1], pStr[i + 
2]));
+            i += 2;
         }
         else
             aFilter.append( pStr[i] );
diff --git a/svgio/inc/svgtools.hxx b/svgio/inc/svgtools.hxx
index dfeb12d9ea49..7bcea2ccedf1 100644
--- a/svgio/inc/svgtools.hxx
+++ b/svgio/inc/svgtools.hxx
@@ -105,7 +105,6 @@ namespace svgio::svgreader
         SvgUnit readUnit(std::u16string_view rCandidate, sal_Int32& nPos, 
const sal_Int32 nLen);
         bool readNumberAndUnit(std::u16string_view rCandidate, sal_Int32& 
nPos, SvgNumber& aNum, const sal_Int32 nLen);
         bool readAngle(std::u16string_view rCandidate, sal_Int32& nPos, 
double& fAngle, const sal_Int32 nLen);
-        sal_Int32 read_hex(sal_Unicode aChar);
         bool match_colorKeyword(basegfx::BColor& rColor, const OUString& 
rName);
         bool read_color(const OUString& rCandidate, basegfx::BColor& rColor, 
SvgNumber& rOpacity);
         basegfx::B2DRange readViewBox(std::u16string_view rCandidate, 
InfoProvider const & rInfoProvider);
diff --git a/svgio/source/svgreader/svgtools.cxx 
b/svgio/source/svgreader/svgtools.cxx
index 11b6aa74db0d..f6078bf49010 100644
--- a/svgio/source/svgreader/svgtools.cxx
+++ b/svgio/source/svgreader/svgtools.cxx
@@ -20,7 +20,9 @@
 #include <svgtools.hxx>
 #include <sal/log.hxx>
 #include <tools/color.hxx>
+#include <rtl/character.hxx>
 #include <rtl/math.hxx>
+#include <o3tl/numeric.hxx>
 #include <o3tl/string_view.hxx>
 #include <basegfx/matrix/b2dhommatrix.hxx>
 #include <basegfx/matrix/b2dhommatrixtools.hxx>
@@ -340,21 +342,9 @@ namespace svgio::svgreader
 
         void copyHex(std::u16string_view rCandidate, sal_Int32& nPos, 
OUStringBuffer& rTarget, const sal_Int32 nLen)
         {
-            bool bOnHex(true);
-
-            while(bOnHex && nPos < nLen)
+            for (; nPos < nLen && rtl::isAsciiHexDigit(rCandidate[nPos]); 
++nPos)
             {
-                const sal_Unicode aChar(rCandidate[nPos]);
-
-                bOnHex = ('0' <= aChar && '9' >= aChar)
-                    || ('A' <= aChar && 'F' >= aChar)
-                    || ('a' <= aChar && 'f' >= aChar);
-
-                if(bOnHex)
-                {
-                    rTarget.append(aChar);
-                    nPos++;
-                }
+                rTarget.append(rCandidate[nPos]);
             }
         }
 
@@ -617,27 +607,6 @@ namespace svgio::svgreader
             return false;
         }
 
-        sal_Int32 read_hex(sal_Unicode nChar)
-        {
-            if(nChar >= '0' && nChar <= '9')
-            {
-                return nChar - u'0';
-            }
-            else if(nChar >= 'A' && nChar <= 'F')
-            {
-                return 10 + sal_Int32(nChar - u'A');
-            }
-            else if(nChar >= 'a' && nChar <= 'f')
-            {
-                return 10 + sal_Int32(nChar - u'a');
-            }
-            else
-            {
-                // error
-                return 0;
-            }
-        }
-
         bool match_colorKeyword(basegfx::BColor& rColor, const OUString& rName)
         {
             auto const aResult = 
aColorTokenMapperList.find(rName.toAsciiLowerCase().trim());
@@ -673,9 +642,9 @@ namespace svgio::svgreader
 
                     if(3 == nLength)
                     {
-                        const sal_Int32 nR(read_hex(aNum[0]));
-                        const sal_Int32 nG(read_hex(aNum[1]));
-                        const sal_Int32 nB(read_hex(aNum[2]));
+                        const sal_Int32 nR(o3tl::convertToHex<sal_Int32, 
0>(aNum[0]));
+                        const sal_Int32 nG(o3tl::convertToHex<sal_Int32, 
0>(aNum[1]));
+                        const sal_Int32 nB(o3tl::convertToHex<sal_Int32, 
0>(aNum[2]));
 
                         rColor.setRed((nR | (nR << 4)) * fFactor);
                         rColor.setGreen((nG | (nG << 4)) * fFactor);
@@ -685,12 +654,12 @@ namespace svgio::svgreader
                     }
                     else if(6 == nLength)
                     {
-                        const sal_Int32 nR1(read_hex(aNum[0]));
-                        const sal_Int32 nR2(read_hex(aNum[1]));
-                        const sal_Int32 nG1(read_hex(aNum[2]));
-                        const sal_Int32 nG2(read_hex(aNum[3]));
-                        const sal_Int32 nB1(read_hex(aNum[4]));
-                        const sal_Int32 nB2(read_hex(aNum[5]));
+                        const sal_Int32 nR1(o3tl::convertToHex<sal_Int32, 
0>(aNum[0]));
+                        const sal_Int32 nR2(o3tl::convertToHex<sal_Int32, 
0>(aNum[1]));
+                        const sal_Int32 nG1(o3tl::convertToHex<sal_Int32, 
0>(aNum[2]));
+                        const sal_Int32 nG2(o3tl::convertToHex<sal_Int32, 
0>(aNum[3]));
+                        const sal_Int32 nB1(o3tl::convertToHex<sal_Int32, 
0>(aNum[4]));
+                        const sal_Int32 nB2(o3tl::convertToHex<sal_Int32, 
0>(aNum[5]));
 
                         rColor.setRed((nR2 | (nR1 << 4)) * fFactor);
                         rColor.setGreen((nG2 | (nG1 << 4)) * fFactor);
diff --git a/svtools/source/svhtml/parhtml.cxx 
b/svtools/source/svhtml/parhtml.cxx
index 0356d7c1a645..fb1a84b13ee4 100644
--- a/svtools/source/svhtml/parhtml.cxx
+++ b/svtools/source/svhtml/parhtml.cxx
@@ -18,6 +18,7 @@
  */
 
 #include <comphelper/string.hxx>
+#include <o3tl/numeric.hxx>
 #include <o3tl/safeint.hxx>
 #include <o3tl/string_view.hxx>
 #include <tools/stream.hxx>
@@ -163,7 +164,7 @@ void HTMLOption::GetColor( Color& rColor ) const
     DBG_ASSERT( (nToken>=HtmlOptionId::COLOR_START && 
nToken<HtmlOptionId::COLOR_END) || nToken==HtmlOptionId::SIZE,
         "GetColor: Option is not a color." );
 
-    OUString aTmp(aValue.toAsciiLowerCase());
+    OUString aTmp(aValue);
     sal_uInt32 nColor = SAL_MAX_UINT32;
     if (!aTmp.isEmpty() && aTmp[0] != '#')
         nColor = GetHTMLColor(aTmp);
@@ -184,10 +185,7 @@ void HTMLOption::GetColor( Color& rColor ) const
                     c = nPos<aTmp.getLength() ? aTmp[nPos++] : '0';
             }
             nColor *= 16;
-            if( c >= '0' && c <= '9' )
-                nColor += (c - '0');
-            else if( c >= 'a' && c <= 'f' )
-                nColor += (c + 0xa - 'a');
+            nColor += o3tl::convertToHex<sal_uInt32, 0>(c);
         }
     }
 
diff --git a/svtools/source/svrtf/parrtf.cxx b/svtools/source/svrtf/parrtf.cxx
index 82d69f7881ac..a7a9a1bbc01c 100644
--- a/svtools/source/svrtf/parrtf.cxx
+++ b/svtools/source/svrtf/parrtf.cxx
@@ -22,6 +22,7 @@
 
 #include <comphelper/scopeguard.hxx>
 
+#include <o3tl/numeric.hxx>
 #include <rtl/character.hxx>
 #include <rtl/strbuf.hxx>
 #include <rtl/tencinfo.h>
@@ -295,21 +296,10 @@ int SvRTFParser::GetNextToken_()
 sal_Unicode SvRTFParser::GetHexValue()
 {
     // collect Hex values
-    int n;
-    sal_Unicode nHexVal = 0;
-
-    for( n = 0; n < 2; ++n )
-    {
-        nHexVal *= 16;
-        nNextCh = GetNextChar();
-        if( nNextCh >= '0' && nNextCh <= '9' )
-            nHexVal += (nNextCh - 48);
-        else if( nNextCh >= 'a' && nNextCh <= 'f' )
-            nHexVal += (nNextCh - 87);
-        else if( nNextCh >= 'A' && nNextCh <= 'F' )
-            nHexVal += (nNextCh - 55);
-    }
-    return nHexVal;
+    sal_uInt32 nHi = GetNextChar();
+    sal_uInt32 nLo = GetNextChar();
+    nNextCh = nLo;
+    return o3tl::convertToHex<sal_Unicode, 0>(nHi, nLo);
 }
 
 void SvRTFParser::ScanText()
diff --git a/sw/source/filter/html/parcss1.cxx 
b/sw/source/filter/html/parcss1.cxx
index a32de72ab52f..6c6ec66c9025 100644
--- a/sw/source/filter/html/parcss1.cxx
+++ b/sw/source/filter/html/parcss1.cxx
@@ -17,6 +17,9 @@
  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
  */
 
+#include <sal/config.h>
+
+#include <o3tl/numeric.hxx>
 #include <o3tl/string_view.hxx>
 #include <o3tl/unit_conversion.hxx>
 #include <osl/diagnose.h>
@@ -311,11 +314,7 @@ CSS1Token CSS1Parser::GetNextToken()
                 do {
                     sTmpBuffer.append( m_cNextCh );
                     m_cNextCh = GetNextChar();
-                } while( sTmpBuffer.getLength() < 7 &&
-                         ( ('0'<=m_cNextCh && '9'>=m_cNextCh) ||
-                           ('A'<=m_cNextCh && 'F'>=m_cNextCh) ||
-                           ('a'<=m_cNextCh && 'f'>=m_cNextCh) ) &&
-                         !IsEOF() );
+                } while (sTmpBuffer.getLength() < 7 && 
rtl::isAsciiHexDigit(m_cNextCh) && !IsEOF());
 
                 if( sTmpBuffer.getLength()==6 )
                 {
@@ -510,11 +509,7 @@ CSS1Token CSS1Parser::GetNextToken()
                 do {
                     sTmpBuffer.append( m_cNextCh );
                     m_cNextCh = GetNextChar();
-                } while( sTmpBuffer.getLength() < 9 &&
-                         ( ('0'<=m_cNextCh && '9'>=m_cNextCh) ||
-                           ('A'<=m_cNextCh && 'F'>=m_cNextCh) ||
-                           ('a'<=m_cNextCh && 'f'>=m_cNextCh) ) &&
-                         !IsEOF() );
+                } while (sTmpBuffer.getLength() < 9 && 
rtl::isAsciiHexDigit(m_cNextCh) && !IsEOF());
 
                 if( sTmpBuffer.getLength()==6 || sTmpBuffer.getLength()==3 )
                 {
@@ -587,10 +582,7 @@ CSS1Token CSS1Parser::GetNextToken()
                     sTmpBuffer.append( m_cNextCh );
                     if( bHexColor )
                     {
-                        bHexColor =  sTmpBuffer.getLength()<7 &&
-                                     ( ('0'<=m_cNextCh && '9'>=m_cNextCh) ||
-                                       ('A'<=m_cNextCh && 'F'>=m_cNextCh) ||
-                                       ('a'<=m_cNextCh && 'f'>=m_cNextCh) );
+                        bHexColor = sTmpBuffer.getLength() < 7 && 
rtl::isAsciiHexDigit(m_cNextCh);
                     }
                     m_cNextCh = GetNextChar();
                 } while( (rtl::isAsciiAlphanumeric(m_cNextCh) ||
@@ -1325,14 +1317,7 @@ bool CSS1Expression::GetColor( Color &rColor ) const
             {
                 sal_Unicode c = (i<aValue.getLength() ? aValue[i]
                                                          : '0' );
-                if( c >= '0' && c <= '9' )
-                    c -= 48;
-                else if( c >= 'A' && c <= 'F' )
-                    c -= 55;
-                else if( c >= 'a' && c <= 'f' )
-                    c -= 87;
-                else
-                    c = 16;
+                c = o3tl::convertToHex<sal_Unicode, 16>(c);
 
                 nColor *= 16;
                 if( c<16 )
diff --git a/vcl/source/filter/idxf/dxfreprd.cxx 
b/vcl/source/filter/idxf/dxfreprd.cxx
index 7124e296e303..f2d61bedadef 100644
--- a/vcl/source/filter/idxf/dxfreprd.cxx
+++ b/vcl/source/filter/idxf/dxfreprd.cxx
@@ -23,6 +23,7 @@
 
 #include "dxfreprd.hxx"
 #include <osl/nlsupport.h>
+#include <rtl/character.hxx>
 #include <unotools/defaultencoding.hxx>
 #include <unotools/wincodepage.hxx>
 
@@ -423,17 +424,6 @@ void DXFRepresentation::CalcBoundingBox(const DXFEntities 
& rEntities,
     mbInCalc = false;
 }
 
-namespace {
-    bool lcl_isDec(sal_Unicode ch)
-    {
-        return ch >= L'0' && ch <= L'9';
-    }
-    bool lcl_isHex(sal_Unicode ch)
-    {
-        return lcl_isDec(ch) || (ch >= L'A' && ch <= L'F') || (ch >= L'a' && 
ch <= L'f');
-    }
-}
-
 OUString DXFRepresentation::ToOUString(std::string_view s) const
 {
     OUString result = OStringToOUString(s, getTextEncoding(),
@@ -450,9 +440,9 @@ OUString DXFRepresentation::ToOUString(std::string_view s) 
const
     sal_Int32 pos = result.indexOf("%%"); // %%nnn, where nnn - 3-digit 
decimal ASCII code
     while (pos != -1 && pos <= result.getLength() - 5) {
         OUString asciiNum = result.copy(pos + 2, 3);
-        if (lcl_isDec(asciiNum[0]) &&
-            lcl_isDec(asciiNum[1]) &&
-            lcl_isDec(asciiNum[2]))
+        if (rtl::isAsciiDigit(asciiNum[0]) &&
+            rtl::isAsciiDigit(asciiNum[1]) &&
+            rtl::isAsciiDigit(asciiNum[2]))
         {
             char ch = static_cast<char>(asciiNum.toUInt32());
             OUString codePt(&ch, 1, mEnc);
@@ -464,10 +454,10 @@ OUString DXFRepresentation::ToOUString(std::string_view 
s) const
     pos = result.indexOf("\U+"); // \U+XXXX, where XXXX - 4-digit hex unicode
     while (pos != -1 && pos <= result.getLength() - 7) {
         OUString codePtNum = result.copy(pos + 3, 4);
-        if (lcl_isHex(codePtNum[0]) &&
-            lcl_isHex(codePtNum[1]) &&
-            lcl_isHex(codePtNum[2]) &&
-            lcl_isHex(codePtNum[3]))
+        if (rtl::isAsciiHexDigit(codePtNum[0]) &&
+            rtl::isAsciiHexDigit(codePtNum[1]) &&
+            rtl::isAsciiHexDigit(codePtNum[2]) &&
+            rtl::isAsciiHexDigit(codePtNum[3]))
         {
             OUString codePt(static_cast<sal_Unicode>(codePtNum.toUInt32(16)));
             result = result.replaceAll(result.subView(pos, 7), codePt, pos);

Reply via email to