// Copyright 2015 The PDFium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com

#include "xfa/fgas/font/cfgas_fontmgr.h"

#include <stdint.h>

#include <algorithm>
#include <array>
#include <iterator>
#include <memory>
#include <type_traits>
#include <utility>

#include "build/build_config.h"
#include "core/fxcrt/byteorder.h"
#include "core/fxcrt/cfx_read_only_container_stream.h"
#include "core/fxcrt/check.h"
#include "core/fxcrt/compiler_specific.h"
#include "core/fxcrt/containers/contains.h"
#include "core/fxcrt/data_vector.h"
#include "core/fxcrt/fixed_size_data_vector.h"
#include "core/fxcrt/fx_codepage.h"
#include "core/fxcrt/fx_extension.h"
#include "core/fxcrt/fx_memcpy_wrappers.h"
#include "core/fxcrt/fx_system.h"
#include "core/fxcrt/span.h"
#include "core/fxcrt/stl_util.h"
#include "core/fxge/cfx_cttnametable.h"
#include "core/fxge/cfx_font.h"
#include "core/fxge/cfx_fontmapper.h"
#include "core/fxge/cfx_fontmgr.h"
#include "core/fxge/cfx_gemodule.h"
#include "core/fxge/fx_font.h"
#include "core/fxge/fx_fontencoding.h"
#include "xfa/fgas/font/cfgas_gefont.h"
#include "xfa/fgas/font/fgas_fontutils.h"

#if BUILDFLAG(IS_WIN)
#include "core/fxcrt/win/win_util.h"
#endif

namespace {

uint32_t ShortFormHash(FX_CodePage wCodePage,
                       uint32_t dwFontStyles,
                       WideStringView wsFontFamily) {
  ByteString bsHash = ByteString::Format("%d, %d", wCodePage, dwFontStyles);
  bsHash += FX_UTF8Encode(wsFontFamily);
  return FX_HashCode_GetA(bsHash.AsStringView());
}

uint32_t LongFormHash(FX_CodePage wCodePage,
                      uint16_t wBitField,
                      uint32_t dwFontStyles,
                      WideStringView wsFontFamily) {
  ByteString bsHash =
      ByteString::Format("%d, %d, %d", wCodePage, wBitField, dwFontStyles);
  bsHash += FX_UTF8Encode(wsFontFamily);
  return FX_HashCode_GetA(bsHash.AsStringView());
}

}  // namespace

#if BUILDFLAG(IS_WIN)

