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

#ifndef CORE_INCLUDE_FPDFDOC_FPDF_DOC_H_
#define CORE_INCLUDE_FPDFDOC_FPDF_DOC_H_

#include <map>
#include <vector>

#include "../fpdfapi/fpdf_parser.h"
#include "../fpdfapi/fpdf_render.h"
#include "third_party/base/nonstd_unique_ptr.h"

class CFieldTree;
class CPDF_AAction;
class CPDF_Action;
class CPDF_ActionFields;
class CPDF_Annot;
class CPDF_AnnotList;
class CPDF_ApSettings;
class CPDF_Bookmark;
class CPDF_BookmarkTree;
class CPDF_DefaultAppearance;
class CPDF_Dest;
class CPDF_DocJSActions;
class CPDF_FileSpec;
class CPDF_FormControl;
class CPDF_FormField;
class CPDF_FormNotify;
class CPDF_IconFit;
class CPDF_InterForm;
class CPDF_Link;
class CPDF_Metadata;
class CPDF_NumberTree;
class CPDF_OCContext;
class CPDF_Page;
class CPDF_RenderOptions;
class CPDF_TextObject;
class CPDF_ViewerPreferences;
class CXML_Element;

class CPDF_NameTree {
 public:
  explicit CPDF_NameTree(CPDF_Dictionary* pRoot) { m_pRoot = pRoot; }

  CPDF_NameTree(CPDF_Document* pDoc, const CFX_ByteStringC& category);

  CPDF_Object* LookupValue(int nIndex, CFX_ByteString& csName) const;

  CPDF_Object* LookupValue(const CFX_ByteString& csName) const;

  CPDF_Array* LookupNamedDest(CPDF_Document* pDoc,
                              const CFX_ByteStringC& sName);

  int GetIndex(const CFX_ByteString& csName) const;

  int GetCount() const;

  CPDF_Dictionary* GetRoot() const { return m_pRoot; }

 protected:
  CPDF_Dictionary* m_pRoot;
};
class CPDF_BookmarkTree {
 public:
  explicit CPDF_BookmarkTree(CPDF_Document* pDoc) : m_pDocument(pDoc) {}

  CPDF_Bookmark GetFirstChild(const CPDF_Bookmark& parent) const;

  CPDF_Bookmark GetNextSibling(const CPDF_Bookmark& bookmark) const;

  CPDF_Document* GetDocument() const { return m_pDocument; }

 protected:
  CPDF_Document* m_pDocument;
};
#define PDFBOOKMARK_ITALIC 1
#define PDFBOOKMARK_BOLD 2
class CPDF_Bookmark {
 public:
  CPDF_Bookmark() : m_pDict(NULL) {}

  explicit CPDF_Bookmark(CPDF_Dictionary* pDict) : m_pDict(pDict) {}

  CPDF_Dictionary* GetDict() const { return m_pDict; }

  operator bool() const { return m_pDict != NULL; }

  FX_DWORD GetColorRef() const;

  FX_DWORD GetFontStyle() const;

  CFX_WideString GetTitle() const;

  CPDF_Dest GetDest(CPDF_Document* pDocument) const;

  CPDF_Action GetAction() const;

  CPDF_Dictionary* m_pDict;
};
#define PDFZOOM_XYZ 1
#define PDFZOOM_FITPAGE 2
#define PDFZOOM_FITHORZ 3
#define PDFZOOM_FITVERT 4
#define PDFZOOM_FITRECT 5
#define PDFZOOM_FITBBOX 6
#define PDFZOOM_FITBHORZ 7
#define PDFZOOM_FITBVERT 8
class CPDF_Dest {
 public:
  CPDF_Dest() : m_pObj(nullptr) {}
  explicit CPDF_Dest(CPDF_Object* pObj) : m_pObj(pObj) {}

  operator bool() const { return m_pObj != NULL; }
  CPDF_Object* GetObject() const { return m_pObj; }

  CFX_ByteString GetRemoteName();
  int GetPageIndex(CPDF_Document* pDoc);
  FX_DWORD GetPageObjNum();
  int GetZoomMode();
  FX_FLOAT GetParam(int index);

 protected:
  CPDF_Object* m_pObj;
};
class CPDF_OCContext : public IPDF_OCContext {
 public:
  enum UsageType { View = 0, Design, Print, Export };

  explicit CPDF_OCContext(CPDF_Document* pDoc, UsageType eUsageType = View);
  ~CPDF_OCContext() override;

  CPDF_Document* GetDocument() const { return m_pDocument; }

  UsageType GetUsageType() const { return m_eUsageType; }

  FX_BOOL CheckOCGVisible(const CPDF_Dictionary* pOCGDict) override;

  void ResetOCContext();

 protected:
  FX_BOOL LoadOCGStateFromConfig(const CFX_ByteStringC& csConfig,
                                 const CPDF_Dictionary* pOCGDict,
                                 FX_BOOL& bValidConfig) const;

  FX_BOOL LoadOCGState(const CPDF_Dictionary* pOCGDict) const;

