Reland "Cleanup some numeric code.""

This reverts commit 0569ab0b11b723d9bca4ddd642b0cf8828c4bdd1.

This changes the various comparisons of char >= '0' && char <= '9' and
char < '0' || char > '9' to use std::isdigit checks. It also cleans up
a handful of hex to digit conversions to call one common method.

R=tsepez@chromium.org

Review URL: https://codereview.chromium.org/1449873003 .
diff --git a/BUILD.gn b/BUILD.gn
index ec16d1b..f1583ab 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -731,6 +731,7 @@
     "core/src/fxcrt/fx_basic_memmgr_unittest.cpp",
     "core/src/fxcrt/fx_basic_wstring_unittest.cpp",
     "core/src/fxcrt/fx_bidi_unittest.cpp",
+    "core/src/fxcrt/fx_extension_unittest.cpp",
     "core/src/fxcrt/fx_system_unittest.cpp",
     "third_party/base/nonstd_unique_ptr_unittest.cpp",
   ]
diff --git a/core/include/fxcrt/fx_ext.h b/core/include/fxcrt/fx_ext.h
index c24955f..f13d37c 100644
--- a/core/include/fxcrt/fx_ext.h
+++ b/core/include/fxcrt/fx_ext.h
@@ -7,11 +7,10 @@
 #ifndef CORE_INCLUDE_FXCRT_FX_EXT_H_
 #define CORE_INCLUDE_FXCRT_FX_EXT_H_
 
-#include "fx_system.h"
+#include <cctype>
+#include <cwctype>
 
-#ifdef __cplusplus
-extern "C" {
-#endif
+#include "fx_system.h"
 
 FX_FLOAT FXSYS_tan(FX_FLOAT a);
 FX_FLOAT FXSYS_logb(FX_FLOAT b, FX_FLOAT x);
@@ -38,6 +37,25 @@
   return ch < 'a' || ch > 'z' ? ch : (ch - 0x20);
 }
 
+inline int FXSYS_toHexDigit(const FX_CHAR c) {
+  if (!std::isxdigit(c))
+    return 0;
+  char upchar = std::toupper(c);
+  return upchar > '9' ? upchar - 'A' + 10 : upchar - '0';
+}
+
+inline int FXSYS_toDecimalDigit(const FX_CHAR c) {
+  if (!std::isdigit(c))
+    return 0;
+  return c - '0';
+}
+
+inline int FXSYS_toDecimalDigitWide(const FX_WCHAR c) {
+  if (!std::iswdigit(c))
+    return 0;
+  return c - L'0';
+}
+
 FX_DWORD FX_HashCode_String_GetA(const FX_CHAR* pStr,
                                  int32_t iLength,
                                  FX_BOOL bIgnoreCase = FALSE);
@@ -45,13 +63,6 @@
                                  int32_t iLength,
                                  FX_BOOL bIgnoreCase = FALSE);
 