namespace {

struct FX_FONTMATCHPARAMS {
  const wchar_t* pwsFamily;
  uint32_t dwFontStyles;
  uint32_t dwUSB;
  bool matchParagraphStyle;
  wchar_t wUnicode;
  FX_CodePage wCodePage;
};

int32_t GetSimilarityScore(FX_FONTDESCRIPTOR const* font,
                           uint32_t dwFontStyles) {
  int32_t iValue = 0;
  if (FontStyleIsSymbolic(dwFontStyles) ==
      FontStyleIsSymbolic(font->dwFontStyles)) {
    iValue += 64;
  }
  if (FontStyleIsFixedPitch(dwFontStyles) ==
      FontStyleIsFixedPitch(font->dwFontStyles)) {
    iValue += 32;
  }
  if (FontStyleIsSerif(dwFontStyles) == FontStyleIsSerif(font->dwFontStyles)) {
    iValue += 16;
  }
  if (FontStyleIsScript(dwFontStyles) ==
      FontStyleIsScript(font->dwFontStyles)) {
    iValue += 8;
  }
  return iValue;
}

const FX_FONTDESCRIPTOR* MatchDefaultFont(
    FX_FONTMATCHPARAMS* pParams,
    const std::deque<FX_FONTDESCRIPTOR>& fonts) {
  const FX_FONTDESCRIPTOR* pBestFont = nullptr;
  int32_t iBestSimilar = 0;
  for (const auto& font : fonts) {
    if (FontStyleIsForceBold(font.dwFontStyles) &&
        FontStyleIsItalic(font.dwFontStyles)) {
      continue;
    }

    if (pParams->pwsFamily) {
      if (FXSYS_wcsicmp(pParams->pwsFamily, font.wsFontFace)) {
        continue;
      }
      if (font.uCharSet == FX_Charset::kSymbol) {
        return &font;
      }
    }
    if (font.uCharSet == FX_Charset::kSymbol) {
      continue;
    }
    if (pParams->wCodePage != FX_CodePage::kFailure) {
      if (FX_GetCodePageFromCharset(font.uCharSet) != pParams->wCodePage) {
        continue;
      }
    } else {
      if (pParams->dwUSB < 128) {
        uint32_t dwByte = pParams->dwUSB / 32;
        uint32_t dwUSB = 1 << (pParams->dwUSB % 32);
        if ((font.FontSignature.fsUsb[dwByte] & dwUSB) == 0) {
          continue;
        }
      }
    }
    if (pParams->matchParagraphStyle) {
      if ((font.dwFontStyles & 0x0F) == (pParams->dwFontStyles & 0x0F)) {
        return &font;
      }
      continue;
    }
    if (pParams->pwsFamily) {
      if (FXSYS_wcsicmp(pParams->pwsFamily, font.wsFontFace) == 0) {
        return &font;
      }
    }
    int32_t iSimilarValue = GetSimilarityScore(&font, pParams->dwFontStyles);
    if (iBestSimilar < iSimilarValue) {
      iBestSimilar = iSimilarValue;
      pBestFont = &font;
    }
  }
  return iBestSimilar < 1 ? nullptr : pBestFont;
}

uint32_t GetGdiFontStyles(const LOGFONTW& lf) {
  uint32_t dwStyles = 0;
  if ((lf.lfPitchAndFamily & 0x03) == FIXED_PITCH) {
    dwStyles |= pdfium::kFontStyleFixedPitch;
  }
  uint8_t nFamilies = lf.lfPitchAndFamily & 0xF0;
  if (nFamilies == FF_ROMAN) {
    dwStyles |= pdfium::kFontStyleSerif;
  }
  if (nFamilies == FF_SCRIPT) {
    dwStyles |= pdfium::kFontStyleScript;
  }
  if (lf.lfCharSet == SYMBOL_CHARSET) {
    dwStyles |= pdfium::kFontStyleSymbolic;
  }
  return dwStyles;
}

int32_t CALLBACK GdiFontEnumProc(ENUMLOGFONTEX* lpelfe,
                                 NEWTEXTMETRICEX* lpntme,
                                 DWORD dwFontType,
                                 LPARAM lParam) {
  if (dwFontType != TRUETYPE_FONTTYPE) {
    return 1;
  }
  const LOGFONTW& lf = ((LPENUMLOGFONTEXW)lpelfe)->elfLogFont;
  if (lf.lfFaceName[0] == L'@') {
    return 1;
  }
  FX_FONTDESCRIPTOR font = {};  // Aggregate initialization.
  static_assert(std::is_aggregate_v<decltype(font)>);
  font.uCharSet = FX_GetCharsetFromInt(lf.lfCharSet);
  font.dwFontStyles = GetGdiFontStyles(lf);
  static_assert(sizeof(font.wsFontFace) == sizeof(lf.lfFaceName));
  pdfium::span(font.wsFontFace).copy_from_nonoverlapping(lf.lfFaceName);
  static_assert(sizeof(font.FontSignature) == sizeof(lpntme->ntmFontSig));
  pdfium::byte_span_from_ref(font.FontSignature)
      .copy_from_nonoverlapping(pdfium::byte_span_from_ref(lpntme->ntmFontSig));
  reinterpret_cast<std::deque<FX_FONTDESCRIPTOR>*>(lParam)->push_back(font);
  return 1;
}

std::deque<FX_FONTDESCRIPTOR> EnumGdiFonts(WideStringView face_name) {
  std::deque<FX_FONTDESCRIPTOR> fonts;
  if (!pdfium::IsUser32AndGdi32Available()) {
    // Without GDI32 and User32, GetDC / EnumFontFamiliesExW / ReleaseDC all
    // fail.
    return fonts;
  }

  LOGFONTW lfFind = {};  // Aggregate initialization.
  static_assert(std::is_aggregate_v<decltype(lfFind)>);
  lfFind.lfCharSet = DEFAULT_CHARSET;
  if (!face_name.IsEmpty()) {
    pdfium::span<wchar_t> dest_face_name_with_null(lfFind.lfFaceName);
    pdfium::span<const wchar_t> src_face_name = face_name.span();
    src_face_name = src_face_name.first(
        std::min(dest_face_name_with_null.size() - 1, src_face_name.size()));
    dest_face_name_with_null.copy_prefix_from(src_face_name);
    dest_face_name_with_null[src_face_name.size() + 1] = 0;
  }
  HDC hdc = ::GetDC(nullptr);
  CHECK(hdc);
  EnumFontFamiliesExW(hdc, (LPLOGFONTW)&lfFind, (FONTENUMPROCW)GdiFontEnumProc,
                      (LPARAM)&fonts, 0);
  ::ReleaseDC(nullptr, hdc);
  return fonts;
}

}  // namespace