  FX_BOOL GetOCGVisible(const CPDF_Dictionary* pOCGDict);

  FX_BOOL GetOCGVE(CPDF_Array* pExpression,
                   FX_BOOL bFromConfig,
                   int nLevel = 0);

  FX_BOOL LoadOCMDState(const CPDF_Dictionary* pOCMDDict, FX_BOOL bFromConfig);

  CPDF_Document* m_pDocument;

  UsageType m_eUsageType;

  std::map<const CPDF_Dictionary*, FX_BOOL> m_OCGStates;
};

class CPDF_ActionFields {
 public:
  // TODO(thestig): Examine why this cannot be explicit.
  CPDF_ActionFields(const CPDF_Action* pAction) {
    m_pAction = (CPDF_Action*)pAction;
  }

  // TODO(thestig): Replace this.
  operator CPDF_Action*() const { return m_pAction; }

  FX_DWORD GetFieldsCount() const;

  std::vector<CPDF_Object*> GetAllFields() const;

  CPDF_Object* GetField(FX_DWORD iIndex) const;

  CPDF_Action* m_pAction;
};

#define PDFNAMED_NEXTPAGE 1
#define PDFNAMED_PREVPAGE 2
#define PDFNAMED_FIRSTPAGE 3
#define PDFNAMED_LASTPAGE 4
#define PDFJS_MAXLENGTH 64
class CPDF_Action {
 public:
  enum ActionType {
    Unknown = 0,
    GoTo,
    GoToR,
    GoToE,
    Launch,
    Thread,
    URI,
    Sound,
    Movie,
    Hide,
    Named,
    SubmitForm,
    ResetForm,
    ImportData,
    JavaScript,
    SetOCGState,
    Rendition,
    Trans,
    GoTo3DView
  };

  CPDF_Action() : m_pDict(nullptr) {}
  explicit CPDF_Action(CPDF_Dictionary* pDict) : m_pDict(pDict) {}

  operator bool() const { return m_pDict != NULL; }

  CPDF_Dictionary* GetDict() const { return m_pDict; }

  CFX_ByteString GetTypeName() const { return m_pDict->GetString("S"); }

  ActionType GetType() const;

  CPDF_Dest GetDest(CPDF_Document* pDoc) const;

  CFX_WideString GetFilePath() const;

  FX_BOOL GetNewWindow() const { return m_pDict->GetBoolean("NewWindow"); }

  CFX_ByteString GetURI(CPDF_Document* pDoc) const;

  FX_BOOL GetMouseMap() const { return m_pDict->GetBoolean("IsMap"); }

  CPDF_ActionFields GetWidgets() const { return this; }

  FX_BOOL GetHideStatus() const { return m_pDict->GetBoolean("H", TRUE); }

  CFX_ByteString GetNamedAction() const { return m_pDict->GetString("N"); }

  FX_DWORD GetFlags() const { return m_pDict->GetInteger("Flags"); }

  CFX_WideString GetJavaScript() const;

  CPDF_Dictionary* GetAnnot() const;

  int32_t GetOperationType() const;

  CPDF_Stream* GetSoundStream() const { return m_pDict->GetStream("Sound"); }

  FX_FLOAT GetVolume() const { return m_pDict->GetNumber("Volume"); }

  FX_BOOL IsSynchronous() const { return m_pDict->GetBoolean("Synchronous"); }

  FX_BOOL IsRepeat() const { return m_pDict->GetBoolean("Repeat"); }

  FX_BOOL IsMixPlay() const { return m_pDict->GetBoolean("Mix"); }

  FX_DWORD GetSubActionsCount() const;

  CPDF_Action GetSubAction(FX_DWORD iIndex) const;

 protected:
  CPDF_Dictionary* m_pDict;
};
class CPDF_AAction {
 public:
  // TODO(thestig): Examine why this cannot be explicit.
  CPDF_AAction(CPDF_Dictionary* pDict = NULL) { m_pDict = pDict; }

  operator CPDF_Dictionary*() const { return m_pDict; }

  enum AActionType {
    CursorEnter = 0,
    CursorExit,
    ButtonDown,
    ButtonUp,
    GetFocus,
    LoseFocus,
    PageOpen,
    PageClose,
    PageVisible,
    PageInvisible,
    OpenPage,
    ClosePage,
    KeyStroke,
    Format,
    Validate,
    Calculate,
    CloseDocument,
    SaveDocument,
    DocumentSaved,
    PrintDocument,
    DocumentPrinted
  };

  FX_BOOL ActionExist(AActionType eType) const;

  CPDF_Action GetAction(AActionType eType) const;

  FX_POSITION GetStartPos() const;

  CPDF_Action GetNextAction(FX_POSITION& pos, AActionType& eType) const;

  CPDF_Dictionary* m_pDict;
};
class CPDF_DocJSActions {
 public:
  explicit CPDF_DocJSActions(CPDF_Document* pDoc);

  int CountJSActions() const;

  CPDF_Action GetJSAction(int index, CFX_ByteString& csName) const;

  CPDF_Action GetJSAction(const CFX_ByteString& csName) const;

