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

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

#include <algorithm>
#include <vector>

#include "core/fpdfapi/fpdf_font/include/cpdf_font.h"
#include "core/fpdfapi/fpdf_parser/include/cpdf_array.h"
#include "core/fpdfapi/fpdf_parser/include/cpdf_document.h"
#include "core/fpdfapi/fpdf_parser/include/cpdf_simple_parser.h"
#include "core/fpdfdoc/doc_utils.h"
#include "core/fpdfdoc/include/fpdf_doc.h"
#include "core/fxge/include/fx_font.h"

namespace {

const int FPDFDOC_UTILS_MAXRECURSION = 32;

CPDF_Object* SearchNumberNode(const CPDF_Dictionary* pNode, int num) {
  CPDF_Array* pLimits = pNode->GetArrayBy("Limits");
  if (pLimits &&
      (num < pLimits->GetIntegerAt(0) || num > pLimits->GetIntegerAt(1))) {
    return NULL;
  }
  CPDF_Array* pNumbers = pNode->GetArrayBy("Nums");
  if (pNumbers) {
    uint32_t dwCount = pNumbers->GetCount() / 2;
    for (uint32_t i = 0; i < dwCount; i++) {
      int index = pNumbers->GetIntegerAt(i * 2);
      if (num == index) {
        return pNumbers->GetDirectObjectAt(i * 2 + 1);
      }
      if (index > num) {
        break;
      }
    }
    return NULL;
  }
  CPDF_Array* pKids = pNode->GetArrayBy("Kids");
  if (!pKids) {
    return NULL;
  }
  for (uint32_t i = 0; i < pKids->GetCount(); i++) {
    CPDF_Dictionary* pKid = pKids->GetDictAt(i);
    if (!pKid) {
      continue;
    }
    CPDF_Object* pFound = SearchNumberNode(pKid, num);
    if (pFound) {
      return pFound;
    }
  }
  return NULL;
}

}  // namespace

CPDF_Object* CPDF_NumberTree::LookupValue(int num) const {
  return SearchNumberNode(m_pRoot, num);
}