CFGAS_FontMgr::CFGAS_FontMgr() : font_faces_(EnumGdiFonts({})) {}

CFGAS_FontMgr::~CFGAS_FontMgr() = default;

bool CFGAS_FontMgr::EnumFonts() {
  return true;
}

RetainPtr<CFGAS_GEFont> CFGAS_FontMgr::GetFontByUnicodeImpl(
    wchar_t wUnicode,
    uint32_t dwFontStyles,
    const wchar_t* pszFontFamily,
    uint32_t dwHash,
    FX_CodePage wCodePage,
    uint16_t wBitField) {
  const FX_FONTDESCRIPTOR* pFD = FindFont(pszFontFamily, dwFontStyles, false,
                                          wCodePage, wBitField, wUnicode);
  if (!pFD && pszFontFamily) {
    pFD =
        FindFont(nullptr, dwFontStyles, false, wCodePage, wBitField, wUnicode);
  }
  if (!pFD) {
    return nullptr;
  }

  FX_CodePage newCodePage = FX_GetCodePageFromCharset(pFD->uCharSet);
  RetainPtr<CFGAS_GEFont> font =
      CFGAS_GEFont::LoadFont(pFD->wsFontFace, dwFontStyles, newCodePage);
  if (!font) {
    return nullptr;
  }

  font->SetLogicalFontStyle(dwFontStyles);
  if (!font->VerifyUnicode(wUnicode)) {
    failed_unicodes_set_.insert(wUnicode);
    return nullptr;
  }

  hash_2fonts_[dwHash].push_back(font);
  return font;
}