  int FindJSAction(const CFX_ByteString& csName) const;

  CPDF_Document* GetDocument() const { return m_pDocument; }

 protected:
  CPDF_Document* const m_pDocument;
};
class CPDF_FileSpec {
 public:
  CPDF_FileSpec();

  explicit CPDF_FileSpec(CPDF_Object* pObj) { m_pObj = pObj; }

  operator CPDF_Object*() const { return m_pObj; }

  FX_BOOL IsURL() const;

  FX_BOOL GetFileName(CFX_WideString& wsFileName) const;

  CPDF_Stream* GetFileStream() const;

  void SetFileName(const CFX_WideStringC& wsFileName, FX_BOOL bURL = FALSE);

 protected:
  CPDF_Object* m_pObj;
};

class CPDF_LinkList {
 public:
  CPDF_LinkList();
  ~CPDF_LinkList();

  CPDF_Link GetLinkAtPoint(CPDF_Page* pPage,
                           FX_FLOAT pdf_x,
                           FX_FLOAT pdf_y,
                           int* z_order);

 private:
  const std::vector<CPDF_Dictionary*>* GetPageLinks(CPDF_Page* pPage);

  void LoadPageLinks(CPDF_Page* pPage, std::vector<CPDF_Dictionary*>* pList);

  std::map<FX_DWORD, std::vector<CPDF_Dictionary*>> m_PageMap;
};

class CPDF_Link {
 public:
  CPDF_Link() : m_pDict(nullptr) {}
  explicit CPDF_Link(CPDF_Dictionary* pDict) : m_pDict(pDict) {}

  CPDF_Dictionary* GetDict() const { return m_pDict; }

  CFX_FloatRect GetRect();
  CPDF_Dest GetDest(CPDF_Document* pDoc);
  CPDF_Action GetAction();

 protected:
  CPDF_Dictionary* m_pDict;
};

#define ANNOTFLAG_INVISIBLE 0x0001
#define ANNOTFLAG_HIDDEN 0x0002
#define ANNOTFLAG_PRINT 0x0004
#define ANNOTFLAG_NOZOOM 0x0008
#define ANNOTFLAG_NOROTATE 0x0010
#define ANNOTFLAG_NOVIEW 0x0020
#define ANNOTFLAG_READONLY 0x0040
#define ANNOTFLAG_LOCKED 0x0080
#define ANNOTFLAG_TOGGLENOVIEW 0x0100

class CPDF_Annot : public CFX_PrivateData {
 public:
  enum AppearanceMode { Normal, Rollover, Down };

  CPDF_Annot(CPDF_Dictionary* pDict, CPDF_AnnotList* pList);
  ~CPDF_Annot();

  CFX_ByteString GetSubType() const;

  FX_DWORD GetFlags() const;

  void GetRect(CFX_FloatRect& rect) const;

  const CPDF_Dictionary* GetAnnotDict() const { return m_pAnnotDict; }
  CPDF_Dictionary* GetAnnotDict() { return m_pAnnotDict; }

  FX_BOOL DrawAppearance(const CPDF_Page* pPage,
                         CFX_RenderDevice* pDevice,
                         const CFX_AffineMatrix* pUser2Device,
                         AppearanceMode mode,
                         const CPDF_RenderOptions* pOptions);

  FX_BOOL DrawInContext(const CPDF_Page* pPage,
                        const CPDF_RenderContext* pContext,
                        const CFX_AffineMatrix* pUser2Device,
                        AppearanceMode mode);

  void ClearCachedAP();

  void DrawBorder(CFX_RenderDevice* pDevice,
                  const CFX_AffineMatrix* pUser2Device,
                  const CPDF_RenderOptions* pOptions);

  CPDF_Form* GetAPForm(const CPDF_Page* pPage, AppearanceMode mode);

 private:
  CPDF_Dictionary* const m_pAnnotDict;

  CPDF_AnnotList* const m_pList;

  const CFX_ByteString m_sSubtype;

  CFX_MapPtrToPtr m_APMap;
};

class CPDF_AnnotList {
 public:
  explicit CPDF_AnnotList(CPDF_Page* pPage);
  ~CPDF_AnnotList();

  void DisplayAnnots(const CPDF_Page* pPage,
                     CPDF_RenderContext* pContext,
                     FX_BOOL bPrinting,
                     CFX_AffineMatrix* pMatrix,
                     FX_BOOL bShowWidget,
                     CPDF_RenderOptions* pOptions) {
    DisplayAnnots(pPage, nullptr, pContext, bPrinting, pMatrix,
                  bShowWidget ? 3 : 1, pOptions, nullptr);
  }
  void DisplayAnnots(const CPDF_Page* pPage,
                     CFX_RenderDevice* pDevice,
                     CPDF_RenderContext* pContext,
                     FX_BOOL bPrinting,
                     CFX_AffineMatrix* pMatrix,
                     FX_DWORD dwAnnotFlags,
                     CPDF_RenderOptions* pOptions,
                     FX_RECT* pClipRect);
  size_t Count() const { return m_AnnotList.size(); }
  CPDF_Annot* GetAt(size_t index) const { return m_AnnotList[index]; }
  const std::vector<CPDF_Annot*>& All() const { return m_AnnotList; }
  CPDF_Document* GetDocument() const { return m_pDocument; }

