Optimize CFX_FolderFontInfo::FindFont() for an exact match case.

Avoid a linear search where possible.

-- Make some free functions be methods of FontFaceInfo.

Fixed: pdfium:2135
Change-Id: I3185c59ef347996ea46792a5d67e0be8f4c787ef
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/117232
Reviewed-by: Thomas Sepez <tsepez@google.com>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fxge/cfx_folderfontinfo.cpp b/core/fxge/cfx_folderfontinfo.cpp
index 6985bb3..b7eec78 100644
--- a/core/fxge/cfx_folderfontinfo.cpp
+++ b/core/fxge/cfx_folderfontinfo.cpp
@@ -12,6 +12,7 @@
 
 #include "build/build_config.h"
 #include "core/fxcrt/byteorder.h"
+#include "core/fxcrt/check_op.h"
 #include "core/fxcrt/containers/contains.h"
 #include "core/fxcrt/fx_codepage.h"
 #include "core/fxcrt/fx_extension.h"
@@ -123,29 +124,6 @@
   return 0;
 }
 
-int32_t GetSimilarValue(int weight,
-                        bool bItalic,
-                        int pitch_family,
-                        uint32_t style,
-                        bool bMatchName,
-                        size_t familyNameLength,
-                        size_t bsNameLength) {
-  int32_t iSimilarValue = 0;
-  if (bMatchName && (familyNameLength == bsNameLength))
-    iSimilarValue += 4;
-  if (FontStyleIsForceBold(style) == (weight > 400))
-    iSimilarValue += 16;
-  if (FontStyleIsItalic(style) == bItalic)
-    iSimilarValue += 16;
-  if (FontStyleIsSerif(style) == FontFamilyIsRoman(pitch_family))
-    iSimilarValue += 16;
-  if (FontStyleIsScript(style) == FontFamilyIsScript(pitch_family))
-    iSimilarValue += 8;
-  if (FontStyleIsFixedPitch(style) == FontFamilyIsFixedPitch(pitch_family))
-    iSimilarValue += 8;
-  return iSimilarValue;
-}
-
 }  // namespace
 
 CFX_FolderFontInfo::CFX_FolderFontInfo() = default;
@@ -324,23 +302,41 @@
                                    const ByteString& family,
                                    bool bMatchName) {
   FontFaceInfo* pFind = nullptr;
-
-  ByteStringView bsFamily = family.AsStringView();
   uint32_t charset_flag = GetCharset(charset);
+
   int32_t iBestSimilar = 0;
+  if (bMatchName) {
+    // Try a direct lookup for either a perfect score or to determine a
+    // baseline similarity score.
+    auto direct_it = m_FontList.find(family);
+    if (direct_it != m_FontList.end()) {
+      FontFaceInfo* pFont = direct_it->second.get();
+      if (pFont->IsEligibleForFindFont(charset_flag, charset)) {
+        iBestSimilar =
+            pFont->SimilarityScore(weight, bItalic, pitch_family, bMatchName);
+        if (iBestSimilar == FontFaceInfo::kSimilarityScoreMax) {
+          return pFont;
+        }
+        pFind = pFont;
+      }
+    }
+  }
+  // Try and find a better match. Since FindFamilyNameMatch() is expensive,
+  // avoid calling it unless there might be a better match.
+  ByteStringView bsFamily = family.AsStringView();
   for (const auto& it : m_FontList) {
     const ByteString& bsName = it.first;
     FontFaceInfo* pFont = it.second.get();
-    if (!(pFont->m_Charsets & charset_flag) && charset != FX_Charset::kDefault)
+    if (!pFont->IsEligibleForFindFont(charset_flag, charset)) {
       continue;
-
-    if (bMatchName && !FindFamilyNameMatch(bsFamily, bsName))
-      continue;
-
-    int32_t iSimilarValue =
-        GetSimilarValue(weight, bItalic, pitch_family, pFont->m_Styles,
-                        bMatchName, bsFamily.GetLength(), bsName.GetLength());
+    }
+    int32_t iSimilarValue = pFont->SimilarityScore(
+        weight, bItalic, pitch_family,
+        bMatchName && bsFamily.GetLength() == bsName.GetLength());
     if (iSimilarValue > iBestSimilar) {
+      if (bMatchName && !FindFamilyNameMatch(bsFamily, bsName)) {
+        continue;
+      }
       iBestSimilar = iSimilarValue;
       pFind = pFont;
     }
@@ -436,3 +432,37 @@
       m_FontTables(fontTables),
       m_FontOffset(fontOffset),
       m_FileSize(fileSize) {}
+
+bool CFX_FolderFontInfo::FontFaceInfo::IsEligibleForFindFont(
+    uint32_t flag,
+    FX_Charset charset) const {
+  return (m_Charsets & flag) || charset == FX_Charset::kDefault;
+}
+
+int32_t CFX_FolderFontInfo::FontFaceInfo::SimilarityScore(
+    int weight,
+    bool italic,
+    int pitch_family,
+    bool exact_match_bonus) const {
+  int32_t score = 0;
+  if (FontStyleIsForceBold(m_Styles) == (weight > 400)) {
+    score += 16;
+  }
+  if (FontStyleIsItalic(m_Styles) == italic) {
+    score += 16;
+  }
+  if (FontStyleIsSerif(m_Styles) == FontFamilyIsRoman(pitch_family)) {
+    score += 16;
+  }
+  if (FontStyleIsScript(m_Styles) == FontFamilyIsScript(pitch_family)) {
+    score += 8;
+  }
+  if (FontStyleIsFixedPitch(m_Styles) == FontFamilyIsFixedPitch(pitch_family)) {
+    score += 8;
+  }
+  if (exact_match_bonus) {
+    score += 4;
+  }
+  DCHECK_LE(score, kSimilarityScoreMax);
+  return score;
+}
diff --git a/core/fxge/cfx_folderfontinfo.h b/core/fxge/cfx_folderfontinfo.h
index 63d73c3..ec53226 100644
--- a/core/fxge/cfx_folderfontinfo.h
+++ b/core/fxge/cfx_folderfontinfo.h
@@ -50,12 +50,20 @@
 
   class FontFaceInfo {
    public:
+    static constexpr int32_t kSimilarityScoreMax = 68;
+
     FontFaceInfo(ByteString filePath,
                  ByteString faceName,
                  ByteString fontTables,
                  uint32_t fontOffset,
                  uint32_t fileSize);
 
+    bool IsEligibleForFindFont(uint32_t flag, FX_Charset charset) const;
+    int32_t SimilarityScore(int weight,
+                            bool italic,
+                            int pitch_family,
+                            bool exact_match_bonus) const;
+
     const ByteString m_FilePath;
     const ByteString m_FaceName;
     const ByteString m_FontTables;