const FX_FONTDESCRIPTOR* CFGAS_FontMgr::FindFont(const wchar_t* pszFontFamily,
                                                 uint32_t dwFontStyles,
                                                 bool matchParagraphStyle,
                                                 FX_CodePage wCodePage,
                                                 uint32_t dwUSB,
                                                 wchar_t wUnicode) {
  FX_FONTMATCHPARAMS params = {};  // Aggregate initialization.
  static_assert(std::is_aggregate_v<decltype(params)>);
  params.dwUSB = dwUSB;
  params.wUnicode = wUnicode;
  params.wCodePage = wCodePage;
  params.pwsFamily = pszFontFamily;
  params.dwFontStyles = dwFontStyles;
  params.matchParagraphStyle = matchParagraphStyle;

  const FX_FONTDESCRIPTOR* pDesc = MatchDefaultFont(&params, font_faces_);
  if (pDesc) {
    return pDesc;
  }

  if (!pszFontFamily) {
    return nullptr;
  }

  // Use a named object to store the returned value of EnumGdiFonts() instead
  // of using a temporary object. This can prevent use-after-free issues since
  // pDesc may point to one of std::deque object's elements.
  std::deque<FX_FONTDESCRIPTOR> namedFonts = EnumGdiFonts(pszFontFamily);
  params.pwsFamily = nullptr;
  pDesc = MatchDefaultFont(&params, namedFonts);
  if (!pDesc) {
    return nullptr;
  }

  auto it = std::find(font_faces_.rbegin(), font_faces_.rend(), *pDesc);
  if (it != font_faces_.rend()) {
    return &*it;
  }

  font_faces_.push_back(*pDesc);
  return &font_faces_.back();
}

#else  // BUILDFLAG(IS_WIN)