 protected:
  void DisplayPass(const CPDF_Page* pPage,
                   CFX_RenderDevice* pDevice,
                   CPDF_RenderContext* pContext,
                   FX_BOOL bPrinting,
                   CFX_AffineMatrix* pMatrix,
                   FX_BOOL bWidget,
                   CPDF_RenderOptions* pOptions,
                   FX_RECT* clip_rect);

  CPDF_Document* const m_pDocument;
  std::vector<CPDF_Annot*> m_AnnotList;
};

#define COLORTYPE_TRANSPARENT 0
#define COLORTYPE_GRAY 1
#define COLORTYPE_RGB 2
#define COLORTYPE_CMYK 3
class CPDF_DefaultAppearance {
 public:
  // TODO(thestig): Examine why this cannot be explicit.
  CPDF_DefaultAppearance(const CFX_ByteString& csDA = "") { m_csDA = csDA; }

  CPDF_DefaultAppearance(const CPDF_DefaultAppearance& cDA) {
    m_csDA = (CFX_ByteString)(CPDF_DefaultAppearance&)cDA;
  }

  operator CFX_ByteString() const { return m_csDA; }

  const CPDF_DefaultAppearance& operator=(const CFX_ByteString& csDA) {
    m_csDA = csDA;
    return *this;
  }

  const CPDF_DefaultAppearance& operator=(const CPDF_DefaultAppearance& cDA) {
    m_csDA = (CFX_ByteString)(CPDF_DefaultAppearance&)cDA;
    return *this;
  }

  FX_BOOL HasFont();

  CFX_ByteString GetFontString();

  void GetFont(CFX_ByteString& csFontNameTag, FX_FLOAT& fFontSize);

  FX_BOOL HasColor(FX_BOOL bStrokingOperation = FALSE);

  CFX_ByteString GetColorString(FX_BOOL bStrokingOperation = FALSE);

  void GetColor(int& iColorType,
                FX_FLOAT fc[4],
                FX_BOOL bStrokingOperation = FALSE);

  void GetColor(FX_ARGB& color,
                int& iColorType,
                FX_BOOL bStrokingOperation = FALSE);

  FX_BOOL HasTextMatrix();

  CFX_ByteString GetTextMatrixString();

  CFX_AffineMatrix GetTextMatrix();

 protected:
  CFX_ByteString m_csDA;
};
#define FIELDTYPE_UNKNOWN 0
#define FIELDTYPE_PUSHBUTTON 1
#define FIELDTYPE_CHECKBOX 2
#define FIELDTYPE_RADIOBUTTON 3
#define FIELDTYPE_COMBOBOX 4
#define FIELDTYPE_LISTBOX 5
#define FIELDTYPE_TEXTFIELD 6
#define FIELDTYPE_SIGNATURE 7
class CPDF_InterForm : public CFX_PrivateData {
 public:
  CPDF_InterForm(CPDF_Document* pDocument, FX_BOOL bUpdateAP);
  ~CPDF_InterForm();

  static void EnableUpdateAP(FX_BOOL bUpdateAP);

  static FX_BOOL UpdatingAPEnabled();

  static CFX_ByteString GenerateNewResourceName(const CPDF_Dictionary* pResDict,
                                                const FX_CHAR* csType,
                                                int iMinLen = 2,
                                                const FX_CHAR* csPrefix = "");

  static CPDF_Font* AddSystemDefaultFont(const CPDF_Document* pDocument);

  static CPDF_Font* AddSystemFont(const CPDF_Document* pDocument,
                                  CFX_ByteString csFontName,
                                  uint8_t iCharSet = 1);

  static CPDF_Font* AddSystemFont(const CPDF_Document* pDocument,
                                  CFX_WideString csFontName,
                                  uint8_t iCharSet = 1);

  static CPDF_Font* AddStandardFont(const CPDF_Document* pDocument,
                                    CFX_ByteString csFontName);

  static CFX_ByteString GetNativeFont(uint8_t iCharSet, void* pLogFont = NULL);

  static CFX_ByteString GetNativeFont(void* pLogFont = NULL);

  static uint8_t GetNativeCharSet();

  static CPDF_Font* AddNativeFont(uint8_t iCharSet,
                                  const CPDF_Document* pDocument);

  static CPDF_Font* AddNativeFont(const CPDF_Document* pDocument);

  FX_BOOL ValidateFieldName(CFX_WideString& csNewFieldName, int iType);

  FX_BOOL ValidateFieldName(const CPDF_FormField* pField,
                            CFX_WideString& csNewFieldName);

  FX_BOOL ValidateFieldName(const CPDF_FormControl* pControl,
                            CFX_WideString& csNewFieldName);

  FX_DWORD CountFields(const CFX_WideString& csFieldName = L"");

  CPDF_FormField* GetField(FX_DWORD index,
                           const CFX_WideString& csFieldName = L"");

