// Copyright 2016 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 "core/fpdfapi/font/cpdf_font.h"

#include <algorithm>
#include <memory>
#include <utility>
#include <vector>

#include "build/build_config.h"
#include "constants/font_encodings.h"
#include "core/fpdfapi/font/cpdf_cidfont.h"
#include "core/fpdfapi/font/cpdf_fontencoding.h"
#include "core/fpdfapi/font/cpdf_fontglobals.h"
#include "core/fpdfapi/font/cpdf_tounicodemap.h"
#include "core/fpdfapi/font/cpdf_truetypefont.h"
#include "core/fpdfapi/font/cpdf_type1font.h"
#include "core/fpdfapi/font/cpdf_type3font.h"
#include "core/fpdfapi/parser/cpdf_array.h"
#include "core/fpdfapi/parser/cpdf_dictionary.h"
#include "core/fpdfapi/parser/cpdf_document.h"
#include "core/fpdfapi/parser/cpdf_name.h"
#include "core/fpdfapi/parser/cpdf_stream.h"
#include "core/fpdfapi/parser/cpdf_stream_acc.h"
#include "core/fxcrt/fx_codepage.h"
#include "core/fxcrt/fx_safe_types.h"
#include "core/fxcrt/stl_util.h"
#include "core/fxge/cfx_fontmapper.h"
#include "core/fxge/cfx_substfont.h"
#include "core/fxge/freetype/fx_freetype.h"
#include "core/fxge/fx_font.h"
#include "third_party/base/check.h"
#include "third_party/base/numerics/safe_conversions.h"

namespace {

constexpr size_t kChineseFontNameSize = 4;
const uint8_t kChineseFontNames[][kChineseFontNameSize] = {
    {0xCB, 0xCE, 0xCC, 0xE5},
    {0xBF, 0xAC, 0xCC, 0xE5},
    {0xBA, 0xDA, 0xCC, 0xE5},
    {0xB7, 0xC2, 0xCB, 0xCE},
    {0xD0, 0xC2, 0xCB, 0xCE}};

}  // namespace

CPDF_Font::CPDF_Font(CPDF_Document* pDocument,
                     RetainPtr<CPDF_Dictionary> pFontDict)
    : m_pDocument(pDocument),
      m_pFontDict(std::move(pFontDict)),
      m_BaseFontName(m_pFontDict->GetByteStringFor("BaseFont")) {}

CPDF_Font::~CPDF_Font() {
  if (m_pFontFile)
    m_pDocument->MaybePurgeFontFileStreamAcc(std::move(m_pFontFile));
}

bool CPDF_Font::IsType1Font() const {
  return false;
}

bool CPDF_Font::IsTrueTypeFont() const {
  return false;
}

bool CPDF_Font::IsType3Font() const {
  return false;
}

bool CPDF_Font::IsCIDFont() const {
  return false;
}

const CPDF_Type1Font* CPDF_Font::AsType1Font() const {
  return nullptr;
}

CPDF_Type1Font* CPDF_Font::AsType1Font() {
  return nullptr;
}

const CPDF_TrueTypeFont* CPDF_Font::AsTrueTypeFont() const {
  return nullptr;
}

CPDF_TrueTypeFont* CPDF_Font::AsTrueTypeFont() {
  return nullptr;
}

const CPDF_Type3Font* CPDF_Font::AsType3Font() const {
  return nullptr;
}

CPDF_Type3Font* CPDF_Font::AsType3Font() {
  return nullptr;
}

const CPDF_CIDFont* CPDF_Font::AsCIDFont() const {
  return nullptr;
}

CPDF_CIDFont* CPDF_Font::AsCIDFont() {
  return nullptr;
}

size_t CPDF_Font::CountChar(ByteStringView pString) const {
  return pString.GetLength();
}

#if BUILDFLAG(IS_APPLE)
int CPDF_Font::GlyphFromCharCodeExt(uint32_t charcode) {
  return GlyphFromCharCode(charcode, nullptr);
}
#endif

void CPDF_Font::WillBeDestroyed() {}

bool CPDF_Font::IsVertWriting() const {
  const CPDF_CIDFont* pCIDFont = AsCIDFont();
  return pCIDFont ? pCIDFont->IsVertWriting() : m_Font.IsVertical();
}

int CPDF_Font::AppendChar(char* buf, uint32_t charcode) const {
  *buf = static_cast<char>(charcode);
  return 1;
}