namespace {

constexpr auto kCodePages =
    std::to_array<const FX_CodePage>({FX_CodePage::kMSWin_WesternEuropean,
                                      FX_CodePage::kMSWin_EasternEuropean,
                                      FX_CodePage::kMSWin_Cyrillic,
                                      FX_CodePage::kMSWin_Greek,
                                      FX_CodePage::kMSWin_Turkish,
                                      FX_CodePage::kMSWin_Hebrew,
                                      FX_CodePage::kMSWin_Arabic,
                                      FX_CodePage::kMSWin_Baltic,
                                      FX_CodePage::kMSWin_Vietnamese,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kMSDOS_Thai,
                                      FX_CodePage::kShiftJIS,
                                      FX_CodePage::kChineseSimplified,
                                      FX_CodePage::kHangul,
                                      FX_CodePage::kChineseTraditional,
                                      FX_CodePage::kJohab,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kDefANSI,
                                      FX_CodePage::kMSDOS_Greek2,
                                      FX_CodePage::kMSDOS_Russian,
                                      FX_CodePage::kMSDOS_Norwegian,
                                      FX_CodePage::kMSDOS_Arabic,
                                      FX_CodePage::kMSDOS_FrenchCanadian,
                                      FX_CodePage::kMSDOS_Hebrew,
                                      FX_CodePage::kMSDOS_Icelandic,
                                      FX_CodePage::kMSDOS_Portuguese,
                                      FX_CodePage::kMSDOS_Turkish,
                                      FX_CodePage::kMSDOS_Cyrillic,
                                      FX_CodePage::kMSDOS_EasternEuropean,
                                      FX_CodePage::kMSDOS_Baltic,
                                      FX_CodePage::kMSDOS_Greek1,
                                      FX_CodePage::kArabic_ASMO708,
                                      FX_CodePage::kMSDOS_WesternEuropean,
                                      FX_CodePage::kMSDOS_US});

uint16_t FX_GetCodePageBit(FX_CodePage wCodePage) {
  for (size_t i = 0; i < kCodePages.size(); ++i) {
    if (kCodePages[i] == wCodePage) {
      return static_cast<uint16_t>(i);
    }
  }
  return static_cast<uint16_t>(-1);
}

uint16_t FX_GetUnicodeBit(wchar_t wcUnicode) {
  const FGAS_FONTUSB* x = FGAS_GetUnicodeBitField(wcUnicode);
  return x ? x->wBitField : FGAS_FONTUSB::kNoBitField;
}


RetainPtr<CFX_ReadOnlyFixedSizeDataVectorStream> CreateFontStream(
    CFX_FontMapper* font_mapper,
    size_t index) {
  FixedSizeDataVector<uint8_t> buffer = font_mapper->RawBytesForIndex(index);
  if (buffer.empty()) {
    return nullptr;
  }
  return pdfium::MakeRetain<CFX_ReadOnlyFixedSizeDataVectorStream>(
      std::move(buffer));
}

RetainPtr<CFX_ReadOnlyFixedSizeDataVectorStream> CreateFontStream(
    const ByteString& bsFaceName) {
  CFX_FontMgr* font_mgr = CFX_GEModule::Get()->GetFontMgr();
  CFX_FontMapper* font_mapper = font_mgr->GetBuiltinMapper();
  font_mapper->LoadInstalledFonts();

  for (size_t i = 0; i < font_mapper->GetFaceSize(); ++i) {
    if (font_mapper->GetFaceName(i) == bsFaceName) {
      return CreateFontStream(font_mapper, i);
    }
  }
  return nullptr;
}

bool IsPartName(const WideString& name1, const WideString& name2) {
  return name1.Contains(name2.AsStringView());
}

int32_t CalcPenalty(CFGAS_FontDescriptor* pInstalled,
                    FX_CodePage wCodePage,
                    uint32_t dwFontStyles,
                    const WideString& FontName,
                    wchar_t wcUnicode) {
  int32_t nPenalty = 30000;
  if (FontName.GetLength() != 0) {
    if (FontName != pInstalled->face_name_) {
      size_t i;
      pdfium::span<const WideString> names =
          pInstalled->family_names_->GetFamilyNames();
      for (i = 0; i < names.size(); ++i) {
        if (names[i] == FontName) {
          break;
        }
      }
      if (i == names.size()) {
        nPenalty += 0xFFFF;
      } else {
        nPenalty -= 28000;
      }
    } else {
      nPenalty -= 30000;
    }
    if (nPenalty == 30000 && !IsPartName(pInstalled->face_name_, FontName)) {
      size_t i;
      pdfium::span<const WideString> names =
          pInstalled->family_names_->GetFamilyNames();
      for (i = 0; i < names.size(); i++) {
        if (IsPartName(names[i], FontName)) {
          break;
        }
      }
      if (i == names.size()) {
        nPenalty += 0xFFFF;
      } else {
        nPenalty -= 26000;
      }
    } else {
      nPenalty -= 27000;
    }
  }
  uint32_t dwStyleMask = pInstalled->font_styles_ ^ dwFontStyles;
  if (FontStyleIsForceBold(dwStyleMask)) {
    nPenalty += 4500;
  }
  if (FontStyleIsFixedPitch(dwStyleMask)) {
    nPenalty += 10000;
  }
  if (FontStyleIsItalic(dwStyleMask)) {
    nPenalty += 10000;
  }
  if (FontStyleIsSerif(dwStyleMask)) {
    nPenalty += 500;
  }
  if (FontStyleIsSymbolic(dwStyleMask)) {
    nPenalty += 0xFFFF;
  }
  if (nPenalty >= 0xFFFF) {
    return 0xFFFF;
  }

  uint16_t wBit =
      (wCodePage == FX_CodePage::kDefANSI || wCodePage == FX_CodePage::kFailure)
          ? static_cast<uint16_t>(-1)
          : FX_GetCodePageBit(wCodePage);
  if (wBit != static_cast<uint16_t>(-1)) {
    DCHECK(wBit < 64);
    if ((pInstalled->csb_[wBit / 32] & (1 << (wBit % 32))) == 0) {
      nPenalty += 0xFFFF;
    } else {
      nPenalty -= 60000;
    }
  }
  wBit = (wcUnicode == 0 || wcUnicode == 0xFFFE) ? FGAS_FONTUSB::kNoBitField
                                                 : FX_GetUnicodeBit(wcUnicode);
  if (wBit != FGAS_FONTUSB::kNoBitField) {
    DCHECK(wBit < 128);
    if ((pInstalled->usb_[wBit / 32] & (1 << (wBit % 32))) == 0) {
      nPenalty += 0xFFFF;
    } else {
      nPenalty -= 60000;
    }
  }
  return nPenalty;
}

}  // namespace