CFX_WideString GetFullName(CPDF_Dictionary* pFieldDict) {
  CFX_WideString full_name;
  CPDF_Dictionary* pLevel = pFieldDict;
  while (pLevel) {
    CFX_WideString short_name = pLevel->GetUnicodeTextBy("T");
    if (short_name != L"") {
      if (full_name == L"") {
        full_name = short_name;
      } else {
        full_name = short_name + L"." + full_name;
      }
    }
    pLevel = pLevel->GetDictBy("Parent");
  }
  return full_name;
}
FX_BOOL CPDF_DefaultAppearance::HasFont() {
  if (m_csDA.IsEmpty()) {
    return FALSE;
  }
  CPDF_SimpleParser syntax(m_csDA.AsByteStringC());
  return syntax.FindTagParamFromStart("Tf", 2);
}
CFX_ByteString CPDF_DefaultAppearance::GetFontString() {
  CFX_ByteString csFont;
  if (m_csDA.IsEmpty()) {
    return csFont;
  }
  CPDF_SimpleParser syntax(m_csDA.AsByteStringC());
  if (syntax.FindTagParamFromStart("Tf", 2)) {
    csFont += (CFX_ByteString)syntax.GetWord();
    csFont += " ";
    csFont += (CFX_ByteString)syntax.GetWord();
    csFont += " ";
    csFont += (CFX_ByteString)syntax.GetWord();
  }
  return csFont;
}
void CPDF_DefaultAppearance::GetFont(CFX_ByteString& csFontNameTag,
                                     FX_FLOAT& fFontSize) {
  csFontNameTag = "";
  fFontSize = 0;
  if (m_csDA.IsEmpty()) {
    return;
  }
  CPDF_SimpleParser syntax(m_csDA.AsByteStringC());
  if (syntax.FindTagParamFromStart("Tf", 2)) {
    csFontNameTag = CFX_ByteString(syntax.GetWord());
    csFontNameTag.Delete(0, 1);
    fFontSize = FX_atof(syntax.GetWord());
  }
  csFontNameTag = PDF_NameDecode(csFontNameTag);
}
FX_BOOL CPDF_DefaultAppearance::HasColor(FX_BOOL bStrokingOperation) {
  if (m_csDA.IsEmpty()) {
    return FALSE;
  }
  CPDF_SimpleParser syntax(m_csDA.AsByteStringC());
  if (syntax.FindTagParamFromStart(bStrokingOperation ? "G" : "g", 1)) {
    return TRUE;
  }
  if (syntax.FindTagParamFromStart(bStrokingOperation ? "RG" : "rg", 3)) {
    return TRUE;
  }
  return syntax.FindTagParamFromStart(bStrokingOperation ? "K" : "k", 4);
}
CFX_ByteString CPDF_DefaultAppearance::GetColorString(
    FX_BOOL bStrokingOperation) {
  CFX_ByteString csColor;
  if (m_csDA.IsEmpty()) {
    return csColor;
  }
  CPDF_SimpleParser syntax(m_csDA.AsByteStringC());
  if (syntax.FindTagParamFromStart(bStrokingOperation ? "G" : "g", 1)) {
    csColor += (CFX_ByteString)syntax.GetWord();
    csColor += " ";
    csColor += (CFX_ByteString)syntax.GetWord();
    return csColor;
  }
  if (syntax.FindTagParamFromStart(bStrokingOperation ? "RG" : "rg", 3)) {
    csColor += (CFX_ByteString)syntax.GetWord();
    csColor += " ";
    csColor += (CFX_ByteString)syntax.GetWord();
    csColor += " ";
    csColor += (CFX_ByteString)syntax.GetWord();
    csColor += " ";
    csColor += (CFX_ByteString)syntax.GetWord();
    return csColor;
  }
  if (syntax.FindTagParamFromStart(bStrokingOperation ? "K" : "k", 4)) {
    csColor += (CFX_ByteString)syntax.GetWord();
    csColor += " ";
    csColor += (CFX_ByteString)syntax.GetWord();
    csColor += " ";
    csColor += (CFX_ByteString)syntax.GetWord();
    csColor += " ";
    csColor += (CFX_ByteString)syntax.GetWord();
    csColor += " ";
    csColor += (CFX_ByteString)syntax.GetWord();
  }
  return csColor;
}
void CPDF_DefaultAppearance::GetColor(int& iColorType,
                                      FX_FLOAT fc[4],
                                      FX_BOOL bStrokingOperation) {
  iColorType = COLORTYPE_TRANSPARENT;
  for (int c = 0; c < 4; c++) {
    fc[c] = 0;
  }
  if (m_csDA.IsEmpty()) {
    return;
  }
  CPDF_SimpleParser syntax(m_csDA.AsByteStringC());
  if (syntax.FindTagParamFromStart(bStrokingOperation ? "G" : "g", 1)) {
    iColorType = COLORTYPE_GRAY;
    fc[0] = FX_atof(syntax.GetWord());
    return;
  }
  if (syntax.FindTagParamFromStart(bStrokingOperation ? "RG" : "rg", 3)) {
    iColorType = COLORTYPE_RGB;
    fc[0] = FX_atof(syntax.GetWord());
    fc[1] = FX_atof(syntax.GetWord());
    fc[2] = FX_atof(syntax.GetWord());
    return;
  }
  if (syntax.FindTagParamFromStart(bStrokingOperation ? "K" : "k", 4)) {
    iColorType = COLORTYPE_CMYK;
    fc[0] = FX_atof(syntax.GetWord());
    fc[1] = FX_atof(syntax.GetWord());
    fc[2] = FX_atof(syntax.GetWord());
    fc[3] = FX_atof(syntax.GetWord());
  }
}
void CPDF_DefaultAppearance::GetColor(FX_ARGB& color,
                                      int& iColorType,
                                      FX_BOOL bStrokingOperation) {
  color = 0;
  iColorType = COLORTYPE_TRANSPARENT;
  if (m_csDA.IsEmpty()) {
    return;
  }
  CPDF_SimpleParser syntax(m_csDA.AsByteStringC());
  if (syntax.FindTagParamFromStart(bStrokingOperation ? "G" : "g", 1)) {
    iColorType = COLORTYPE_GRAY;
    FX_FLOAT g = FX_atof(syntax.GetWord()) * 255 + 0.5f;
    color = ArgbEncode(255, (int)g, (int)g, (int)g);
    return;
  }
  if (syntax.FindTagParamFromStart(bStrokingOperation ? "RG" : "rg", 3)) {
    iColorType = COLORTYPE_RGB;
    FX_FLOAT r = FX_atof(syntax.GetWord()) * 255 + 0.5f;
    FX_FLOAT g = FX_atof(syntax.GetWord()) * 255 + 0.5f;
    FX_FLOAT b = FX_atof(syntax.GetWord()) * 255 + 0.5f;
    color = ArgbEncode(255, (int)r, (int)g, (int)b);
    return;
  }
  if (syntax.FindTagParamFromStart(bStrokingOperation ? "K" : "k", 4)) {
    iColorType = COLORTYPE_CMYK;
    FX_FLOAT c = FX_atof(syntax.GetWord());
    FX_FLOAT m = FX_atof(syntax.GetWord());
    FX_FLOAT y = FX_atof(syntax.GetWord());
    FX_FLOAT k = FX_atof(syntax.GetWord());
    FX_FLOAT r = 1.0f - std::min(1.0f, c + k);
    FX_FLOAT g = 1.0f - std::min(1.0f, m + k);
    FX_FLOAT b = 1.0f - std::min(1.0f, y + k);
    color = ArgbEncode(255, (int)(r * 255 + 0.5f), (int)(g * 255 + 0.5f),
                       (int)(b * 255 + 0.5f));
  }
}
FX_BOOL CPDF_DefaultAppearance::HasTextMatrix() {
  if (m_csDA.IsEmpty()) {
    return FALSE;
  }
  CPDF_SimpleParser syntax(m_csDA.AsByteStringC());
  return syntax.FindTagParamFromStart("Tm", 6);
}
CFX_ByteString CPDF_DefaultAppearance::GetTextMatrixString() {
  CFX_ByteString csTM;
  if (m_csDA.IsEmpty()) {
    return csTM;
  }
  CPDF_SimpleParser syntax(m_csDA.AsByteStringC());
  if (syntax.FindTagParamFromStart("Tm", 6)) {
    for (int i = 0; i < 6; i++) {
      csTM += (CFX_ByteString)syntax.GetWord();
      csTM += " ";
    }
    csTM += (CFX_ByteString)syntax.GetWord();
  }
  return csTM;
}
CFX_Matrix CPDF_DefaultAppearance::GetTextMatrix() {
  CFX_Matrix tm;
  if (m_csDA.IsEmpty()) {
    return tm;
  }
  CPDF_SimpleParser syntax(m_csDA.AsByteStringC());
  if (syntax.FindTagParamFromStart("Tm", 6)) {
    FX_FLOAT f[6];
    for (int i = 0; i < 6; i++) {
      f[i] = FX_atof(syntax.GetWord());
    }
    tm.Set(f[0], f[1], f[2], f[3], f[4], f[5]);
  }
  return tm;
}
void InitInterFormDict(CPDF_Dictionary*& pFormDict, CPDF_Document* pDocument) {
  if (!pDocument) {
    return;
  }
  if (!pFormDict) {
    pFormDict = new CPDF_Dictionary;
    uint32_t dwObjNum = pDocument->AddIndirectObject(pFormDict);
    CPDF_Dictionary* pRoot = pDocument->GetRoot();
    pRoot->SetAtReference("AcroForm", pDocument, dwObjNum);
  }
  CFX_ByteString csDA;
  if (!pFormDict->KeyExist("DR")) {
    CPDF_Font* pFont = NULL;
    CFX_ByteString csBaseName, csDefault;
    uint8_t charSet = CPDF_InterForm::GetNativeCharSet();
    pFont = CPDF_InterForm::AddStandardFont(pDocument, "Helvetica");
    if (pFont) {
      AddInterFormFont(pFormDict, pDocument, pFont, csBaseName);
      csDefault = csBaseName;
    }
    if (charSet != 0) {
      CFX_ByteString csFontName = CPDF_InterForm::GetNativeFont(charSet, NULL);
      if (!pFont || csFontName != "Helvetica") {
        pFont = CPDF_InterForm::AddNativeFont(pDocument);
        if (pFont) {
          csBaseName = "";
          AddInterFormFont(pFormDict, pDocument, pFont, csBaseName);
          csDefault = csBaseName;
        }
      }
    }
    if (pFont) {
      csDA = "/" + PDF_NameEncode(csDefault) + " 0 Tf";
    }
  }
  if (!csDA.IsEmpty()) {
    csDA += " ";
  }
  csDA += "0 g";
  if (!pFormDict->KeyExist("DA")) {
    pFormDict->SetAtString("DA", csDA);
  }
}
uint32_t CountInterFormFonts(CPDF_Dictionary* pFormDict) {
  if (!pFormDict) {
    return 0;
  }
  CPDF_Dictionary* pDR = pFormDict->GetDictBy("DR");
  if (!pDR) {
    return 0;
  }
  CPDF_Dictionary* pFonts = pDR->GetDictBy("Font");
  if (!pFonts) {
    return 0;
  }
  uint32_t dwCount = 0;
  for (const auto& it : *pFonts) {
    CPDF_Object* pObj = it.second;
    if (!pObj) {
      continue;
    }
    if (CPDF_Dictionary* pDirect = ToDictionary(pObj->GetDirect())) {
      if (pDirect->GetStringBy("Type") == "Font") {
        dwCount++;
      }
    }
  }
  return dwCount;
}
CPDF_Font* GetInterFormFont(CPDF_Dictionary* pFormDict,
                            CPDF_Document* pDocument,
                            uint32_t index,
                            CFX_ByteString& csNameTag) {
  if (!pFormDict) {
    return NULL;
  }
  CPDF_Dictionary* pDR = pFormDict->GetDictBy("DR");
  if (!pDR) {
    return NULL;
  }
  CPDF_Dictionary* pFonts = pDR->GetDictBy("Font");
  if (!pFonts) {
    return NULL;
  }
  uint32_t dwCount = 0;
  for (const auto& it : *pFonts) {
    const CFX_ByteString& csKey = it.first;
    CPDF_Object* pObj = it.second;
    if (!pObj) {
      continue;
    }
    CPDF_Dictionary* pElement = ToDictionary(pObj->GetDirect());
    if (!pElement)
      continue;
    if (pElement->GetStringBy("Type") != "Font")
      continue;
    if (dwCount == index) {
      csNameTag = csKey;
      return pDocument->LoadFont(pElement);
    }
    dwCount++;
  }
  return NULL;
}
CPDF_Font* GetInterFormFont(CPDF_Dictionary* pFormDict,
                            CPDF_Document* pDocument,
                            CFX_ByteString csNameTag) {
  CFX_ByteString csAlias = PDF_NameDecode(csNameTag);
  if (!pFormDict || csAlias.IsEmpty()) {
    return NULL;
  }
  CPDF_Dictionary* pDR = pFormDict->GetDictBy("DR");
  if (!pDR) {
    return NULL;
  }
  CPDF_Dictionary* pFonts = pDR->GetDictBy("Font");
  if (!pFonts) {
    return NULL;
  }
  CPDF_Dictionary* pElement = pFonts->GetDictBy(csAlias.AsByteStringC());
  if (!pElement) {
    return NULL;
  }
  if (pElement->GetStringBy("Type") == "Font") {
    return pDocument->LoadFont(pElement);
  }
  return NULL;
}
CPDF_Font* GetInterFormFont(CPDF_Dictionary* pFormDict,
                            CPDF_Document* pDocument,
                            CFX_ByteString csFontName,
                            CFX_ByteString& csNameTag) {
  if (!pFormDict || csFontName.IsEmpty()) {
    return NULL;
  }
  CPDF_Dictionary* pDR = pFormDict->GetDictBy("DR");
  if (!pDR) {
    return NULL;
  }
  CPDF_Dictionary* pFonts = pDR->GetDictBy("Font");
  if (!pFonts) {
    return NULL;
  }
  for (const auto& it : *pFonts) {
    const CFX_ByteString& csKey = it.first;
    CPDF_Object* pObj = it.second;
    if (!pObj) {
      continue;
    }
    CPDF_Dictionary* pElement = ToDictionary(pObj->GetDirect());
    if (!pElement)
      continue;
    if (pElement->GetStringBy("Type") != "Font")
      continue;

    CPDF_Font* pFind = pDocument->LoadFont(pElement);
    if (!pFind)
      continue;

    CFX_ByteString csBaseFont;
    csBaseFont = pFind->GetBaseFont();
    csBaseFont.Remove(' ');
    if (csBaseFont == csFontName) {
      csNameTag = csKey;
      return pFind;
    }
  }
  return NULL;
}
CPDF_Font* GetNativeInterFormFont(CPDF_Dictionary* pFormDict,
                                  CPDF_Document* pDocument,
                                  uint8_t charSet,
                                  CFX_ByteString& csNameTag) {
  if (!pFormDict) {
    return NULL;
  }
  CPDF_Dictionary* pDR = pFormDict->GetDictBy("DR");
  if (!pDR) {
    return NULL;
  }
  CPDF_Dictionary* pFonts = pDR->GetDictBy("Font");
  if (!pFonts) {
    return NULL;
  }
  for (const auto& it : *pFonts) {
    const CFX_ByteString& csKey = it.first;
    CPDF_Object* pObj = it.second;
    if (!pObj) {
      continue;
    }
    CPDF_Dictionary* pElement = ToDictionary(pObj->GetDirect());
    if (!pElement)
      continue;
    if (pElement->GetStringBy("Type") != "Font")
      continue;
    CPDF_Font* pFind = pDocument->LoadFont(pElement);
    if (!pFind) {
      continue;
    }
    CFX_SubstFont* pSubst = (CFX_SubstFont*)pFind->GetSubstFont();
    if (!pSubst) {
      continue;
    }
    if (pSubst->m_Charset == (int)charSet) {
      csNameTag = csKey;
      return pFind;
    }
  }
  return NULL;
}
CPDF_Font* GetNativeInterFormFont(CPDF_Dictionary* pFormDict,
                                  CPDF_Document* pDocument,
                                  CFX_ByteString& csNameTag) {
  csNameTag = "";
  uint8_t charSet = CPDF_InterForm::GetNativeCharSet();
  CFX_SubstFont* pSubst;
  CPDF_Font* pFont = GetDefaultInterFormFont(pFormDict, pDocument);
  if (pFont) {
    pSubst = (CFX_SubstFont*)pFont->GetSubstFont();
    if (pSubst && pSubst->m_Charset == (int)charSet) {
      FindInterFormFont(pFormDict, pFont, csNameTag);
      return pFont;
    }
  }
  return GetNativeInterFormFont(pFormDict, pDocument, charSet, csNameTag);
}
FX_BOOL FindInterFormFont(CPDF_Dictionary* pFormDict,
                          const CPDF_Font* pFont,
                          CFX_ByteString& csNameTag) {
  if (!pFormDict || !pFont) {
    return FALSE;
  }
  CPDF_Dictionary* pDR = pFormDict->GetDictBy("DR");
  if (!pDR) {
    return FALSE;
  }
  CPDF_Dictionary* pFonts = pDR->GetDictBy("Font");
  if (!pFonts) {
    return FALSE;
  }
  for (const auto& it : *pFonts) {
    const CFX_ByteString& csKey = it.first;
    CPDF_Object* pObj = it.second;
    if (!pObj) {
      continue;
    }
    CPDF_Dictionary* pElement = ToDictionary(pObj->GetDirect());
    if (!pElement)
      continue;
    if (pElement->GetStringBy("Type") != "Font") {
      continue;
    }
    if (pFont->GetFontDict() == pElement) {
      csNameTag = csKey;
      return TRUE;
    }
  }
  return FALSE;
}
FX_BOOL FindInterFormFont(CPDF_Dictionary* pFormDict,
                          CPDF_Document* pDocument,
                          CFX_ByteString csFontName,
                          CPDF_Font*& pFont,
                          CFX_ByteString& csNameTag) {
  if (!pFormDict) {
    return FALSE;
  }
  CPDF_Dictionary* pDR = pFormDict->GetDictBy("DR");
  if (!pDR) {
    return FALSE;
  }
  CPDF_Dictionary* pFonts = pDR->GetDictBy("Font");
  if (!pFonts) {
    return FALSE;
  }
  if (csFontName.GetLength() > 0) {
    csFontName.Remove(' ');
  }
  for (const auto& it : *pFonts) {
    const CFX_ByteString& csKey = it.first;
    CPDF_Object* pObj = it.second;
    if (!pObj) {
      continue;
    }
    CPDF_Dictionary* pElement = ToDictionary(pObj->GetDirect());
    if (!pElement)
      continue;
    if (pElement->GetStringBy("Type") != "Font") {
      continue;
    }
    pFont = pDocument->LoadFont(pElement);
    if (!pFont) {
      continue;
    }
    CFX_ByteString csBaseFont;
    csBaseFont = pFont->GetBaseFont();
    csBaseFont.Remove(' ');
    if (csBaseFont == csFontName) {
      csNameTag = csKey;
      return TRUE;
    }
  }
  return FALSE;
}
void AddInterFormFont(CPDF_Dictionary*& pFormDict,
                      CPDF_Document* pDocument,
                      const CPDF_Font* pFont,
                      CFX_ByteString& csNameTag) {
  if (!pFont) {
    return;
  }
  if (!pFormDict) {
    InitInterFormDict(pFormDict, pDocument);
  }
  CFX_ByteString csTag;
  if (FindInterFormFont(pFormDict, pFont, csTag)) {
    csNameTag = csTag;
    return;
  }
  if (!pFormDict) {
    InitInterFormDict(pFormDict, pDocument);
  }
  CPDF_Dictionary* pDR = pFormDict->GetDictBy("DR");
  if (!pDR) {
    pDR = new CPDF_Dictionary;
    pFormDict->SetAt("DR", pDR);
  }
  CPDF_Dictionary* pFonts = pDR->GetDictBy("Font");
  if (!pFonts) {
    pFonts = new CPDF_Dictionary;
    pDR->SetAt("Font", pFonts);
  }
  if (csNameTag.IsEmpty()) {
    csNameTag = pFont->GetBaseFont();
  }
  csNameTag.Remove(' ');
  csNameTag =
      CPDF_InterForm::GenerateNewResourceName(pDR, "Font", 4, csNameTag);
  pFonts->SetAtReference(csNameTag.AsByteStringC(), pDocument,
                         pFont->GetFontDict());
}
CPDF_Font* AddNativeInterFormFont(CPDF_Dictionary*& pFormDict,
                                  CPDF_Document* pDocument,
                                  uint8_t charSet,
                                  CFX_ByteString& csNameTag) {
  if (!pFormDict) {
    InitInterFormDict(pFormDict, pDocument);
  }
  CFX_ByteString csTemp;
  CPDF_Font* pFont =
      GetNativeInterFormFont(pFormDict, pDocument, charSet, csTemp);
  if (pFont) {
    csNameTag = csTemp;
    return pFont;
  }
  CFX_ByteString csFontName = CPDF_InterForm::GetNativeFont(charSet);
  if (!csFontName.IsEmpty()) {
    if (FindInterFormFont(pFormDict, pDocument, csFontName, pFont, csNameTag)) {
      return pFont;
    }
  }
  pFont = CPDF_InterForm::AddNativeFont(charSet, pDocument);
  if (pFont) {
    AddInterFormFont(pFormDict, pDocument, pFont, csNameTag);
  }
  return pFont;
}
CPDF_Font* AddNativeInterFormFont(CPDF_Dictionary*& pFormDict,
                                  CPDF_Document* pDocument,
                                  CFX_ByteString& csNameTag) {
  uint8_t charSet = CPDF_InterForm::GetNativeCharSet();
  return AddNativeInterFormFont(pFormDict, pDocument, charSet, csNameTag);
}
void RemoveInterFormFont(CPDF_Dictionary* pFormDict, const CPDF_Font* pFont) {
  if (!pFormDict || !pFont) {
    return;
  }
  CFX_ByteString csTag;
  if (!FindInterFormFont(pFormDict, pFont, csTag)) {
    return;
  }
  CPDF_Dictionary* pDR = pFormDict->GetDictBy("DR");
  CPDF_Dictionary* pFonts = pDR->GetDictBy("Font");
  pFonts->RemoveAt(csTag.AsByteStringC());
}
void RemoveInterFormFont(CPDF_Dictionary* pFormDict, CFX_ByteString csNameTag) {
  if (!pFormDict || csNameTag.IsEmpty()) {
    return;
  }
  CPDF_Dictionary* pDR = pFormDict->GetDictBy("DR");
  if (!pDR) {
    return;
  }
  CPDF_Dictionary* pFonts = pDR->GetDictBy("Font");
  if (!pFonts) {
    return;
  }
  pFonts->RemoveAt(csNameTag.AsByteStringC());
}