void CPDF_Font::AppendChar(ByteString* str, uint32_t charcode) const {
  char buf[4];
  int len = AppendChar(buf, charcode);
  *str += ByteStringView(buf, len);
}

WideString CPDF_Font::UnicodeFromCharCode(uint32_t charcode) const {
  if (!m_bToUnicodeLoaded)
    LoadUnicodeMap();

  return m_pToUnicodeMap ? m_pToUnicodeMap->Lookup(charcode) : WideString();
}

uint32_t CPDF_Font::CharCodeFromUnicode(wchar_t unicode) const {
  if (!m_bToUnicodeLoaded)
    LoadUnicodeMap();

  return m_pToUnicodeMap ? m_pToUnicodeMap->ReverseLookup(unicode) : 0;
}

bool CPDF_Font::HasFontWidths() const {
  return true;
}

void CPDF_Font::LoadFontDescriptor(const CPDF_Dictionary* pFontDesc) {
  m_Flags = pFontDesc->GetIntegerFor("Flags", FXFONT_NONSYMBOLIC);
  int ItalicAngle = 0;
  bool bExistItalicAngle = false;
  if (pFontDesc->KeyExist("ItalicAngle")) {
    ItalicAngle = pFontDesc->GetIntegerFor("ItalicAngle");
    bExistItalicAngle = true;
  }
  if (ItalicAngle < 0) {
    m_Flags |= FXFONT_ITALIC;
    m_ItalicAngle = ItalicAngle;
  }
  bool bExistStemV = false;
  if (pFontDesc->KeyExist("StemV")) {
    m_StemV = pFontDesc->GetIntegerFor("StemV");
    bExistStemV = true;
  }
  bool bExistAscent = false;
  if (pFontDesc->KeyExist("Ascent")) {
    m_Ascent = pFontDesc->GetIntegerFor("Ascent");
    bExistAscent = true;
  }
  bool bExistDescent = false;
  if (pFontDesc->KeyExist("Descent")) {
    m_Descent = pFontDesc->GetIntegerFor("Descent");
    bExistDescent = true;
  }
  bool bExistCapHeight = false;
  if (pFontDesc->KeyExist("CapHeight"))
    bExistCapHeight = true;
  if (bExistItalicAngle && bExistAscent && bExistCapHeight && bExistDescent &&
      bExistStemV) {
    m_Flags |= FXFONT_USEEXTERNATTR;
  }
  if (m_Descent > 10)
    m_Descent = -m_Descent;
  RetainPtr<const CPDF_Array> pBBox = pFontDesc->GetArrayFor("FontBBox");
  if (pBBox) {
    m_FontBBox.left = pBBox->GetIntegerAt(0);
    m_FontBBox.bottom = pBBox->GetIntegerAt(1);
    m_FontBBox.right = pBBox->GetIntegerAt(2);
    m_FontBBox.top = pBBox->GetIntegerAt(3);
  }

  RetainPtr<const CPDF_Stream> pFontFile = pFontDesc->GetStreamFor("FontFile");
  if (!pFontFile)
    pFontFile = pFontDesc->GetStreamFor("FontFile2");
  if (!pFontFile)
    pFontFile = pFontDesc->GetStreamFor("FontFile3");
  if (!pFontFile)
    return;

  const uint64_t key = pFontFile->KeyForCache();
  m_pFontFile = m_pDocument->GetFontFileStreamAcc(std::move(pFontFile));
  if (!m_pFontFile)
    return;

  if (!m_Font.LoadEmbedded(m_pFontFile->GetSpan(), IsVertWriting(), key))
    m_pDocument->MaybePurgeFontFileStreamAcc(std::move(m_pFontFile));
}