CFGAS_FontDescriptor::CFGAS_FontDescriptor() = default;

CFGAS_FontDescriptor::~CFGAS_FontDescriptor() = default;

bool CFGAS_FontDescriptor::VerifyUnicode(wchar_t unicode) {
  if (!face_) {
    RetainPtr<CFX_ReadOnlyFixedSizeDataVectorStream> file_read =
        CreateFontStream(face_name_.ToUTF8());
    if (!file_read) {
      return false;
    }
    RetainPtr<CFX_Face> ft_face =
        CFX_Face::New(nullptr, file_read, face_index_);
    if (!ft_face) {
      return false;
    }
    face_ = std::move(ft_face);
  }
  return face_->SelectCharMap(fxge::FontEncoding::kUnicode) &&
         face_->GetCharIndex(unicode);
}

CFGAS_FontMgr::CFGAS_FontMgr() = default;

CFGAS_FontMgr::~CFGAS_FontMgr() = default;

void CFGAS_FontMgr::EnsureFontsEnumerated() {
  if (fonts_enumerated_) {
    return;
  }
  fonts_enumerated_ = true;
  EnumFontsFromFontMapper();
}

bool CFGAS_FontMgr::EnumFontsFromFontMapper() {
  CFX_FontMapper* font_mapper =
      CFX_GEModule::Get()->GetFontMgr()->GetBuiltinMapper();
  font_mapper->LoadInstalledFonts();

  for (size_t i = 0; i < font_mapper->GetFaceSize(); ++i) {
    RetainPtr<CFX_ReadOnlyFixedSizeDataVectorStream> font_stream =
        CreateFontStream(font_mapper, i);
    if (!font_stream) {
      continue;
    }

    WideString face_name =
        WideString::FromDefANSI(font_mapper->GetFaceName(i).AsStringView());
    RegisterFaces(font_stream, face_name);
  }

  return !installed_fonts_.empty();
}

bool CFGAS_FontMgr::EnumFonts() {
  return EnumFontsFromFontMapper();
}

RetainPtr<CFGAS_GEFont> CFGAS_FontMgr::GetFontByUnicodeImpl(
    wchar_t wUnicode,
    uint32_t dwFontStyles,
    const wchar_t* pszFontFamily,
    uint32_t dwHash,
    FX_CodePage wCodePage,
    uint16_t /* wBitField*/) {
  if (!pdfium::Contains(hash_to_candidates_map_, dwHash)) {
    hash_to_candidates_map_[dwHash] =
        MatchFonts(wCodePage, dwFontStyles, pszFontFamily, wUnicode);
  }
  for (const auto& info : hash_to_candidates_map_[dwHash]) {
    CFGAS_FontDescriptor* pDesc = info.font;
    if (!pDesc->VerifyUnicode(wUnicode)) {
      continue;
    }
    RetainPtr<CFGAS_GEFont> font =
        LoadFontInternal(pDesc->face_name_, pDesc->face_index_);
    if (!font) {
      continue;
    }
    font->SetLogicalFontStyle(dwFontStyles);
    hash_2fonts_[dwHash].push_back(font);
    return font;
  }
  if (!pszFontFamily) {
    failed_unicodes_set_.insert(wUnicode);
  }
  return nullptr;
}

RetainPtr<CFGAS_GEFont> CFGAS_FontMgr::LoadFontInternal(
    const WideString& face_name,
    int32_t face_index) {
  RetainPtr<CFX_ReadOnlyFixedSizeDataVectorStream> font_stream =
      CreateFontStream(face_name.ToUTF8());
  if (!font_stream) {
    return nullptr;
  }
  auto internal_font = std::make_unique<CFX_Font>();
  if (!internal_font->LoadFaceFromSpanStream(font_stream, face_index, 0)) {
    return nullptr;
  }
  return CFGAS_GEFont::LoadFont(std::move(internal_font));
}