  void GetAllFieldNames(CFX_WideStringArray& allFieldNames);

  CPDF_FormField* GetFieldByDict(CPDF_Dictionary* pFieldDict) const;

  CPDF_FormControl* GetControlAtPoint(CPDF_Page* pPage,
                                      FX_FLOAT pdf_x,
                                      FX_FLOAT pdf_y,
                                      int* z_order) const;

  CPDF_FormControl* GetControlByDict(const CPDF_Dictionary* pWidgetDict) const;

  CPDF_Document* GetDocument() const { return m_pDocument; }

  CPDF_Dictionary* GetFormDict() const { return m_pFormDict; }

  FX_BOOL NeedConstructAP();

  void NeedConstructAP(FX_BOOL bNeedAP);

  int CountFieldsInCalculationOrder();

  CPDF_FormField* GetFieldInCalculationOrder(int index);

  int FindFieldInCalculationOrder(const CPDF_FormField* pField);

  FX_DWORD CountFormFonts();

  CPDF_Font* GetFormFont(FX_DWORD index, CFX_ByteString& csNameTag);

  CPDF_Font* GetFormFont(CFX_ByteString csNameTag);

  CPDF_Font* GetFormFont(CFX_ByteString csFontName, CFX_ByteString& csNameTag);

  CPDF_Font* GetNativeFormFont(uint8_t iCharSet, CFX_ByteString& csNameTag);

  CPDF_Font* GetNativeFormFont(CFX_ByteString& csNameTag);

  FX_BOOL FindFormFont(const CPDF_Font* pFont, CFX_ByteString& csNameTag);

  FX_BOOL FindFormFont(CFX_ByteString csFontName,
                       CPDF_Font*& pFont,
                       CFX_ByteString& csNameTag);

  inline FX_BOOL FindFormFont(CFX_WideString csFontName,
                              CPDF_Font*& pFont,
                              CFX_ByteString& csNameTag) {
    return FindFormFont(PDF_EncodeText(csFontName), pFont, csNameTag);
  }

  void AddFormFont(const CPDF_Font* pFont, CFX_ByteString& csNameTag);

  CPDF_Font* AddNativeFormFont(uint8_t iCharSet, CFX_ByteString& csNameTag);

  CPDF_Font* AddNativeFormFont(CFX_ByteString& csNameTag);

  void RemoveFormFont(const CPDF_Font* pFont);

  void RemoveFormFont(CFX_ByteString csNameTag);

  CPDF_DefaultAppearance GetDefaultAppearance();

  CPDF_Font* GetDefaultFormFont();

  int GetFormAlignment();

  CPDF_FormField* CheckRequiredFields(
      const std::vector<CPDF_FormField*>* fields,
      bool bIncludeOrExclude) const;

  CFDF_Document* ExportToFDF(const CFX_WideStringC& pdf_path,
                             bool bSimpleFileSpec = false) const;

  CFDF_Document* ExportToFDF(const CFX_WideStringC& pdf_path,
                             const std::vector<CPDF_FormField*>& fields,
                             bool bIncludeOrExclude = true,
                             bool bSimpleFileSpec = false) const;

  FX_BOOL ImportFromFDF(const CFDF_Document* pFDFDoc, FX_BOOL bNotify = FALSE);

  bool ResetForm(const std::vector<CPDF_FormField*>& fields,
                 bool bIncludeOrExclude = true,
                 bool bNotify = false);

  bool ResetForm(bool bNotify = false);

  CPDF_FormNotify* GetFormNotify() const { return m_pFormNotify; }

  void SetFormNotify(const CPDF_FormNotify* pNotify);

  FX_BOOL IsUpdated() { return m_bUpdated; }

  void ClearUpdatedFlag() { m_bUpdated = FALSE; }

  FX_BOOL HasXFAForm() const;

  void FixPageFields(const CPDF_Page* pPage);

 protected:
  static FX_BOOL m_bUpdateAP;

  void LoadField(CPDF_Dictionary* pFieldDict, int nLevel = 0);

  CPDF_Object* GetFieldAttr(CPDF_Dictionary* pFieldDict, const FX_CHAR* name);

  CPDF_FormField* AddTerminalField(CPDF_Dictionary* pFieldDict);

  CPDF_FormControl* AddControl(const CPDF_FormField* pField,
                               CPDF_Dictionary* pWidgetDict);

  void FDF_ImportField(CPDF_Dictionary* pField,
                       const CFX_WideString& parent_name,
                       FX_BOOL bNotify = FALSE,
                       int nLevel = 0);

  FX_BOOL ValidateFieldName(CFX_WideString& csNewFieldName,
                            int iType,
                            const CPDF_FormField* pExcludedField,
                            const CPDF_FormControl* pExcludedControl);

  int CompareFieldName(const CFX_WideString& name1,
                       const CFX_WideString& name2);

  int CompareFieldName(const CFX_ByteString& name1,
                       const CFX_ByteString& name2);

  CPDF_Document* const m_pDocument;

  FX_BOOL m_bGenerateAP;