void CPDF_Font::CheckFontMetrics() {
  if (m_FontBBox.top == 0 && m_FontBBox.bottom == 0 && m_FontBBox.left == 0 &&
      m_FontBBox.right == 0) {
    FXFT_FaceRec* face = m_Font.GetFaceRec();
    if (face) {
      m_FontBBox.left = TT2PDF(FXFT_Get_Face_xMin(face), face);
      m_FontBBox.bottom = TT2PDF(FXFT_Get_Face_yMin(face), face);
      m_FontBBox.right = TT2PDF(FXFT_Get_Face_xMax(face), face);
      m_FontBBox.top = TT2PDF(FXFT_Get_Face_yMax(face), face);
      m_Ascent = TT2PDF(FXFT_Get_Face_Ascender(face), face);
      m_Descent = TT2PDF(FXFT_Get_Face_Descender(face), face);
    } else {
      bool bFirst = true;
      for (int i = 0; i < 256; i++) {
        FX_RECT rect = GetCharBBox(i);
        if (rect.left == rect.right) {
          continue;
        }
        if (bFirst) {
          m_FontBBox = rect;
          bFirst = false;
        } else {
          m_FontBBox.left = std::min(m_FontBBox.left, rect.left);
          m_FontBBox.top = std::max(m_FontBBox.top, rect.top);
          m_FontBBox.right = std::max(m_FontBBox.right, rect.right);
          m_FontBBox.bottom = std::min(m_FontBBox.bottom, rect.bottom);
        }
      }
    }
  }
  if (m_Ascent == 0 && m_Descent == 0) {
    FX_RECT rect = GetCharBBox('A');
    m_Ascent = rect.bottom == rect.top ? m_FontBBox.top : rect.top;
    rect = GetCharBBox('g');
    m_Descent = rect.bottom == rect.top ? m_FontBBox.bottom : rect.bottom;
  }
}

void CPDF_Font::LoadUnicodeMap() const {
  m_bToUnicodeLoaded = true;
  RetainPtr<const CPDF_Stream> pStream = m_pFontDict->GetStreamFor("ToUnicode");
  if (!pStream)
    return;

  m_pToUnicodeMap = std::make_unique<CPDF_ToUnicodeMap>(std::move(pStream));
}

int CPDF_Font::GetStringWidth(ByteStringView pString) {
  size_t offset = 0;
  int width = 0;
  while (offset < pString.GetLength())
    width += GetCharWidthF(GetNextChar(pString, &offset));
  return width;
}

// static
RetainPtr<CPDF_Font> CPDF_Font::GetStockFont(CPDF_Document* pDoc,
                                             ByteStringView name) {
  ByteString fontname(name);
  absl::optional<CFX_FontMapper::StandardFont> font_id =
      CFX_FontMapper::GetStandardFontName(&fontname);
  if (!font_id.has_value())
    return nullptr;

  auto* pFontGlobals = CPDF_FontGlobals::GetInstance();
  RetainPtr<CPDF_Font> pFont = pFontGlobals->Find(pDoc, font_id.value());
  if (pFont)
    return pFont;

  auto pDict = pDoc->New<CPDF_Dictionary>();
  pDict->SetNewFor<CPDF_Name>("Type", "Font");
  pDict->SetNewFor<CPDF_Name>("Subtype", "Type1");
  pDict->SetNewFor<CPDF_Name>("BaseFont", fontname);
  pDict->SetNewFor<CPDF_Name>("Encoding",
                              pdfium::font_encodings::kWinAnsiEncoding);
  pFont = CPDF_Font::Create(nullptr, std::move(pDict), nullptr);
  pFontGlobals->Set(pDoc, font_id.value(), pFont);
  return pFont;
}

// static
RetainPtr<CPDF_Font> CPDF_Font::Create(CPDF_Document* pDoc,
                                       RetainPtr<CPDF_Dictionary> pFontDict,
                                       FormFactoryIface* pFactory) {
  ByteString type = pFontDict->GetByteStringFor("Subtype");
  RetainPtr<CPDF_Font> pFont;
  if (type == "TrueType") {
    ByteString tag = pFontDict->GetByteStringFor("BaseFont").First(4);
    for (size_t i = 0; i < std::size(kChineseFontNames); ++i) {
      if (tag == ByteString(kChineseFontNames[i], kChineseFontNameSize)) {
        RetainPtr<const CPDF_Dictionary> pFontDesc =
            pFontDict->GetDictFor("FontDescriptor");
        if (!pFontDesc || !pFontDesc->KeyExist("FontFile2"))
          pFont = pdfium::MakeRetain<CPDF_CIDFont>(pDoc, std::move(pFontDict));
        break;
      }
    }
    if (!pFont)
      pFont = pdfium::MakeRetain<CPDF_TrueTypeFont>(pDoc, std::move(pFontDict));
  } else if (type == "Type3") {
    pFont = pdfium::MakeRetain<CPDF_Type3Font>(pDoc, std::move(pFontDict),
                                               pFactory);
  } else if (type == "Type0") {
    pFont = pdfium::MakeRetain<CPDF_CIDFont>(pDoc, std::move(pFontDict));
  } else {
    pFont = pdfium::MakeRetain<CPDF_Type1Font>(pDoc, std::move(pFontDict));
  }
  if (!pFont->Load())
    return nullptr;

  return pFont;
}