std::vector<CFGAS_FontDescriptor::Rank> CFGAS_FontMgr::MatchFonts(
    FX_CodePage wCodePage,
    uint32_t dwFontStyles,
    const WideString& FontName,
    wchar_t wcUnicode) {
  EnsureFontsEnumerated();
  std::vector<CFGAS_FontDescriptor::Rank> matched_fonts;
  for (const auto& font : installed_fonts_) {
    int32_t nPenalty =
        CalcPenalty(font.get(), wCodePage, dwFontStyles, FontName, wcUnicode);
    if (nPenalty >= 0xffff) {
      continue;
    }
    matched_fonts.push_back({font.get(), nPenalty});
    if (matched_fonts.size() == 0xffff) {
      break;
    }
  }
  std::stable_sort(matched_fonts.begin(), matched_fonts.end());
  return matched_fonts;
}

void CFGAS_FontMgr::RegisterFace(RetainPtr<CFX_Face> face,
                                 int face_index,
                                 const WideString& wsFaceName) {
  if (!face->IsScalable()) {
    return;
  }

  auto font = std::make_unique<CFGAS_FontDescriptor>();
  font->font_styles_ |= face->GetFontStyle();

  std::optional<std::array<uint32_t, 4>> unicode_range =
      face->GetOs2UnicodeRange();
  if (unicode_range.has_value()) {
    fxcrt::Copy(unicode_range.value(), font->usb_);
  }

  std::optional<std::array<uint32_t, 2>> code_page_range =
      face->GetOs2CodePageRange();
  if (code_page_range.has_value()) {
    fxcrt::Copy(code_page_range.value(), font->csb_);
  }

  font->family_names_ = face->ParseNameTable();
  if (!font->family_names_) {
    font->family_names_ = std::make_unique<CFX_CTTNameTable>();
  }
  font->family_names_->AddFamilyName(
      WideString::FromUTF8(face->GetFamilyName().AsStringView()));
  font->face_name_ = wsFaceName;
  font->face_index_ = face_index;
  installed_fonts_.push_back(std::move(font));
}

void CFGAS_FontMgr::RegisterFaces(
    const RetainPtr<CFX_ReadOnlyFixedSizeDataVectorStream>& font_stream,
    const WideString& face_name) {
  int index = 0;
  int num_faces = 0;
  do {
    RetainPtr<CFX_Face> face = CFX_Face::New(nullptr, font_stream, index);
    if (!face) {
      ++index;
      continue;
    }
    // All faces keep number of faces. It can be retrieved from any one face.
    if (num_faces == 0) {
      num_faces = face->GetNumFaces();
    }
    RegisterFace(face, index, face_name);
    ++index;
  } while (index < num_faces);
}

#endif  // BUILDFLAG(IS_WIN)