CPDF_Font* GetDefaultInterFormFont(CPDF_Dictionary* pFormDict,
                                   CPDF_Document* pDocument) {
  if (!pFormDict) {
    return NULL;
  }
  CPDF_DefaultAppearance cDA(pFormDict->GetStringBy("DA"));
  CFX_ByteString csFontNameTag;
  FX_FLOAT fFontSize;
  cDA.GetFont(csFontNameTag, fFontSize);
  return GetInterFormFont(pFormDict, pDocument, csFontNameTag);
}

CPDF_IconFit::ScaleMethod CPDF_IconFit::GetScaleMethod() {
  if (!m_pDict) {
    return Always;
  }
  CFX_ByteString csSW = m_pDict->GetStringBy("SW", "A");
  if (csSW == "B") {
    return Bigger;
  }
  if (csSW == "S") {
    return Smaller;
  }
  if (csSW == "N") {
    return Never;
  }
  return Always;
}
FX_BOOL CPDF_IconFit::IsProportionalScale() {
  if (!m_pDict) {
    return TRUE;
  }
  return m_pDict->GetStringBy("S", "P") != "A";
}
void CPDF_IconFit::GetIconPosition(FX_FLOAT& fLeft, FX_FLOAT& fBottom) {
  fLeft = fBottom = 0.5;
  if (!m_pDict) {
    return;
  }
  CPDF_Array* pA = m_pDict->GetArrayBy("A");
  if (pA) {
    uint32_t dwCount = pA->GetCount();
    if (dwCount > 0) {
      fLeft = pA->GetNumberAt(0);
    }
    if (dwCount > 1) {
      fBottom = pA->GetNumberAt(1);
    }
  }
}
FX_BOOL CPDF_IconFit::GetFittingBounds() {
  if (!m_pDict) {
    return FALSE;
  }
  return m_pDict->GetBooleanBy("FB");
}

std::vector<bool> SaveCheckedFieldStatus(CPDF_FormField* pField) {
  std::vector<bool> result;
  int iCount = pField->CountControls();
  for (int i = 0; i < iCount; ++i) {
    if (CPDF_FormControl* pControl = pField->GetControl(i))
      result.push_back(pControl->IsChecked());
  }
  return result;
}

CPDF_Object* FPDF_GetFieldAttr(CPDF_Dictionary* pFieldDict,
                               const FX_CHAR* name,
                               int nLevel) {
  if (nLevel > FPDFDOC_UTILS_MAXRECURSION) {
    return NULL;
  }
  if (!pFieldDict) {
    return NULL;
  }
  CPDF_Object* pAttr = pFieldDict->GetDirectObjectBy(name);
  if (pAttr) {
    return pAttr;
  }
  CPDF_Dictionary* pParent = pFieldDict->GetDictBy("Parent");
  if (!pParent) {
    return NULL;
  }
  return FPDF_GetFieldAttr(pParent, name, nLevel + 1);
}