  CPDF_Dictionary* m_pFormDict;

  std::map<const CPDF_Dictionary*, CPDF_FormControl*> m_ControlMap;

  nonstd::unique_ptr<CFieldTree> m_pFieldTree;

  CFX_ByteString m_bsEncoding;

  CPDF_FormNotify* m_pFormNotify;

  FX_BOOL m_bUpdated;
  friend class CPDF_FormControl;
  friend class CPDF_FormField;
};
#define FORMFIELD_READONLY 0x01
#define FORMFIELD_REQUIRED 0x02
#define FORMFIELD_NOEXPORT 0x04
#define FORMRADIO_NOTOGGLEOFF 0x100
#define FORMRADIO_UNISON 0x200
#define FORMTEXT_MULTILINE 0x100
#define FORMTEXT_PASSWORD 0x200
#define FORMTEXT_NOSCROLL 0x400
#define FORMTEXT_COMB 0x800
#define FORMCOMBO_EDIT 0x100
#define FORMLIST_MULTISELECT 0x100
class CPDF_FormField {
 public:
  enum Type {
    Unknown,
    PushButton,
    RadioButton,
    CheckBox,
    Text,
    RichText,
    File,
    ListBox,
    ComboBox,
    Sign
  };

  CFX_WideString GetFullName();

  Type GetType() { return m_Type; }

  FX_DWORD GetFlags() { return m_Flags; }

  CPDF_Dictionary* GetFieldDict() const { return m_pDict; }

  void SetFieldDict(CPDF_Dictionary* pDict) { m_pDict = pDict; }

  FX_BOOL ResetField(FX_BOOL bNotify = FALSE);

  int CountControls() { return m_ControlList.GetSize(); }

  CPDF_FormControl* GetControl(int index) {
    return (CPDF_FormControl*)m_ControlList.GetAt(index);
  }

  int GetControlIndex(const CPDF_FormControl* pControl);

  int GetFieldType();

  CPDF_AAction GetAdditionalAction();

  CFX_WideString GetAlternateName();

  CFX_WideString GetMappingName();

  FX_DWORD GetFieldFlags();

  CFX_ByteString GetDefaultStyle();

  CFX_WideString GetRichTextString();

  CFX_WideString GetValue();

  CFX_WideString GetDefaultValue();

  FX_BOOL SetValue(const CFX_WideString& value, FX_BOOL bNotify = FALSE);

  int GetMaxLen();

  int CountSelectedItems();

  int GetSelectedIndex(int index);

  FX_BOOL ClearSelection(FX_BOOL bNotify = FALSE);

  FX_BOOL IsItemSelected(int index);

  FX_BOOL SetItemSelection(int index,
                           FX_BOOL bSelected,
                           FX_BOOL bNotify = FALSE);

  FX_BOOL IsItemDefaultSelected(int index);

  int GetDefaultSelectedItem();

  int CountOptions();

  CFX_WideString GetOptionLabel(int index);

  CFX_WideString GetOptionValue(int index);

  int FindOption(CFX_WideString csOptLabel);

  int FindOptionValue(const CFX_WideString& csOptValue, int iStartIndex = 0);

  FX_BOOL CheckControl(int iControlIndex,
                       FX_BOOL bChecked,
                       FX_BOOL bNotify = FALSE);

  int GetTopVisibleIndex();

  int CountSelectedOptions();

  int GetSelectedOptionIndex(int index);

  FX_BOOL IsOptionSelected(int iOptIndex);

  FX_BOOL SelectOption(int iOptIndex,
                       FX_BOOL bSelected,
                       FX_BOOL bNotify = FALSE);

  FX_BOOL ClearSelectedOptions(FX_BOOL bNotify = FALSE);

  FX_BOOL ClearOptions(FX_BOOL bNotify = FALSE);

  int InsertOption(CFX_WideString csOptLabel,
                   int index = -1,
                   FX_BOOL bNotify = FALSE);

  FX_FLOAT GetFontSize() { return m_FontSize; }

  CPDF_Font* GetFont() { return m_pFont; }

 protected:
  CPDF_FormField(CPDF_InterForm* pForm, CPDF_Dictionary* pDict);

  ~CPDF_FormField();

  CPDF_FormField::Type m_Type;

  FX_DWORD m_Flags;

  CPDF_InterForm* m_pForm;

  CPDF_Dictionary* m_pDict;

  CFX_PtrArray m_ControlList;
  friend class CPDF_InterForm;
  friend class CPDF_FormControl;

  CFX_WideString GetValue(FX_BOOL bDefault);

  FX_BOOL SetValue(const CFX_WideString& value,
                   FX_BOOL bDefault,
                   FX_BOOL bNotify);

  void SyncFieldFlags();

  int FindListSel(CPDF_String* str);

  CFX_WideString GetOptionText(int index, int sub_index);

  void LoadDA();

  void UpdateAP(CPDF_FormControl* pControl);

  CFX_WideString GetCheckValue(FX_BOOL bDefault);

  FX_BOOL SetCheckValue(const CFX_WideString& value,
                        FX_BOOL bDefault,
                        FX_BOOL bNotify);