uint32_t CPDF_Font::GetNextChar(ByteStringView pString, size_t* pOffset) const {
  if (pString.IsEmpty())
    return 0;

  size_t& offset = *pOffset;
  return offset < pString.GetLength() ? pString[offset++] : pString.Back();
}

bool CPDF_Font::IsStandardFont() const {
  if (!IsType1Font())
    return false;
  if (m_pFontFile)
    return false;
  return AsType1Font()->IsBase14Font();
}

absl::optional<FX_Charset> CPDF_Font::GetSubstFontCharset() const {
  CFX_SubstFont* pFont = m_Font.GetSubstFont();
  if (!pFont)
    return absl::nullopt;
  return pFont->m_Charset;
}

// static
const char* CPDF_Font::GetAdobeCharName(
    FontEncoding base_encoding,
    const std::vector<ByteString>& charnames,
    uint32_t charcode) {
  if (charcode >= 256)
    return nullptr;

  if (!charnames.empty() && !charnames[charcode].IsEmpty())
    return charnames[charcode].c_str();

  const char* name = nullptr;
  if (base_encoding != FontEncoding::kBuiltin)
    name = CharNameFromPredefinedCharSet(base_encoding, charcode);
  if (!name)
    return nullptr;

  DCHECK(name[0]);
  return name;
}

uint32_t CPDF_Font::FallbackFontFromCharcode(uint32_t charcode) {
  if (m_FontFallbacks.empty()) {
    m_FontFallbacks.push_back(std::make_unique<CFX_Font>());
    FX_SAFE_INT32 safeWeight = m_StemV;
    safeWeight *= 5;
    m_FontFallbacks[0]->LoadSubst("Arial", IsTrueTypeFont(), m_Flags,
                                  safeWeight.ValueOrDefault(FXFONT_FW_NORMAL),
                                  m_ItalicAngle, FX_CodePage::kDefANSI,
                                  IsVertWriting());
  }
  return 0;
}

int CPDF_Font::FallbackGlyphFromCharcode(int fallbackFont, uint32_t charcode) {
  if (!fxcrt::IndexInBounds(m_FontFallbacks, fallbackFont))
    return -1;

  WideString str = UnicodeFromCharCode(charcode);
  uint32_t unicode = !str.IsEmpty() ? str[0] : charcode;
  int glyph =
      FT_Get_Char_Index(m_FontFallbacks[fallbackFont]->GetFaceRec(), unicode);
  if (glyph == 0)
    return -1;

  return glyph;
}

CFX_Font* CPDF_Font::GetFontFallback(int position) {
  if (position < 0 || static_cast<size_t>(position) >= m_FontFallbacks.size())
    return nullptr;
  return m_FontFallbacks[position].get();
}

// static
int CPDF_Font::TT2PDF(FT_Pos m, FXFT_FaceRec* face) {
  int upm = FXFT_Get_Face_UnitsPerEM(face);
  if (upm == 0)
    return pdfium::base::saturated_cast<int>(m);

  const double dm = (m * 1000.0 + upm / 2) / upm;
  return pdfium::base::saturated_cast<int>(dm);
}

// static
bool CPDF_Font::UseTTCharmap(FXFT_FaceRec* face,
                             int platform_id,
                             int encoding_id) {
  for (int i = 0; i < face->num_charmaps; i++) {
    if (FXFT_Get_Charmap_PlatformID(face->charmaps[i]) == platform_id &&
        FXFT_Get_Charmap_EncodingID(face->charmaps[i]) == encoding_id) {
      FT_Set_Charmap(face, face->charmaps[i]);
      return true;
    }
  }
  return false;
}

int CPDF_Font::GetFontWeight() const {
  FX_SAFE_INT32 safeStemV(m_StemV);
  if (m_StemV < 140)
    safeStemV *= 5;
  else
    safeStemV = safeStemV * 4 + 140;
  return safeStemV.ValueOrDefault(FXFONT_FW_NORMAL);
}
