// Copyright 2017 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.

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

#include "xfa/fxfa/cxfa_pdffontmgr.h"

#include <algorithm>

#include "core/fpdfapi/font/cpdf_font.h"
#include "xfa/fxfa/cxfa_ffapp.h"

namespace {

// The 5 names per entry are: PsName, Normal, Bold, Italic, BoldItalic.
const char* const g_XFAPDFFontName[][5] = {
    {"Adobe PI Std", "AdobePIStd", "AdobePIStd", "AdobePIStd", "AdobePIStd"},
    {"Myriad Pro Light", "MyriadPro-Light", "MyriadPro-Semibold",
     "MyriadPro-LightIt", "MyriadPro-SemiboldIt"},
};

}  // namespace

CXFA_PDFFontMgr::CXFA_PDFFontMgr(CXFA_FFDoc* pDoc) : m_pDoc(pDoc) {}

CXFA_PDFFontMgr::~CXFA_PDFFontMgr() {}

CFX_RetainPtr<CFGAS_GEFont> CXFA_PDFFontMgr::FindFont(
    const CFX_ByteString& strPsName,
    bool bBold,
    bool bItalic,
    CPDF_Font** pDstPDFFont,
    bool bStrictMatch) {
  CPDF_Document* pDoc = m_pDoc->GetPDFDoc();
  if (!pDoc)
    return nullptr;

  CPDF_Dictionary* pFontSetDict =
      pDoc->GetRoot()->GetDictFor("AcroForm")->GetDictFor("DR");
  if (!pFontSetDict)
    return nullptr;

  pFontSetDict = pFontSetDict->GetDictFor("Font");
  if (!pFontSetDict)
    return nullptr;

  CFX_ByteString name = strPsName;
  name.Remove(' ');
  CFGAS_FontMgr* pFDEFontMgr = m_pDoc->GetApp()->GetFDEFontMgr();
  for (const auto& it : *pFontSetDict) {
    const CFX_ByteString& key = it.first;
    CPDF_Object* pObj = it.second.get();
    if (!PsNameMatchDRFontName(name.AsStringC(), bBold, bItalic, key,
                               bStrictMatch)) {
      continue;
    }
    CPDF_Dictionary* pFontDict = ToDictionary(pObj->GetDirect());
    if (!pFontDict || pFontDict->GetStringFor("Type") != "Font") {
      return nullptr;
    }
    CPDF_Font* pPDFFont = pDoc->LoadFont(pFontDict);
    if (!pPDFFont) {
      return nullptr;
    }
    if (!pPDFFont->IsEmbedded()) {
      *pDstPDFFont = pPDFFont;
      return nullptr;
    }
    return CFGAS_GEFont::LoadFont(&pPDFFont->m_Font, pFDEFontMgr);
  }
  return nullptr;
}

CFX_RetainPtr<CFGAS_GEFont> CXFA_PDFFontMgr::GetFont(
    const CFX_WideStringC& wsFontFamily,
    uint32_t dwFontStyles,
    CPDF_Font** pPDFFont,
    bool bStrictMatch) {
  uint32_t dwHashCode = FX_HashCode_GetW(wsFontFamily, false);
  CFX_ByteString strKey;
  strKey.Format("%u%u", dwHashCode, dwFontStyles);
  auto it = m_FontMap.find(strKey);
  if (it != m_FontMap.end())
    return it->second;
  CFX_ByteString bsPsName =
      CFX_ByteString::FromUnicode(CFX_WideString(wsFontFamily));
  bool bBold = (dwFontStyles & FX_FONTSTYLE_Bold) == FX_FONTSTYLE_Bold;
  bool bItalic = (dwFontStyles & FX_FONTSTYLE_Italic) == FX_FONTSTYLE_Italic;
  CFX_ByteString strFontName = PsNameToFontName(bsPsName, bBold, bItalic);
  CFX_RetainPtr<CFGAS_GEFont> pFont =
      FindFont(strFontName, bBold, bItalic, pPDFFont, bStrictMatch);
  if (pFont)
    m_FontMap[strKey] = pFont;
  return pFont;
}