  FX_FLOAT m_FontSize;

  CPDF_Font* m_pFont;
};
CPDF_Object* FPDF_GetFieldAttr(CPDF_Dictionary* pFieldDict,
                               const FX_CHAR* name,
                               int nLevel = 0);
class CPDF_IconFit {
 public:
  // TODO(thestig): Examine why this cannot be explicit.
  CPDF_IconFit(CPDF_Dictionary* pDict = NULL) { m_pDict = pDict; }

  operator CPDF_Dictionary*() const { return m_pDict; }

  enum ScaleMethod { Always = 0, Bigger, Smaller, Never };

  ScaleMethod GetScaleMethod();

  FX_BOOL IsProportionalScale();

  void GetIconPosition(FX_FLOAT& fLeft, FX_FLOAT& fBottom);

  FX_BOOL GetFittingBounds();

  CPDF_Dictionary* m_pDict;
};

#define TEXTPOS_CAPTION 0
#define TEXTPOS_ICON 1
#define TEXTPOS_BELOW 2
#define TEXTPOS_ABOVE 3
#define TEXTPOS_RIGHT 4
#define TEXTPOS_LEFT 5
#define TEXTPOS_OVERLAID 6
class CPDF_FormControl {
 public:
  CPDF_FormField::Type GetType() { return m_pField->GetType(); }

  CPDF_InterForm* GetInterForm() const { return m_pForm; }

  CPDF_FormField* GetField() const { return m_pField; }

  CPDF_Dictionary* GetWidget() const { return m_pWidgetDict; }

  CFX_FloatRect GetRect() const;

  void DrawControl(CFX_RenderDevice* pDevice,
                   CFX_AffineMatrix* pMatrix,
                   CPDF_Page* pPage,
                   CPDF_Annot::AppearanceMode mode,
                   const CPDF_RenderOptions* pOptions = NULL);

  CFX_ByteString GetCheckedAPState();

  CFX_WideString GetExportValue();

  FX_BOOL IsChecked();

  FX_BOOL IsDefaultChecked();

  enum HighlightingMode { None = 0, Invert, Outline, Push, Toggle };

  HighlightingMode GetHighlightingMode();

  FX_BOOL HasMKEntry(CFX_ByteString csEntry);

  int GetRotation();

  inline FX_ARGB GetBorderColor(int& iColorType) {
    return GetColor(iColorType, "BC");
  }

  inline FX_FLOAT GetOriginalBorderColor(int index) {
    return GetOriginalColor(index, "BC");
  }

  inline void GetOriginalBorderColor(int& iColorType, FX_FLOAT fc[4]) {
    GetOriginalColor(iColorType, fc, "BC");
  }

  inline FX_ARGB GetBackgroundColor(int& iColorType) {
    return GetColor(iColorType, "BG");
  }

  inline FX_FLOAT GetOriginalBackgroundColor(int index) {
    return GetOriginalColor(index, "BG");
  }

  inline void GetOriginalBackgroundColor(int& iColorType, FX_FLOAT fc[4]) {
    GetOriginalColor(iColorType, fc, "BG");
  }

  inline CFX_WideString GetNormalCaption() { return GetCaption("CA"); }

  inline CFX_WideString GetRolloverCaption() { return GetCaption("RC"); }

  inline CFX_WideString GetDownCaption() { return GetCaption("AC"); }

  inline CPDF_Stream* GetNormalIcon() { return GetIcon("I"); }

  inline CPDF_Stream* GetRolloverIcon() { return GetIcon("RI"); }

  inline CPDF_Stream* GetDownIcon() { return GetIcon("IX"); }

  CPDF_IconFit GetIconFit();

  int GetTextPosition();

  CPDF_Action GetAction();

  CPDF_AAction GetAdditionalAction();

  CPDF_DefaultAppearance GetDefaultAppearance();

  CPDF_Font* GetDefaultControlFont();

  int GetControlAlignment();

 protected:
  CPDF_FormControl(CPDF_FormField* pField, CPDF_Dictionary* pWidgetDict);

  CFX_ByteString GetOnStateName();

  void SetOnStateName(const CFX_ByteString& csOn);

  void CheckControl(FX_BOOL bChecked);

  FX_ARGB GetColor(int& iColorType, CFX_ByteString csEntry);

  FX_FLOAT GetOriginalColor(int index, CFX_ByteString csEntry);

  void GetOriginalColor(int& iColorType,
                        FX_FLOAT fc[4],
                        CFX_ByteString csEntry);

  CFX_WideString GetCaption(CFX_ByteString csEntry);

  CPDF_Stream* GetIcon(CFX_ByteString csEntry);

  CPDF_ApSettings GetMK(FX_BOOL bCreate);

  CPDF_InterForm* m_pForm;

  CPDF_FormField* m_pField;

  CPDF_Dictionary* m_pWidgetDict;
  friend class CPDF_InterForm;
  friend class CPDF_FormField;
};
class CPDF_FormNotify {
 public:
  virtual ~CPDF_FormNotify() {}