RetainPtr<CFGAS_GEFont> CFGAS_FontMgr::GetFontByCodePage(
    FX_CodePage wCodePage,
    uint32_t dwFontStyles,
    const wchar_t* pszFontFamily) {
  uint32_t dwHash = ShortFormHash(wCodePage, dwFontStyles, pszFontFamily);
  auto* font_vector = &hash_2fonts_[dwHash];
  if (!font_vector->empty()) {
    for (auto iter = font_vector->begin(); iter != font_vector->end(); ++iter) {
      if (*iter != nullptr) {
        return *iter;
      }
    }
    return nullptr;
  }

#if BUILDFLAG(IS_WIN)
  const FX_FONTDESCRIPTOR* pFD =
      FindFont(pszFontFamily, dwFontStyles, true, wCodePage,
               FGAS_FONTUSB::kNoBitField, 0);
  if (!pFD) {
    pFD = FindFont(nullptr, dwFontStyles, true, wCodePage,
                   FGAS_FONTUSB::kNoBitField, 0);
  }
  if (!pFD) {
    pFD = FindFont(nullptr, dwFontStyles, false, wCodePage,
                   FGAS_FONTUSB::kNoBitField, 0);
  }
  if (!pFD) {
    return nullptr;
  }

  RetainPtr<CFGAS_GEFont> font =
      CFGAS_GEFont::LoadFont(pFD->wsFontFace, dwFontStyles, wCodePage);
#else   // BUILDFLAG(IS_WIN)
  if (!pdfium::Contains(hash_to_candidates_map_, dwHash)) {
    hash_to_candidates_map_[dwHash] =
        MatchFonts(wCodePage, dwFontStyles, WideString(pszFontFamily), 0);
  }
  if (hash_to_candidates_map_[dwHash].empty()) {
    return nullptr;
  }

  CFGAS_FontDescriptor* pDesc = hash_to_candidates_map_[dwHash].front().font;
  RetainPtr<CFGAS_GEFont> font =
      LoadFontInternal(pDesc->face_name_, pDesc->face_index_);
#endif  // BUILDFLAG(IS_WIN)

  if (!font) {
    return nullptr;
  }

  font->SetLogicalFontStyle(dwFontStyles);
  font_vector->push_back(font);
  return font;
}

RetainPtr<CFGAS_GEFont> CFGAS_FontMgr::GetFontByUnicode(
    wchar_t wUnicode,
    uint32_t dwFontStyles,
    const wchar_t* pszFontFamily) {
  if (pdfium::Contains(failed_unicodes_set_, wUnicode)) {
    return nullptr;
  }

  const FGAS_FONTUSB* x = FGAS_GetUnicodeBitField(wUnicode);
  FX_CodePage wCodePage = x ? x->wCodePage : FX_CodePage::kFailure;
  uint16_t wBitField = x ? x->wBitField : FGAS_FONTUSB::kNoBitField;
  uint32_t dwHash =
      wCodePage == FX_CodePage::kFailure
          ? LongFormHash(wCodePage, wBitField, dwFontStyles, pszFontFamily)
          : ShortFormHash(wCodePage, dwFontStyles, pszFontFamily);
  for (auto& font : hash_2fonts_[dwHash]) {
    if (font->VerifyUnicode(wUnicode)) {
      return font;
    }
  }
  return GetFontByUnicodeImpl(wUnicode, dwFontStyles, pszFontFamily, dwHash,
                              wCodePage, wBitField);
}

RetainPtr<CFGAS_GEFont> CFGAS_FontMgr::LoadFont(const wchar_t* pszFontFamily,
                                                uint32_t dwFontStyles,
                                                FX_CodePage wCodePage) {
#if BUILDFLAG(IS_WIN)
  uint32_t dwHash = ShortFormHash(wCodePage, dwFontStyles, pszFontFamily);
  std::vector<RetainPtr<CFGAS_GEFont>>* font_array = &hash_2fonts_[dwHash];
  if (!font_array->empty()) {
    return font_array->front();
  }

  const FX_FONTDESCRIPTOR* pFD =
      FindFont(pszFontFamily, dwFontStyles, true, wCodePage,
               FGAS_FONTUSB::kNoBitField, 0);
  if (!pFD) {
    pFD = FindFont(pszFontFamily, dwFontStyles, false, wCodePage,
                   FGAS_FONTUSB::kNoBitField, 0);
  }
  if (!pFD) {
    return nullptr;
  }

  RetainPtr<CFGAS_GEFont> font =
      CFGAS_GEFont::LoadFont(pFD->wsFontFace, dwFontStyles, wCodePage);
  if (!font) {
    return nullptr;
  }

  font->SetLogicalFontStyle(dwFontStyles);
  font_array->push_back(font);
  return font;
#else   // BUILDFLAG(IS_WIN)
  return GetFontByCodePage(wCodePage, dwFontStyles, pszFontFamily);
#endif  // BUILDFLAG(IS_WIN)
}