CFX_ByteString CXFA_PDFFontMgr::PsNameToFontName(
    const CFX_ByteString& strPsName,
    bool bBold,
    bool bItalic) {
  for (size_t i = 0; i < FX_ArraySize(g_XFAPDFFontName); ++i) {
    if (strPsName == g_XFAPDFFontName[i][0]) {
      size_t index = 1;
      if (bBold)
        ++index;
      if (bItalic)
        index += 2;
      return g_XFAPDFFontName[i][index];
    }
  }
  return strPsName;
}

bool CXFA_PDFFontMgr::PsNameMatchDRFontName(const CFX_ByteStringC& bsPsName,
                                            bool bBold,
                                            bool bItalic,
                                            const CFX_ByteString& bsDRFontName,
                                            bool bStrictMatch) {
  CFX_ByteString bsDRName = bsDRFontName;
  bsDRName.Remove('-');
  int32_t iPsLen = bsPsName.GetLength();
  int32_t nIndex = bsDRName.Find(bsPsName);
  if (nIndex != -1 && !bStrictMatch)
    return true;

  if (nIndex != 0)
    return false;

  int32_t iDifferLength = bsDRName.GetLength() - iPsLen;
  if (iDifferLength > 1 || (bBold || bItalic)) {
    int32_t iBoldIndex = bsDRName.Find("Bold");
    bool bBoldFont = iBoldIndex > 0;
    if (bBold != bBoldFont)
      return false;

    if (bBoldFont) {
      iDifferLength =
          std::min(iDifferLength - 4, bsDRName.GetLength() - iBoldIndex - 4);
    }
    bool bItalicFont = true;
    if (bsDRName.Find("Italic") > 0) {
      iDifferLength -= 6;
    } else if (bsDRName.Find("It") > 0) {
      iDifferLength -= 2;
    } else if (bsDRName.Find("Oblique") > 0) {
      iDifferLength -= 7;
    } else {
      bItalicFont = false;
    }
    if (bItalic != bItalicFont)
      return false;

    if (iDifferLength > 1) {
      CFX_ByteString bsDRTailer = bsDRName.Right(iDifferLength);
      if (bsDRTailer == "MT" || bsDRTailer == "PSMT" ||
          bsDRTailer == "Regular" || bsDRTailer == "Reg") {
        return true;
      }
      if (bBoldFont || bItalicFont)
        return false;

      bool bMatch = false;
      switch (bsPsName.GetAt(iPsLen - 1)) {
        case 'L': {
          if (bsDRName.Right(5) == "Light") {
            bMatch = true;
          }
        } break;
        case 'R': {
          if (bsDRName.Right(7) == "Regular" || bsDRName.Right(3) == "Reg") {
            bMatch = true;
          }
        } break;
        case 'M': {
          if (bsDRName.Right(5) == "Medium") {
            bMatch = true;
          }
        } break;
        default:
          break;
      }
      return bMatch;
    }
  }
  return true;
}

bool CXFA_PDFFontMgr::GetCharWidth(const CFX_RetainPtr<CFGAS_GEFont>& pFont,
                                   wchar_t wUnicode,
                                   bool bCharCode,
                                   int32_t* pWidth) {
  if (wUnicode != 0x20 || bCharCode)
    return false;

  auto it = m_FDE2PDFFont.find(pFont);
  if (it == m_FDE2PDFFont.end())
    return false;

  CPDF_Font* pPDFFont = it->second;
  *pWidth = pPDFFont->GetCharWidthF(pPDFFont->CharCodeFromUnicode(wUnicode));
  return true;
}

void CXFA_PDFFontMgr::SetFont(const CFX_RetainPtr<CFGAS_GEFont>& pFont,
                              CPDF_Font* pPDFFont) {
  m_FDE2PDFFont[pFont] = pPDFFont;
}