-#ifdef __cplusplus
-}
-#endif
-#ifdef __cplusplus
-extern "C" {
-#endif
-
 void* FX_Random_MT_Start(FX_DWORD dwSeed);
 
 FX_DWORD FX_Random_MT_Generate(void* pContext);
@@ -63,9 +74,7 @@
 void FX_Random_GenerateMT(FX_DWORD* pBuffer, int32_t iCount);
 
 void FX_Random_GenerateCrypto(FX_DWORD* pBuffer, int32_t iCount);
-#ifdef __cplusplus
-}
-#endif
+
 template <class baseType>
 class CFX_SSortTemplate {
  public:
diff --git a/core/src/fpdfapi/fpdf_font/fpdf_font.cpp b/core/src/fpdfapi/fpdf_font/fpdf_font.cpp
index 9f83da0..64d6cdc 100644
--- a/core/src/fpdfapi/fpdf_font/fpdf_font.cpp
+++ b/core/src/fpdfapi/fpdf_font/fpdf_font.cpp
@@ -11,6 +11,7 @@
 #include "core/include/fpdfapi/fpdf_page.h"
 #include "core/include/fpdfapi/fpdf_pageobj.h"
 #include "core/include/fpdfapi/fpdf_resource.h"
+#include "core/include/fxcrt/fx_ext.h"
 #include "core/include/fxge/fx_freetype.h"
 
 #if _FXM_PLATFORM_ == _FXM_PLATFORM_APPLE_
@@ -518,32 +519,19 @@
 FX_DWORD CPDF_ToUnicodeMap::StringToCode(const CFX_ByteStringC& str) {
   const FX_CHAR* buf = str.GetCStr();
   int len = str.GetLength();
-  if (len == 0) {
+  if (len == 0)
     return 0;
-  }
+
   int result = 0;
   if (buf[0] == '<') {
-    for (int i = 1; i < len; i++) {
-      int digit;
-      if (buf[i] >= '0' && buf[i] <= '9') {
-        digit = buf[i] - '0';
-      } else if (buf[i] >= 'a' && buf[i] <= 'f') {
-        digit = buf[i] - 'a' + 10;
-      } else if (buf[i] >= 'A' && buf[i] <= 'F') {
-        digit = buf[i] - 'A' + 10;
-      } else {
-        break;
-      }
-      result = result * 16 + digit;
-    }
+    for (int i = 1; i < len && std::isxdigit(buf[i]); ++i)
+      result = result * 16 + FXSYS_toHexDigit(buf[i]);
     return result;
   }
-  for (int i = 0; i < len; i++) {
-    if (buf[i] < '0' || buf[i] > '9') {
-      break;
-    }
-    result = result * 10 + buf[i] - '0';
-  }
+
+  for (int i = 0; i < len && std::isdigit(buf[i]); ++i)
+    result = result * 10 + FXSYS_toDecimalDigit(buf[i]);
+
   return result;
 }
 static CFX_WideString StringDataAdd(CFX_WideString str) {
@@ -570,26 +558,15 @@
     const CFX_ByteStringC& str) {
   const FX_CHAR* buf = str.GetCStr();
   int len = str.GetLength();
-  if (len == 0) {
+  if (len == 0)
     return CFX_WideString();
-  }
+
   CFX_WideString result;
   if (buf[0] == '<') {
     int byte_pos = 0;
     FX_WCHAR ch = 0;
-    for (int i = 1; i < len; i++) {
-      int digit;
-      if (buf[i] >= '0' && buf[i] <= '9') {
-        digit = buf[i] - '0';
-      } else if (buf[i] >= 'a' && buf[i] <= 'f') {
-        digit = buf[i] - 'a' + 10;
-      } else if (buf[i] >= 'A' && buf[i] <= 'F') {
-        digit = buf[i] - 'A' + 10;
-      } else {
-        break;
-      }
-      ch = ch * 16 + digit;
-
+    for (int i = 1; i < len && std::isxdigit(buf[i]); ++i) {
+      ch = ch * 16 + FXSYS_toHexDigit(buf[i]);
       byte_pos++;
       if (byte_pos == 4) {
         result += ch;
@@ -599,8 +576,6 @@
     }
     return result;
   }
-  if (buf[0] == '(') {
-  }
   return result;
 }
 void CPDF_ToUnicodeMap::Load(CPDF_Stream* pStream) {
diff --git a/core/src/fpdfapi/fpdf_font/fpdf_font_cid.cpp b/core/src/fpdfapi/fpdf_font/fpdf_font_cid.cpp
index 65b4b78..d4c7108 100644
--- a/core/src/fpdfapi/fpdf_font/fpdf_font_cid.cpp
+++ b/core/src/fpdfapi/fpdf_font/fpdf_font_cid.cpp
@@ -9,6 +9,7 @@
 #include "core/include/fpdfapi/fpdf_module.h"
 #include "core/include/fpdfapi/fpdf_page.h"
 #include "core/include/fpdfapi/fpdf_resource.h"
+#include "core/include/fxcrt/fx_ext.h"
 #include "core/include/fxge/fx_freetype.h"
 #include "core/include/fxge/fx_ge.h"
 #include "core/src/fpdfapi/fpdf_cmaps/cmap_int.h"
@@ -699,30 +700,17 @@
   m_LastWord = word;
 }
 
+// Static.
 FX_DWORD CPDF_CMapParser::CMap_GetCode(const CFX_ByteStringC& word) {
   int num = 0;
   if (word.GetAt(0) == '<') {
-    for (int i = 1; i < word.GetLength(); i++) {
-      uint8_t digit = word.GetAt(i);
-      if (digit >= '0' && digit <= '9') {
-        digit = digit - '0';
-      } else if (digit >= 'a' && digit <= 'f') {
-        digit = digit - 'a' + 10;
-      } else if (digit >= 'A' && digit <= 'F') {
-        digit = digit - 'A' + 10;
-      } else {
-        return num;
-      }
-      num = num * 16 + digit;
-    }
-  } else {
-    for (int i = 0; i < word.GetLength(); i++) {
-      if (word.GetAt(i) < '0' || word.GetAt(i) > '9') {
-        return num;
-      }
-      num = num * 10 + word.GetAt(i) - '0';
-    }
+    for (int i = 1; i < word.GetLength() && std::isxdigit(word.GetAt(i)); ++i)
+      num = num * 16 + FXSYS_toHexDigit(word.GetAt(i));
+    return num;
   }
+
+  for (int i = 0; i < word.GetLength() && std::isdigit(word.GetAt(i)); ++i)
+    num = num * 10 + FXSYS_toDecimalDigit(word.GetAt(i));
   return num;
 }
 
@@ -746,13 +734,7 @@
   for (i = 0; i < range.m_CharSize; ++i) {
     uint8_t digit1 = first.GetAt(i * 2 + 1);
     uint8_t digit2 = first.GetAt(i * 2 + 2);
-    uint8_t byte = (digit1 >= '0' && digit1 <= '9')
-                       ? (digit1 - '0')
-                       : ((digit1 & 0xdf) - 'A' + 10);
-    byte = byte * 16 + ((digit2 >= '0' && digit2 <= '9')
-                            ? (digit2 - '0')
-                            : ((digit2 & 0xdf) - 'A' + 10));
-    range.m_Lower[i] = byte;
+    range.m_Lower[i] = FXSYS_toHexDigit(digit1) * 16 + FXSYS_toHexDigit(digit2);
   }
 
   FX_DWORD size = second.GetLength();
@@ -763,13 +745,7 @@
     uint8_t digit2 = ((FX_DWORD)i * 2 + 2 < size)
                          ? second.GetAt((FX_STRSIZE)i * 2 + 2)
                          : '0';
-    uint8_t byte = (digit1 >= '0' && digit1 <= '9')
-                       ? (digit1 - '0')
-                       : ((digit1 & 0xdf) - 'A' + 10);
-    byte = byte * 16 + ((digit2 >= '0' && digit2 <= '9')
-                            ? (digit2 - '0')
-                            : ((digit2 & 0xdf) - 'A' + 10));
-    range.m_Upper[i] = byte;
+    range.m_Upper[i] = FXSYS_toHexDigit(digit1) * 16 + FXSYS_toHexDigit(digit2);
   }
   return true;
 }
diff --git a/core/src/fpdfapi/fpdf_page/fpdf_page_parser_old.cpp b/core/src/fpdfapi/fpdf_page/fpdf_page_parser_old.cpp
index 8d155e7..a1aa686 100644
--- a/core/src/fpdfapi/fpdf_page/fpdf_page_parser_old.cpp
+++ b/core/src/fpdfapi/fpdf_page/fpdf_page_parser_old.cpp
@@ -11,6 +11,7 @@
 #include "core/include/fpdfapi/fpdf_module.h"
 #include "core/include/fpdfapi/fpdf_page.h"
 #include "core/include/fxcodec/fx_codec.h"
+#include "core/include/fxcrt/fx_ext.h"
 
 namespace {
 
@@ -791,7 +792,7 @@
         break;
       case 1:
         if (ch >= '0' && ch <= '7') {
-          iEscCode = ch - '0';
+          iEscCode = FXSYS_toDecimalDigit(ch);
           status = 2;
           break;
         }
@@ -816,7 +817,7 @@
         break;
       case 2:
         if (ch >= '0' && ch <= '7') {
-          iEscCode = iEscCode * 8 + ch - '0';
+          iEscCode = iEscCode * 8 + FXSYS_toDecimalDigit(ch);
           status = 3;
         } else {
           buf.AppendChar(iEscCode);
@@ -826,7 +827,7 @@
         break;
       case 3:
         if (ch >= '0' && ch <= '7') {
-          iEscCode = iEscCode * 8 + ch - '0';
+          iEscCode = iEscCode * 8 + FXSYS_toDecimalDigit(ch);
           buf.AppendChar(iEscCode);
           status = 0;
         } else {
@@ -859,50 +860,33 @@
   if (!PositionIsInBounds())
     return CFX_ByteString();
 
-  int ch = m_pBuf[m_Pos++];
   CFX_ByteTextBuf buf;
-  FX_BOOL bFirst = TRUE;
+  bool bFirst = true;
   int code = 0;
-  while (1) {
-    if (ch == '>') {
-      break;
-    }
-    if (ch >= '0' && ch <= '9') {
-      if (bFirst) {
-        code = (ch - '0') * 16;
-      } else {
-        code += ch - '0';
-        buf.AppendChar((char)code);
-      }
-      bFirst = !bFirst;
-    } else if (ch >= 'A' && ch <= 'F') {
-      if (bFirst) {
-        code = (ch - 'A' + 10) * 16;
-      } else {
-        code += ch - 'A' + 10;
-        buf.AppendChar((char)code);
-      }
-      bFirst = !bFirst;
-    } else if (ch >= 'a' && ch <= 'f') {
-      if (bFirst) {
-        code = (ch - 'a' + 10) * 16;
-      } else {
-        code += ch - 'a' + 10;
-        buf.AppendChar((char)code);
-      }
-      bFirst = !bFirst;
-    }
-    if (!PositionIsInBounds())
+  while (PositionIsInBounds()) {
+    int ch = m_pBuf[m_Pos++];
+
+    if (ch == '>')
       break;
 
-    ch = m_pBuf[m_Pos++];
+    if (!std::isxdigit(ch))
+      continue;
+
+    int val = FXSYS_toHexDigit(ch);
+    if (bFirst) {
+      code = val * 16;
+    } else {
+      code += val;
+      buf.AppendByte((uint8_t)code);
+    }
+    bFirst = !bFirst;
   }
-  if (!bFirst) {
+  if (!bFirst)
     buf.AppendChar((char)code);
-  }
-  if (buf.GetLength() > MAX_STRING_LENGTH) {
+
+  if (buf.GetLength() > MAX_STRING_LENGTH)
     return CFX_ByteString(buf.GetBuffer(), MAX_STRING_LENGTH);
-  }
+
   return buf.GetByteString();
 }
 
diff --git a/core/src/fpdfapi/fpdf_parser/fpdf_parser_decode.cpp b/core/src/fpdfapi/fpdf_parser/fpdf_parser_decode.cpp
index 588ab5d..57d1971 100644
--- a/core/src/fpdfapi/fpdf_parser/fpdf_parser_decode.cpp
+++ b/core/src/fpdfapi/fpdf_parser/fpdf_parser_decode.cpp
@@ -9,6 +9,7 @@
 #include "core/include/fpdfapi/fpdf_module.h"
 #include "core/include/fpdfapi/fpdf_parser.h"
 #include "core/include/fxcodec/fx_codec.h"
+#include "core/include/fxcrt/fx_ext.h"
 
 #define _STREAM_MAX_SIZE_ 20 * 1024 * 1024
 
@@ -129,37 +130,32 @@
     }
   dest_buf = FX_Alloc(uint8_t, i / 2 + 1);
   dest_size = 0;
-  FX_BOOL bFirstDigit = TRUE;
+  bool bFirst = true;
   for (i = 0; i < src_size; i++) {
     uint8_t ch = src_buf[i];
     if (PDFCharIsLineEnding(ch) || ch == ' ' || ch == '\t')
       continue;
 
-    int digit;
-    if (ch <= '9' && ch >= '0') {
-      digit = ch - '0';
-    } else if (ch <= 'f' && ch >= 'a') {
-      digit = ch - 'a' + 10;
-    } else if (ch <= 'F' && ch >= 'A') {
-      digit = ch - 'A' + 10;
-    } else if (ch == '>') {
-      i++;
+    if (ch == '>') {
+      ++i;
       break;
-    } else {
+    }
+    if (!std::isxdigit(ch))
       continue;
-    }
-    if (bFirstDigit) {
+
+    int digit = FXSYS_toHexDigit(ch);
+    if (bFirst)
       dest_buf[dest_size] = digit * 16;
-    } else {
+    else
       dest_buf[dest_size++] += digit;
-    }
-    bFirstDigit = !bFirstDigit;
+
+    bFirst = !bFirst;
   }
-  if (!bFirstDigit) {
+  if (!bFirst)
     dest_size++;
-  }
   return i;
 }
+
 FX_DWORD RunLengthDecode(const uint8_t* src_buf,
                          FX_DWORD src_size,
                          uint8_t*& dest_buf,
diff --git a/core/src/fpdfapi/fpdf_parser/fpdf_parser_parser.cpp b/core/src/fpdfapi/fpdf_parser/fpdf_parser_parser.cpp
index 6e927e1..6251748 100644
--- a/core/src/fpdfapi/fpdf_parser/fpdf_parser_parser.cpp
+++ b/core/src/fpdfapi/fpdf_parser/fpdf_parser_parser.cpp
@@ -13,6 +13,7 @@
 #include "core/include/fpdfapi/fpdf_module.h"
 #include "core/include/fpdfapi/fpdf_page.h"
 #include "core/include/fpdfapi/fpdf_parser.h"
+#include "core/include/fxcrt/fx_ext.h"
 #include "core/include/fxcrt/fx_safe_types.h"
 #include "core/src/fpdfapi/fpdf_page/pageint.h"
 #include "third_party/base/nonstd_unique_ptr.h"
@@ -164,85 +165,83 @@
   m_bXRefStream = FALSE;
   m_LastXRefOffset = 0;
   m_bOwnFileRead = bOwnFileRead;
+
   int32_t offset = GetHeaderOffset(pFileAccess);
   if (offset == -1) {
-    if (bOwnFileRead && pFileAccess) {
+    if (bOwnFileRead && pFileAccess)
       pFileAccess->Release();
-    }
     return PDFPARSE_ERROR_FORMAT;
   }
   m_Syntax.InitParser(pFileAccess, offset);
+
   uint8_t ch;
-  if (!m_Syntax.GetCharAt(5, ch)) {
+  if (!m_Syntax.GetCharAt(5, ch))
     return PDFPARSE_ERROR_FORMAT;
-  }
-  if (ch >= '0' && ch <= '9') {
-    m_FileVersion = (ch - '0') * 10;
-  }
-  if (!m_Syntax.GetCharAt(7, ch)) {
+  if (std::isdigit(ch))
+    m_FileVersion = FXSYS_toDecimalDigit(ch) * 10;
+
+  if (!m_Syntax.GetCharAt(7, ch))
     return PDFPARSE_ERROR_FORMAT;
-  }
-  if (ch >= '0' && ch <= '9') {
-    m_FileVersion += ch - '0';
-  }
-  if (m_Syntax.m_FileLen < m_Syntax.m_HeaderOffset + 9) {
+  if (std::isdigit(ch))
+    m_FileVersion += FXSYS_toDecimalDigit(ch);
+
+  if (m_Syntax.m_FileLen < m_Syntax.m_HeaderOffset + 9)
     return PDFPARSE_ERROR_FORMAT;
-  }
+
   m_Syntax.RestorePos(m_Syntax.m_FileLen - m_Syntax.m_HeaderOffset - 9);
-  if (!bReParse) {
+  if (!bReParse)
     m_pDocument = new CPDF_Document(this);
-  }
+
   FX_BOOL bXRefRebuilt = FALSE;
   if (m_Syntax.SearchWord(FX_BSTRC("startxref"), TRUE, FALSE, 4096)) {
     FX_FILESIZE startxref_offset = m_Syntax.SavePos();
     void* pResult = FXSYS_bsearch(&startxref_offset, m_SortedOffset.GetData(),
                                   m_SortedOffset.GetSize(), sizeof(FX_FILESIZE),
                                   CompareFileSize);
-    if (pResult == NULL) {
+    if (!pResult)
       m_SortedOffset.Add(startxref_offset);
-    }
+
     m_Syntax.GetKeyword();
     FX_BOOL bNumber;
     CFX_ByteString xrefpos_str = m_Syntax.GetNextWord(bNumber);
-    if (!bNumber) {
+    if (!bNumber)
       return PDFPARSE_ERROR_FORMAT;
-    }
+
     m_LastXRefOffset = (FX_FILESIZE)FXSYS_atoi64(xrefpos_str);
     if (!LoadAllCrossRefV4(m_LastXRefOffset) &&
         !LoadAllCrossRefV5(m_LastXRefOffset)) {
-      if (!RebuildCrossRef()) {
+      if (!RebuildCrossRef())
         return PDFPARSE_ERROR_FORMAT;
-      }
+
       bXRefRebuilt = TRUE;
       m_LastXRefOffset = 0;
     }
   } else {
-    if (!RebuildCrossRef()) {
+    if (!RebuildCrossRef())
       return PDFPARSE_ERROR_FORMAT;
-    }
+
     bXRefRebuilt = TRUE;
   }
   FX_DWORD dwRet = SetEncryptHandler();
-  if (dwRet != PDFPARSE_ERROR_SUCCESS) {
+  if (dwRet != PDFPARSE_ERROR_SUCCESS)
     return dwRet;
-  }
+
   m_pDocument->LoadDoc();
-  if (m_pDocument->GetRoot() == NULL || m_pDocument->GetPageCount() == 0) {
-    if (bXRefRebuilt) {
+  if (!m_pDocument->GetRoot() || m_pDocument->GetPageCount() == 0) {
+    if (bXRefRebuilt)
       return PDFPARSE_ERROR_FORMAT;
-    }
+
     ReleaseEncryptHandler();
-    if (!RebuildCrossRef()) {
+    if (!RebuildCrossRef())
       return PDFPARSE_ERROR_FORMAT;
-    }
+
     dwRet = SetEncryptHandler();
-    if (dwRet != PDFPARSE_ERROR_SUCCESS) {
+    if (dwRet != PDFPARSE_ERROR_SUCCESS)
       return dwRet;
-    }
+
     m_pDocument->LoadDoc();
-    if (m_pDocument->GetRoot() == NULL) {
+    if (!m_pDocument->GetRoot())
       return PDFPARSE_ERROR_FORMAT;
-    }
   }
   FXSYS_qsort(m_SortedOffset.GetData(), m_SortedOffset.GetSize(),
               sizeof(FX_FILESIZE), CompareFileSize);
@@ -251,13 +250,12 @@
     ReleaseEncryptHandler();
     RebuildCrossRef();
     RootObjNum = GetRootObjNum();
-    if (RootObjNum == 0) {
+    if (RootObjNum == 0)
       return PDFPARSE_ERROR_FORMAT;
-    }
+
     dwRet = SetEncryptHandler();
-    if (dwRet != PDFPARSE_ERROR_SUCCESS) {
+    if (dwRet != PDFPARSE_ERROR_SUCCESS)
       return dwRet;
-    }
   }
   if (m_pSecurityHandler && !m_pSecurityHandler->IsMetadataEncrypted()) {
     CPDF_Reference* pMetadata =
@@ -461,9 +459,8 @@
         int32_t offset = FXSYS_atoi(pEntry);
         if (offset == 0) {
           for (int32_t c = 0; c < 10; c++) {
-            if (pEntry[c] < '0' || pEntry[c] > '9') {
+            if (!std::isdigit(pEntry[c]))
               return FALSE;
-            }
           }
         }
         m_CrossRef.SetAtGrow(objnum, offset);
@@ -562,9 +559,8 @@
             FX_FILESIZE offset = (FX_FILESIZE)FXSYS_atoi64(pEntry);
             if (offset == 0) {
               for (int32_t c = 0; c < 10; c++) {
-                if (pEntry[c] < '0' || pEntry[c] > '9') {
+                if (!std::isdigit(pEntry[c]))
                   return false;
-                }
               }
             }
             m_CrossRef.SetAtGrow(objnum, offset);
@@ -632,28 +628,32 @@
       uint8_t byte = buffer[i];
       switch (status) {
         case 0:
-          if (PDFCharIsWhitespace(byte)) {
+          if (PDFCharIsWhitespace(byte))
             status = 1;
-          }
-          if (byte <= '9' && byte >= '0') {
+
+          if (std::isdigit(byte)) {
             --i;
             status = 1;
           }
+
           if (byte == '%') {
             inside_index = 0;
             status = 9;
           }
+
           if (byte == '(') {
             status = 10;
             depth = 1;
           }
+
           if (byte == '<') {
             inside_index = 1;
             status = 11;
           }
-          if (byte == '\\') {
+
+          if (byte == '\\')
             status = 13;
-          }
+
           if (byte == 't') {
             status = 7;
             inside_index = 1;
@@ -662,10 +662,10 @@
         case 1:
           if (PDFCharIsWhitespace(byte)) {
             break;
-          } else if (byte <= '9' && byte >= '0') {
+          } else if (std::isdigit(byte)) {
             start_pos = pos + i;
             status = 2;
-            objnum = byte - '0';
+            objnum = FXSYS_toDecimalDigit(byte);
           } else if (byte == 't') {
             status = 7;
             inside_index = 1;
@@ -678,8 +678,8 @@
           }
           break;
         case 2:
-          if (byte <= '9' && byte >= '0') {
-            objnum = objnum * 10 + byte - '0';
+          if (std::isdigit(byte)) {
+            objnum = objnum * 10 + FXSYS_toDecimalDigit(byte);
             break;
           } else if (PDFCharIsWhitespace(byte)) {
             status = 3;
@@ -690,10 +690,10 @@
           }
           break;
         case 3:
-          if (byte <= '9' && byte >= '0') {
+          if (std::isdigit(byte)) {
             start_pos1 = pos + i;
             status = 4;
-            gennum = byte - '0';
+            gennum = FXSYS_toDecimalDigit(byte);
           } else if (PDFCharIsWhitespace(byte)) {
             break;
           } else if (byte == 't') {
@@ -705,8 +705,8 @@
           }
           break;
         case 4:
-          if (byte <= '9' && byte >= '0') {
-            gennum = gennum * 10 + byte - '0';
+          if (std::isdigit(byte)) {
+            gennum = gennum * 10 + FXSYS_toDecimalDigit(byte);
             break;
           } else if (PDFCharIsWhitespace(byte)) {
             status = 5;
@@ -721,9 +721,9 @@
             inside_index = 1;
           } else if (PDFCharIsWhitespace(byte)) {
             break;
-          } else if (byte <= '9' && byte >= '0') {
+          } else if (std::isdigit(byte)) {
             objnum = gennum;
-            gennum = byte - '0';
+            gennum = FXSYS_toDecimalDigit(byte);
             start_pos = start_pos1;
             start_pos1 = pos + i;
             status = 4;
@@ -1858,7 +1858,7 @@
         break;
       case 1:
         if (ch >= '0' && ch <= '7') {
-          iEscCode = ch - '0';
+          iEscCode = FXSYS_toDecimalDigit(ch);
           status = 2;
           break;
         }
@@ -1883,7 +1883,7 @@
         break;
       case 2:
         if (ch >= '0' && ch <= '7') {
-          iEscCode = iEscCode * 8 + ch - '0';
+          iEscCode = iEscCode * 8 + FXSYS_toDecimalDigit(ch);
           status = 3;
         } else {
           buf.AppendChar(iEscCode);
@@ -1893,7 +1893,7 @@
         break;
       case 3:
         if (ch >= '0' && ch <= '7') {
-          iEscCode = iEscCode * 8 + ch - '0';
+          iEscCode = iEscCode * 8 + FXSYS_toDecimalDigit(ch);
           buf.AppendChar(iEscCode);
           status = 0;
         } else {
@@ -1918,48 +1918,33 @@
 }
 CFX_ByteString CPDF_SyntaxParser::ReadHexString() {
   uint8_t ch;
-  if (!GetNextChar(ch)) {
+  if (!GetNextChar(ch))
     return CFX_ByteString();
-  }
+
   CFX_BinaryBuf buf;
-  FX_BOOL bFirst = TRUE;
+  bool bFirst = true;
   uint8_t code = 0;
   while (1) {
-    if (ch == '>') {
+    if (ch == '>')
       break;
-    }
-    if (ch >= '0' && ch <= '9') {
+
+    if (std::isxdigit(ch)) {
+      int val = FXSYS_toHexDigit(ch);
       if (bFirst) {
-        code = (ch - '0') * 16;
+        code = val * 16;
       } else {
-        code += ch - '0';
-        buf.AppendByte((uint8_t)code);
-      }
-      bFirst = !bFirst;
-    } else if (ch >= 'A' && ch <= 'F') {
-      if (bFirst) {
-        code = (ch - 'A' + 10) * 16;
-      } else {
-        code += ch - 'A' + 10;
-        buf.AppendByte((uint8_t)code);
-      }
-      bFirst = !bFirst;
-    } else if (ch >= 'a' && ch <= 'f') {
-      if (bFirst) {
-        code = (ch - 'a' + 10) * 16;
-      } else {
-        code += ch - 'a' + 10;
+        code += val;
         buf.AppendByte((uint8_t)code);
       }
       bFirst = !bFirst;
     }
-    if (!GetNextChar(ch)) {
+
+    if (!GetNextChar(ch))
       break;
-    }
   }
-  if (!bFirst) {
+  if (!bFirst)
     buf.AppendByte((uint8_t)code);
-  }
+
   return buf.GetByteString();
 }
 void CPDF_SyntaxParser::ToNextLine() {
diff --git a/core/src/fpdfapi/fpdf_parser/fpdf_parser_utility.cpp b/core/src/fpdfapi/fpdf_parser/fpdf_parser_utility.cpp
index 89a5deb..9729bab 100644
--- a/core/src/fpdfapi/fpdf_parser/fpdf_parser_utility.cpp
+++ b/core/src/fpdfapi/fpdf_parser/fpdf_parser_utility.cpp
@@ -5,6 +5,7 @@
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
 #include "core/include/fpdfapi/fpdf_parser.h"
+#include "core/include/fxcrt/fx_ext.h"
 
 // Indexed by 8-bit character code, contains either:
 //   'W' - for whitespace: NUL, TAB, CR, LF, FF, 0x80, 0xff
@@ -279,18 +280,7 @@
   }
   return FALSE;
 }
-static int _hex2dec(char ch) {
-  if (ch >= '0' && ch <= '9') {
-    return ch - '0';
-  }
-  if (ch >= 'a' && ch <= 'f') {
-    return ch - 'a' + 10;
-  }
-  if (ch >= 'A' && ch <= 'F') {
-    return ch - 'A' + 10;
-  }
-  return 0;
-}
+
 CFX_ByteString PDF_NameDecode(const CFX_ByteStringC& bstr) {
   int size = bstr.GetLength();
   const FX_CHAR* pSrc = bstr.GetCStr();
@@ -302,7 +292,8 @@
   FX_CHAR* pDest = pDestStart;
   for (int i = 0; i < size; i++) {
     if (pSrc[i] == '#' && i < size - 2) {
-      *pDest++ = _hex2dec(pSrc[i + 1]) * 16 + _hex2dec(pSrc[i + 2]);
+      *pDest++ =
+          FXSYS_toHexDigit(pSrc[i + 1]) * 16 + FXSYS_toHexDigit(pSrc[i + 2]);
       i += 2;
     } else {
       *pDest++ = pSrc[i];
diff --git a/core/src/fpdftext/fpdf_text.cpp b/core/src/fpdftext/fpdf_text.cpp
index 55ab483..0b7a849 100644
--- a/core/src/fpdftext/fpdf_text.cpp
+++ b/core/src/fpdftext/fpdf_text.cpp
@@ -14,6 +14,9 @@
 #include "third_party/base/nonstd_unique_ptr.h"
 #include "txtproc.h"
 
+#include <cctype>
+#include <cwctype>
+
 CFX_ByteString CharFromUnicodeAlt(FX_WCHAR unicode,
                                   int destcp,
                                   const FX_CHAR* defchar) {
@@ -436,10 +439,9 @@
 static FX_BOOL IsNumber(CFX_WideString& str) {
   for (int i = 0; i < str.GetLength(); i++) {
     FX_WCHAR ch = str[i];
-    if ((ch < '0' || ch > '9') && ch != '-' && ch != '+' && ch != '.' &&
-        ch != ' ') {
+    // TODO(dsinclair): --.+ +.-- should probably not be a number.
+    if (!std::iswdigit(ch) && ch != '-' && ch != '+' && ch != '.' && ch != ' ')
       return FALSE;
-    }
   }
   return TRUE;
 }
diff --git a/core/src/fpdftext/fpdf_text_int.cpp b/core/src/fpdftext/fpdf_text_int.cpp
index dd6be31..4ed11e3 100644
--- a/core/src/fpdftext/fpdf_text_int.cpp
+++ b/core/src/fpdftext/fpdf_text_int.cpp
@@ -4,7 +4,8 @@
 
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
-#include <ctype.h>
+#include <cctype>
+#include <cwctype>
 #include <algorithm>
 
 #include "core/include/fpdfapi/fpdf_module.h"
@@ -2416,8 +2417,8 @@
 FX_BOOL CPDF_TextPageFind::IsMatchWholeWord(const CFX_WideString& csPageText,
                                             int startPos,
                                             int endPos) {
-  int char_left = 0;
-  int char_right = 0;
+  FX_WCHAR char_left = 0;
+  FX_WCHAR char_right = 0;
   int char_count = endPos - startPos + 1;
   if (char_count < 1) {
     return FALSE;
@@ -2433,12 +2434,11 @@
   }
   if ((char_left > 'A' && char_left < 'a') ||
       (char_left > 'a' && char_left < 'z') ||
-      (char_left > 0xfb00 && char_left < 0xfb06) ||
-      (char_left >= '0' && char_left <= '9') ||
+      (char_left > 0xfb00 && char_left < 0xfb06) || std::iswdigit(char_left) ||
       (char_right > 'A' && char_right < 'a') ||
       (char_right > 'a' && char_right < 'z') ||
       (char_right > 0xfb00 && char_right < 0xfb06) ||
-      (char_right >= '0' && char_right <= '9')) {
+      std::iswdigit(char_right)) {
     return FALSE;
   }
   if (!(('A' > char_left || char_left > 'Z') &&
diff --git a/core/src/fxcodec/codec/fx_codec.cpp b/core/src/fxcodec/codec/fx_codec.cpp
index bfa5bef..51a1f5d 100644
--- a/core/src/fxcodec/codec/fx_codec.cpp
+++ b/core/src/fxcodec/codec/fx_codec.cpp
@@ -1,3 +1,4 @@
+
 // Copyright 2014 PDFium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
@@ -9,6 +10,7 @@
 #include <cmath>
 
 #include "codec_int.h"
+#include "core/include/fxcrt/fx_ext.h"
 #include "core/include/fxcrt/fx_safe_types.h"
 #include "third_party/base/logging.h"
 
@@ -147,6 +149,20 @@
                                             FX_DWORD& dest_size) {
   return FALSE;
 }
+
+#define EXPONENT_DETECT(ptr)                 \
+  for (;; ptr++) {                           \
+    if (!std::isdigit(*ptr)) {               \
+      if (endptr)                            \
+        *endptr = (char*)ptr;                \
+      break;                                 \
+    } else {                                 \
+      exp_ret *= 10;                         \
+      exp_ret += FXSYS_toDecimalDigit(*ptr); \
+      continue;                              \
+    }                                        \
+  }
+
 extern "C" double FXstrtod(const char* nptr, char** endptr) {
   double ret = 0.0;
   const char* ptr = nptr;
@@ -157,20 +173,20 @@
     return 0.0;
   }
   for (;; ptr++) {
-    if (!e_number && !e_point && (*ptr == '\t' || *ptr == ' ')) {
+    if (!e_number && !e_point && (*ptr == '\t' || *ptr == ' '))
       continue;
-    }
-    if (*ptr >= '0' && *ptr <= '9') {
-      if (!e_number) {
+
+    if (std::isdigit(*ptr)) {
+      if (!e_number)
         e_number = 1;
-      }
+
       if (!e_point) {
         ret *= 10;
-        ret += (*ptr - '0');
+        ret += FXSYS_toDecimalDigit(*ptr);
       } else {
         fra_count++;
         fra_ret *= 10;
-        fra_ret += (*ptr - '0');
+        fra_ret += FXSYS_toDecimalDigit(*ptr);
       }
       continue;
     }
@@ -188,29 +204,17 @@
       }
     }
     if (e_number && (*ptr == 'e' || *ptr == 'E')) {
-#define EXPONENT_DETECT(ptr)        \
-  for (;; ptr++) {                  \
-    if (*ptr < '0' || *ptr > '9') { \
-      if (endptr)                   \
-        *endptr = (char*)ptr;       \
-      break;                        \
-    } else {                        \
-      exp_ret *= 10;                \
-      exp_ret += (*ptr - '0');      \
-      continue;                     \
-    }                               \
-  }
       exp_ptr = ptr++;
       if (*ptr == '+' || *ptr == '-') {
         exp_sig = (*ptr++ == '+') ? 1 : -1;
-        if (*ptr < '0' || *ptr > '9') {
+        if (!std::isdigit(*ptr)) {
           if (endptr) {
             *endptr = (char*)exp_ptr;
           }
           break;
         }
         EXPONENT_DETECT(ptr);
-      } else if (*ptr >= '0' && *ptr <= '9') {
+      } else if (std::isdigit(*ptr)) {
         EXPONENT_DETECT(ptr);
       } else {
         if (endptr) {
@@ -218,7 +222,6 @@
         }
         break;
       }
-#undef EXPONENT_DETECT
       break;
     }
     if (ptr != nptr && !e_number) {
@@ -247,6 +250,8 @@
   }
   return is_negative ? -ret : ret;
 }
+#undef EXPONENT_DETECT
+
 FX_BOOL CCodec_BasicModule::A85Encode(const uint8_t* src_buf,
                                       FX_DWORD src_size,
                                       uint8_t*& dest_buf,
diff --git a/core/src/fxcrt/fx_basic_bstring.cpp b/core/src/fxcrt/fx_basic_bstring.cpp
index 9d64fbe..574e57a 100644
--- a/core/src/fxcrt/fx_basic_bstring.cpp
+++ b/core/src/fxcrt/fx_basic_bstring.cpp
@@ -5,6 +5,7 @@
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
 #include <stddef.h>  // For offsetof().
+#include <cctype>
 
 #include "core/include/fxcrt/fx_basic.h"
 #include "third_party/base/numerics/safe_math.h"
@@ -493,8 +494,8 @@
     }
     if (nWidth == 0) {
       nWidth = FXSYS_atoi(lpsz);
-      for (; (*lpsz) >= '0' && (*lpsz) <= '9'; lpsz++)
-        ;
+      while (std::isdigit(*lpsz))
+        lpsz++;
     }
     if (nWidth < 0 || nWidth > 128 * 1024) {
       lpszFormat = "Bad width";
@@ -509,8 +510,8 @@
         lpsz++;
       } else {
         nPrecision = FXSYS_atoi(lpsz);
-        for (; (*lpsz) >= '0' && (*lpsz) <= '9'; lpsz++)
-          ;
+        while (std::isdigit(*lpsz))
+          lpsz++;
       }
     }
     if (nPrecision < 0 || nPrecision > 128 * 1024) {
diff --git a/core/src/fxcrt/fx_basic_gcc.cpp b/core/src/fxcrt/fx_basic_gcc.cpp
index f8b0c7a..c352ee3 100644
--- a/core/src/fxcrt/fx_basic_gcc.cpp
+++ b/core/src/fxcrt/fx_basic_gcc.cpp
@@ -5,29 +5,49 @@
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
 #include <limits>
+#include <cctype>
+#include <cwctype>
 
 #include "core/include/fxcrt/fx_ext.h"
 #include "core/include/fxcrt/fx_string.h"
 
-template <class T, class STR_T>
-T FXSYS_StrToInt(STR_T str) {
+template <class T>
+T FXSYS_StrToInt(const FX_CHAR* str) {
   FX_BOOL neg = FALSE;
-  if (str == NULL) {
+  if (!str)
     return 0;
-  }
+
   if (*str == '-') {
     neg = TRUE;
     str++;
   }
   T num = 0;
-  while (*str) {
-    if ((*str) < '0' || (*str) > '9') {
+  while (*str && std::isdigit(*str)) {
+    if (num > (std::numeric_limits<T>::max() - 9) / 10)
       break;
-    }
-    if (num > (std::numeric_limits<T>::max() - 9) / 10) {
+
+    num = num * 10 + FXSYS_toDecimalDigit(*str);
+    str++;
+  }
+  return neg ? -num : num;
+}
+
+template <class T>
+T FXSYS_StrToInt(const FX_WCHAR* str) {
+  FX_BOOL neg = FALSE;
+  if (!str)
+    return 0;
+
+  if (*str == '-') {
+    neg = TRUE;
+    str++;
+  }
+  T num = 0;
+  while (*str && std::iswdigit(*str)) {
+    if (num > (std::numeric_limits<T>::max() - 9) / 10)
       break;
-    }
-    num = num * 10 + (*str) - '0';
+
+    num = num * 10 + FXSYS_toDecimalDigitWide(*str);
     str++;
   }
   return neg ? -num : num;
@@ -71,16 +91,16 @@
 extern "C" {
 #endif
 int32_t FXSYS_atoi(const FX_CHAR* str) {
-  return FXSYS_StrToInt<int32_t, const FX_CHAR*>(str);
+  return FXSYS_StrToInt<int32_t>(str);
 }
 int32_t FXSYS_wtoi(const FX_WCHAR* str) {
-  return FXSYS_StrToInt<int32_t, const FX_WCHAR*>(str);
+  return FXSYS_StrToInt<int32_t>(str);
 }
 int64_t FXSYS_atoi64(const FX_CHAR* str) {
-  return FXSYS_StrToInt<int64_t, const FX_CHAR*>(str);
+  return FXSYS_StrToInt<int64_t>(str);
 }
 int64_t FXSYS_wtoi64(const FX_WCHAR* str) {
-  return FXSYS_StrToInt<int64_t, const FX_WCHAR*>(str);
+  return FXSYS_StrToInt<int64_t>(str);
 }
 const FX_CHAR* FXSYS_i64toa(int64_t value, FX_CHAR* str, int radix) {
   return FXSYS_IntToStr<int64_t, uint64_t, FX_CHAR*>(value, str, radix);
diff --git a/core/src/fxcrt/fx_basic_util.cpp b/core/src/fxcrt/fx_basic_util.cpp
index 3e9d616..b4c7064 100644
--- a/core/src/fxcrt/fx_basic_util.cpp
+++ b/core/src/fxcrt/fx_basic_util.cpp
@@ -5,6 +5,9 @@
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
 #include "core/include/fxcrt/fx_basic.h"
+#include "core/include/fxcrt/fx_ext.h"
+
+#include <cctype>
 
 #if _FXM_PLATFORM_ != _FXM_PLATFORM_WINDOWS_
 #include <sys/types.h>
@@ -102,14 +105,11 @@
       bNegative = TRUE;
       cc++;
     }
-    while (cc < len) {
-      if (str[cc] < '0' || str[cc] > '9') {
+    while (cc < len && std::isdigit(str[cc])) {
+      // TODO(dsinclair): This is not the right way to handle overflow.
+      integer = integer * 10 + FXSYS_toDecimalDigit(str[cc]);
+      if (integer < 0)
         break;
-      }
-      integer = integer * 10 + str[cc] - '0';
-      if (integer < 0) {
-        break;
-      }
       cc++;
     }
     if (bNegative) {
@@ -146,7 +146,7 @@
     if (str[cc] == '.') {
       break;
     }
-    value = value * 10 + str[cc] - '0';
+    value = value * 10 + FXSYS_toDecimalDigit(str[cc]);
     cc++;
   }
   static const FX_FLOAT fraction_scales[] = {
@@ -157,7 +157,7 @@
   if (cc < len && str[cc] == '.') {
     cc++;
     while (cc < len) {
-      value += fraction_scales[scale] * (str[cc] - '0');
+      value += fraction_scales[scale] * FXSYS_toDecimalDigit(str[cc]);
       scale++;
       if (scale == sizeof fraction_scales / sizeof(FX_FLOAT)) {
         break;
diff --git a/core/src/fxcrt/fx_basic_wstring.cpp b/core/src/fxcrt/fx_basic_wstring.cpp
index 131672d..220ffbd 100644
--- a/core/src/fxcrt/fx_basic_wstring.cpp
+++ b/core/src/fxcrt/fx_basic_wstring.cpp
@@ -5,8 +5,10 @@
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
 #include <stddef.h>  // For offsetof().
+#include <cctype>
 
 #include "core/include/fxcrt/fx_basic.h"
+#include "core/include/fxcrt/fx_ext.h"
 #include "third_party/base/numerics/safe_math.h"
 
 // static
@@ -765,8 +767,8 @@
     }
     if (nWidth == 0) {
       nWidth = FXSYS_wtoi(lpsz);
-      for (; *lpsz != 0 && (*lpsz) <= '9' && (*lpsz) >= '0'; lpsz++)
-        ;
+      while (std::iswdigit(*lpsz))
+        ++lpsz;
     }
     if (nWidth < 0 || nWidth > 128 * 1024) {
       lpszFormat = L"Bad width";
@@ -781,8 +783,8 @@
         lpsz++;
       } else {
         nPrecision = FXSYS_wtoi(lpsz);
-        for (; *lpsz != 0 && (*lpsz) >= '0' && (*lpsz) <= '9'; lpsz++)
-          ;
+        while (std::iswdigit(*lpsz))
+          ++lpsz;
       }
     }
     if (nPrecision < 0 || nPrecision > 128 * 1024) {
@@ -968,7 +970,7 @@
     if (str[cc] == '.') {
       break;
     }
-    integer = integer * 10 + str[cc] - '0';
+    integer = integer * 10 + FXSYS_toDecimalDigitWide(str[cc]);
     cc++;
   }
   FX_FLOAT fraction = 0;
@@ -976,7 +978,7 @@
     cc++;
     FX_FLOAT scale = 0.1f;
     while (cc < len) {
-      fraction += scale * (str[cc] - '0');
+      fraction += scale * FXSYS_toDecimalDigitWide(str[cc]);
       scale *= 0.1f;
       cc++;
     }
diff --git a/core/src/fxcrt/fx_extension.cpp b/core/src/fxcrt/fx_extension.cpp
index 7eb86d6..37437ae 100644
--- a/core/src/fxcrt/fx_extension.cpp
+++ b/core/src/fxcrt/fx_extension.cpp
@@ -51,9 +51,7 @@
 IFX_MemoryStream* FX_CreateMemoryStream(FX_BOOL bConsecutive) {
   return new CFX_MemoryStream(bConsecutive);
 }
-#ifdef __cplusplus
-extern "C" {
-#endif
+
 FX_FLOAT FXSYS_tan(FX_FLOAT a) {
   return (FX_FLOAT)tan(a);
 }
@@ -190,12 +188,7 @@
   }
   return dwHashCode;
 }
-#ifdef __cplusplus
-}
-#endif
-#ifdef __cplusplus
-extern "C" {
-#endif
+
 void* FX_Random_MT_Start(FX_DWORD dwSeed) {
   FX_LPMTRANDOMCONTEXT pContext = FX_Alloc(FX_MTRANDOMCONTEXT, 1);
   pContext->mt[0] = dwSeed;
@@ -298,6 +291,3 @@
   FX_Random_GenerateBase(pBuffer, iCount);
 #endif
 }
-#ifdef __cplusplus
-}
-#endif
diff --git a/core/src/fxcrt/fx_extension_unittest.cpp b/core/src/fxcrt/fx_extension_unittest.cpp
new file mode 100644
index 0000000..b12dcbe
--- /dev/null
+++ b/core/src/fxcrt/fx_extension_unittest.cpp
@@ -0,0 +1,24 @@
+// Copyright 2015 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "core/include/fxcrt/fx_ext.h"
+
+TEST(fxcrt, FXSYS_toHexDigit) {
+  EXPECT_EQ(10, FXSYS_toHexDigit('a'));
+  EXPECT_EQ(10, FXSYS_toHexDigit('A'));
+  EXPECT_EQ(7, FXSYS_toHexDigit('7'));
+  EXPECT_EQ(0, FXSYS_toHexDigit('i'));
+}
+
+TEST(fxcrt, FXSYS_toDecimalDigit) {
+  EXPECT_EQ(7, FXSYS_toDecimalDigit('7'));
+  EXPECT_EQ(0, FXSYS_toDecimalDigit('a'));
+}
+
+TEST(fxcrt, FXSYS_toDecimalDigitWide) {
+  EXPECT_EQ(7, FXSYS_toDecimalDigitWide(L'7'));
+  EXPECT_EQ(0, FXSYS_toDecimalDigitWide(L'a'));
+}
diff --git a/core/src/fxcrt/fx_xml_parser.cpp b/core/src/fxcrt/fx_xml_parser.cpp
index 429bc38..2d3ff66 100644
--- a/core/src/fxcrt/fx_xml_parser.cpp
+++ b/core/src/fxcrt/fx_xml_parser.cpp
@@ -6,6 +6,7 @@
 
 #include "xml_int.h"
 
+#include "core/include/fxcrt/fx_ext.h"
 #include "core/include/fxcrt/fx_xml.h"
 
 CXML_Parser::~CXML_Parser() {
@@ -228,9 +229,8 @@
             iState = 10;
             break;
           }
-          if (g_FXCRT_XML_IsDigital(ch)) {
-            code = code * 10 + ch - '0';
-          }
+          if (g_FXCRT_XML_IsDigital(ch))
+            code = code * 10 + FXSYS_toDecimalDigit(ch);
           break;
         case 4:
           m_dwIndex++;
@@ -242,7 +242,7 @@
               g_FXCRT_XML_ByteTypes[ch] & FXCRTM_XML_CHARTYPE_HexChar;
           if (nHex) {
             if (nHex == FXCRTM_XML_CHARTYPE_HexDigital) {
-              code = (code << 4) + ch - '0';
+              code = (code << 4) + FXSYS_toDecimalDigit(ch);
             } else if (nHex == FXCRTM_XML_CHARTYPE_HexLowerLetter) {
               code = (code << 4) + ch - 87;
             } else {
diff --git a/fpdfsdk/src/fsdk_baseannot.cpp b/fpdfsdk/src/fsdk_baseannot.cpp
index f6ead75..8cd3df9 100644
--- a/fpdfsdk/src/fsdk_baseannot.cpp
+++ b/fpdfsdk/src/fsdk_baseannot.cpp
@@ -4,6 +4,7 @@
 
 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
 
+#include "core/include/fxcrt/fx_ext.h"
 #include "fpdfsdk/include/fsdk_baseannot.h"
 #include "fpdfsdk/include/fsdk_define.h"
 #include "fpdfsdk/include/fsdk_mgr.h"
@@ -216,12 +217,9 @@
     int i = 0;
     int j, k;
     FX_CHAR ch;
-    while (i < strLength) {
-      ch = dtStr[i];
-      if (ch >= '0' && ch <= '9')
-        break;
-      i++;
-    }
+    while (i < strLength && !std::isdigit(dtStr[i]))
+      ++i;
+
     if (i >= strLength)
       return *this;
 
@@ -229,9 +227,9 @@
     k = 0;
     while (i < strLength && j < 4) {
       ch = dtStr[i];
-      k = k * 10 + ch - '0';
+      k = k * 10 + FXSYS_toDecimalDigit(ch);
       j++;
-      if (ch < '0' || ch > '9')
+      if (!std::isdigit(ch))
         break;
       i++;
     }
@@ -243,9 +241,9 @@
     k = 0;
     while (i < strLength && j < 2) {
       ch = dtStr[i];
-      k = k * 10 + ch - '0';
+      k = k * 10 + FXSYS_toDecimalDigit(ch);
       j++;
-      if (ch < '0' || ch > '9')
+      if (!std::isdigit(ch))
         break;
       i++;
     }
@@ -257,9 +255,9 @@
     k = 0;
     while (i < strLength && j < 2) {
       ch = dtStr[i];
-      k = k * 10 + ch - '0';
+      k = k * 10 + FXSYS_toDecimalDigit(ch);
       j++;
-      if (ch < '0' || ch > '9')
+      if (!std::isdigit(ch))
         break;
       i++;
     }
@@ -271,9 +269,9 @@
     k = 0;
     while (i < strLength && j < 2) {
       ch = dtStr[i];
-      k = k * 10 + ch - '0';
+      k = k * 10 + FXSYS_toDecimalDigit(ch);
       j++;
-      if (ch < '0' || ch > '9')
+      if (!std::isdigit(ch))
         break;
       i++;
     }
@@ -285,9 +283,9 @@
     k = 0;
     while (i < strLength && j < 2) {
       ch = dtStr[i];
-      k = k * 10 + ch - '0';
+      k = k * 10 + FXSYS_toDecimalDigit(ch);
       j++;
-      if (ch < '0' || ch > '9')
+      if (!std::isdigit(ch))
         break;
       i++;
     }
@@ -299,9 +297,9 @@
     k = 0;
     while (i < strLength && j < 2) {
       ch = dtStr[i];
-      k = k * 10 + ch - '0';
+      k = k * 10 + FXSYS_toDecimalDigit(ch);
       j++;
-      if (ch < '0' || ch > '9')
+      if (!std::isdigit(ch))
         break;
       i++;
     }
@@ -320,9 +318,9 @@
     k = 0;
     while (i < strLength && j < 2) {
       ch = dtStr[i];
-      k = k * 10 + ch - '0';
+      k = k * 10 + FXSYS_toDecimalDigit(ch);
       j++;
-      if (ch < '0' || ch > '9')
+      if (!std::isdigit(ch))
         break;
       i++;
     }
@@ -337,9 +335,9 @@
     k = 0;
     while (i < strLength && j < 2) {
       ch = dtStr[i];
-      k = k * 10 + ch - '0';
+      k = k * 10 + FXSYS_toDecimalDigit(ch);
       j++;
-      if (ch < '0' || ch > '9')
+      if (!std::isdigit(ch))
         break;
       i++;
     }
diff --git a/fpdfsdk/src/javascript/PublicMethods.cpp b/fpdfsdk/src/javascript/PublicMethods.cpp
index cd71e70..094f3e7 100644
--- a/fpdfsdk/src/javascript/PublicMethods.cpp
+++ b/fpdfsdk/src/javascript/PublicMethods.cpp
@@ -14,6 +14,7 @@
 #include "JS_Runtime.h"
 #include "JS_Value.h"
 #include "color.h"
+#include "core/include/fxcrt/fx_ext.h"
 #include "fpdfsdk/include/fsdk_mgr.h"  // For CPDFDoc_Environment.
 #include "fpdfsdk/include/javascript/IJavaScript.h"
 #include "resource.h"
@@ -116,7 +117,7 @@
 }
 
 FX_BOOL CJS_PublicMethods::IsDigit(char ch) {
-  return (ch >= '0' && ch <= '9');
+  return std::isdigit(ch);
 }
 
 FX_BOOL CJS_PublicMethods::IsAlphabetic(wchar_t ch) {
@@ -396,7 +397,7 @@
 
     FX_WCHAR c = string.GetAt(i);
     if (IsDigit((wchar_t)c)) {
-      nRet = nRet * 10 + (c - '0');
+      nRet = nRet * 10 + FXSYS_toDecimalDigitWide(c);
       nSkip = i - nStart + 1;
       if (nSkip >= nMaxStep)
         break;
diff --git a/fpdfsdk/src/javascript/util.cpp b/fpdfsdk/src/javascript/util.cpp
index c021ec9..30df53e 100644
--- a/fpdfsdk/src/javascript/util.cpp
+++ b/fpdfsdk/src/javascript/util.cpp
@@ -13,6 +13,7 @@
 #include "JS_Runtime.h"
 #include "JS_Value.h"
 #include "PublicMethods.h"
+#include "core/include/fxcrt/fx_ext.h"
 #include "fpdfsdk/include/javascript/IJavaScript.h"
 #include "resource.h"
 
@@ -425,7 +426,7 @@
         break;
       case 'X': {
         while (itSource < iSize) {
-          if ((cSource[itSource] >= '0' && cSource[itSource] <= '9') ||
+          if (std::isdigit(cSource[itSource]) ||
               (cSource[itSource] >= 'a' && cSource[itSource] <= 'z') ||
               (cSource[itSource] >= 'A' && cSource[itSource] <= 'Z')) {
             cPurpose += cSource[itSource];
@@ -450,7 +451,7 @@
       } break;
       case '9': {
         while (itSource < iSize) {
-          if (cSource[itSource] >= '0' && cSource[itSource] <= '9') {
+          if (std::isdigit(cSource[itSource])) {
             cPurpose += cSource[itSource];
             itSource++;
             break;
@@ -531,7 +532,7 @@
   total = 0;
 
   while (isdigit(c)) {
-    total = 10 * total + (c - '0');  /* accumulate digit */
+    total = 10 * total + FXSYS_toDecimalDigit(c); /* accumulate digit */
     c = (int)(unsigned char)*nptr++; /* get next char */
   }
 
diff --git a/pdfium.gyp b/pdfium.gyp
index 0bf0742..58d6b8f 100644
--- a/pdfium.gyp
+++ b/pdfium.gyp
@@ -727,6 +727,7 @@
         'core/src/fxcrt/fx_basic_memmgr_unittest.cpp',
         'core/src/fxcrt/fx_basic_wstring_unittest.cpp',
         'core/src/fxcrt/fx_bidi_unittest.cpp',
+        'core/src/fxcrt/fx_extension_unittest.cpp',
         'core/src/fxcrt/fx_system_unittest.cpp',
         'testing/fx_string_testhelpers.h',
         'testing/fx_string_testhelpers.cpp',