  virtual int BeforeValueChange(const CPDF_FormField* pField,
                                CFX_WideString& csValue) {
    return 0;
  }

  virtual int AfterValueChange(const CPDF_FormField* pField) { return 0; }

  virtual int BeforeSelectionChange(const CPDF_FormField* pField,
                                    CFX_WideString& csValue) {
    return 0;
  }

  virtual int AfterSelectionChange(const CPDF_FormField* pField) { return 0; }

  virtual int AfterCheckedStatusChange(const CPDF_FormField* pField,
                                       const CFX_ByteArray& statusArray) {
    return 0;
  }

  virtual int BeforeFormReset(const CPDF_InterForm* pForm) { return 0; }

  virtual int AfterFormReset(const CPDF_InterForm* pForm) { return 0; }

  virtual int BeforeFormImportData(const CPDF_InterForm* pForm) { return 0; }

  virtual int AfterFormImportData(const CPDF_InterForm* pForm) { return 0; }
};
FX_BOOL FPDF_GenerateAP(CPDF_Document* pDoc, CPDF_Dictionary* pAnnotDict);
class CPDF_PageLabel {
 public:
  explicit CPDF_PageLabel(CPDF_Document* pDocument) { m_pDocument = pDocument; }

  CFX_WideString GetLabel(int nPage) const;

  int32_t GetPageByLabel(const CFX_ByteStringC& bsLabel) const;

  int32_t GetPageByLabel(const CFX_WideStringC& wsLabel) const;

 protected:
  CPDF_Document* m_pDocument;
};

class CPDF_Metadata {
 public:
  explicit CPDF_Metadata(CPDF_Document* pDoc);
  ~CPDF_Metadata();

  const CXML_Element* GetRoot() const;

 private:
  nonstd::unique_ptr<CXML_Element> m_pXmlElement;
};

class CPDF_ViewerPreferences {
 public:
  explicit CPDF_ViewerPreferences(CPDF_Document* pDoc);

  ~CPDF_ViewerPreferences();

  FX_BOOL IsDirectionR2L() const;

  FX_BOOL PrintScaling() const;

  int32_t NumCopies() const;

  CPDF_Array* PrintPageRange() const;

  CFX_ByteString Duplex() const;

 protected:
  CPDF_Document* const m_pDoc;
};

class CPDF_ApSettings {
 public:
  // TODO(thestig): Examine why this cannot be explicit.
  CPDF_ApSettings(CPDF_Dictionary* pDict = NULL) { m_pDict = pDict; }

  operator CPDF_Dictionary*() const { return m_pDict; }

  FX_BOOL HasMKEntry(const CFX_ByteStringC& csEntry);

  int GetRotation();

  inline FX_ARGB GetBorderColor(int& iColorType) {
    return GetColor(iColorType, FX_BSTRC("BC"));
  }

  inline FX_FLOAT GetOriginalBorderColor(int index) {
    return GetOriginalColor(index, FX_BSTRC("BC"));
  }

  inline void GetOriginalBorderColor(int& iColorType, FX_FLOAT fc[4]) {
    GetOriginalColor(iColorType, fc, FX_BSTRC("BC"));
  }

  inline FX_ARGB GetBackgroundColor(int& iColorType) {
    return GetColor(iColorType, FX_BSTRC("BG"));
  }

  inline FX_FLOAT GetOriginalBackgroundColor(int index) {
    return GetOriginalColor(index, FX_BSTRC("BG"));
  }

  inline void GetOriginalBackgroundColor(int& iColorType, FX_FLOAT fc[4]) {
    GetOriginalColor(iColorType, fc, FX_BSTRC("BG"));
  }

  inline CFX_WideString GetNormalCaption() {
    return GetCaption(FX_BSTRC("CA"));
  }

  inline CFX_WideString GetRolloverCaption() {
    return GetCaption(FX_BSTRC("RC"));
  }

  inline CFX_WideString GetDownCaption() { return GetCaption(FX_BSTRC("AC")); }

  inline CPDF_Stream* GetNormalIcon() { return GetIcon(FX_BSTRC("I")); }

  inline CPDF_Stream* GetRolloverIcon() { return GetIcon(FX_BSTRC("RI")); }

  inline CPDF_Stream* GetDownIcon() { return GetIcon(FX_BSTRC("IX")); }

  CPDF_IconFit GetIconFit();

  int GetTextPosition();

  CPDF_Dictionary* m_pDict;

 protected:
  FX_ARGB GetColor(int& iColorType, const CFX_ByteStringC& csEntry);

  FX_FLOAT GetOriginalColor(int index, const CFX_ByteStringC& csEntry);

  void GetOriginalColor(int& iColorType,
                        FX_FLOAT fc[4],
                        const CFX_ByteStringC& csEntry);

  CFX_WideString GetCaption(const CFX_ByteStringC& csEntry);

  CPDF_Stream* GetIcon(const CFX_ByteStringC& csEntry);
  friend class CPDF_FormControl;
};

#endif  // CORE_INCLUDE_FPDFDOC_FPDF_DOC_H_