blob: e11168717ce509999f5fd79ce24e5247c5387d93 [file] [log] [blame]
// Copyright 2016 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 "core/fpdfdoc/cpdf_interactiveform.h"
#include <utility>
#include <vector>
#include "build/build_config.h"
#include "constants/form_fields.h"
#include "constants/form_flags.h"
#include "constants/stream_dict_common.h"
#include "core/fpdfapi/font/cpdf_font.h"
#include "core/fpdfapi/font/cpdf_fontencoding.h"
#include "core/fpdfapi/page/cpdf_docpagedata.h"
#include "core/fpdfapi/page/cpdf_page.h"
#include "core/fpdfapi/parser/cfdf_document.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_reference.h"
#include "core/fpdfapi/parser/cpdf_string.h"
#include "core/fpdfapi/parser/fpdf_parser_utility.h"
#include "core/fpdfdoc/cpdf_filespec.h"
#include "core/fpdfdoc/cpdf_formcontrol.h"
#include "core/fxcrt/fx_codepage.h"
#include "core/fxcrt/stl_util.h"
#include "core/fxge/cfx_substfont.h"
#include "core/fxge/fx_font.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/base/check.h"
#include "third_party/base/containers/contains.h"
#include "third_party/base/numerics/safe_conversions.h"
namespace {
const int nMaxRecursion = 32;
#if BUILDFLAG(IS_WIN)
struct PDF_FONTDATA {
bool bFind;
LOGFONTA lf;
};
int CALLBACK EnumFontFamExProc(ENUMLOGFONTEXA* lpelfe,
NEWTEXTMETRICEX* lpntme,
DWORD FontType,
LPARAM lParam) {
if (FontType != 0x004 || strchr(lpelfe->elfLogFont.lfFaceName, '@'))
return 1;
PDF_FONTDATA* pData = (PDF_FONTDATA*)lParam;
memcpy(&pData->lf, &lpelfe->elfLogFont, sizeof(LOGFONTA));
pData->bFind = true;
return 0;
}
bool RetrieveSpecificFont(FX_Charset charSet,
LPCSTR pcsFontName,
LOGFONTA& lf) {
memset(&lf, 0, sizeof(LOGFONTA));
lf.lfCharSet = static_cast<int>(charSet);
lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
if (pcsFontName) {
// TODO(dsinclair): Should this be strncpy?
// NOLINTNEXTLINE(runtime/printf)
strcpy(lf.lfFaceName, pcsFontName);
}
PDF_FONTDATA fd;
memset(&fd, 0, sizeof(PDF_FONTDATA));
HDC hDC = ::GetDC(nullptr);
EnumFontFamiliesExA(hDC, &lf, (FONTENUMPROCA)EnumFontFamExProc, (LPARAM)&fd,
0);
::ReleaseDC(nullptr, hDC);
if (fd.bFind)
memcpy(&lf, &fd.lf, sizeof(LOGFONTA));
return fd.bFind;
}
#endif // BUILDFLAG(IS_WIN)
ByteString GetNativeFontName(FX_Charset charSet, void* pLogFont) {
ByteString csFontName;
#if BUILDFLAG(IS_WIN)
LOGFONTA lf = {};
if (charSet == FX_Charset::kANSI) {
csFontName = CFX_Font::kDefaultAnsiFontName;
return csFontName;
}
bool bRet = false;
const ByteString default_font_name =
CFX_Font::GetDefaultFontNameByCharset(charSet);
if (!default_font_name.IsEmpty())
bRet = RetrieveSpecificFont(charSet, default_font_name.c_str(), lf);
if (!bRet) {
bRet =
RetrieveSpecificFont(charSet, CFX_Font::kUniversalDefaultFontName, lf);
}
if (!bRet)
bRet = RetrieveSpecificFont(charSet, "Microsoft Sans Serif", lf);
if (!bRet)
bRet = RetrieveSpecificFont(charSet, nullptr, lf);
if (bRet) {
if (pLogFont)
memcpy(pLogFont, &lf, sizeof(LOGFONTA));
csFontName = lf.lfFaceName;
}
#endif
return csFontName;
}
ByteString GenerateNewFontResourceName(const CPDF_Dictionary* pResDict,
const ByteString& csPrefix) {
static const char kDummyFontName[] = "ZiTi";
ByteString csStr = csPrefix;
if (csStr.IsEmpty())
csStr = kDummyFontName;
const size_t szCount = csStr.GetLength();
size_t m = 0;
ByteString csTmp;
while (m < strlen(kDummyFontName) && m < szCount)
csTmp += csStr[m++];
while (m < strlen(kDummyFontName)) {
csTmp += '0' + m % 10;
m++;
}
const CPDF_Dictionary* pDict = pResDict->GetDictFor("Font");
DCHECK(pDict);
int num = 0;
ByteString bsNum;
while (true) {
ByteString csKey = csTmp + bsNum;
if (!pDict->KeyExist(csKey))
return csKey;
if (m < szCount)
csTmp += csStr[m++];
else
bsNum = ByteString::FormatInteger(num++);
m++;
}
}
RetainPtr<CPDF_Font> AddStandardFont(CPDF_Document* pDocument) {
auto* pPageData = CPDF_DocPageData::FromDocument(pDocument);
static const CPDF_FontEncoding encoding(FontEncoding::kWinAnsi);
return pPageData->AddStandardFont(CFX_Font::kDefaultAnsiFontName, &encoding);
}
RetainPtr<CPDF_Font> AddNativeFont(FX_Charset charSet,
CPDF_Document* pDocument) {
DCHECK(pDocument);
#if BUILDFLAG(IS_WIN)
LOGFONTA lf;
ByteString csFontName = GetNativeFontName(charSet, &lf);
if (!csFontName.IsEmpty()) {
if (csFontName == CFX_Font::kDefaultAnsiFontName)
return AddStandardFont(pDocument);
return CPDF_DocPageData::FromDocument(pDocument)->AddWindowsFont(&lf);
}
#endif
return nullptr;
}
bool FindFont(CPDF_Dictionary* pFormDict,
const CPDF_Font* pFont,
ByteString* csNameTag) {
CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR");
if (!pDR)
return false;
CPDF_Dictionary* pFonts = pDR->GetDictFor("Font");
if (!ValidateFontResourceDict(pFonts))
return false;
CPDF_DictionaryLocker locker(pFonts);
for (const auto& it : locker) {
const ByteString& csKey = it.first;
const CPDF_Dictionary* pElement = ToDictionary(it.second->GetDirect());
if (!ValidateDictType(pElement, "Font"))
continue;
if (pFont->GetFontDict() == pElement) {
*csNameTag = csKey;
return true;
}
}
return false;
}
bool FindFontFromDoc(CPDF_Dictionary* pFormDict,
CPDF_Document* pDocument,
ByteString csFontName,
RetainPtr<CPDF_Font>& pFont,
ByteString* csNameTag) {
if (csFontName.IsEmpty())
return false;
CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR");
if (!pDR)
return false;
CPDF_Dictionary* pFonts = pDR->GetDictFor("Font");
if (!ValidateFontResourceDict(pFonts))
return false;
csFontName.Remove(' ');
CPDF_DictionaryLocker locker(pFonts);
for (const auto& it : locker) {
const ByteString& csKey = it.first;
CPDF_Dictionary* pElement = ToDictionary(it.second->GetDirect());
if (!ValidateDictType(pElement, "Font"))
continue;
pFont = CPDF_DocPageData::FromDocument(pDocument)->GetFont(pElement);
if (!pFont)
continue;
ByteString csBaseFont = pFont->GetBaseFontName();
csBaseFont.Remove(' ');
if (csBaseFont == csFontName) {
*csNameTag = csKey;
return true;
}
}
return false;
}
void AddFont(CPDF_Dictionary* pFormDict,
CPDF_Document* pDocument,
const RetainPtr<CPDF_Font>& pFont,
ByteString* csNameTag) {
DCHECK(pFormDict);
DCHECK(pFont);
ByteString csTag;
if (FindFont(pFormDict, pFont.Get(), &csTag)) {
*csNameTag = std::move(csTag);
return;
}
CPDF_Dictionary* pDR = GetOrCreateDict(pFormDict, "DR");
CPDF_Dictionary* pFonts = GetOrCreateDict(pDR, "Font");
if (csNameTag->IsEmpty())
*csNameTag = pFont->GetBaseFontName();
csNameTag->Remove(' ');
*csNameTag = GenerateNewFontResourceName(pDR, *csNameTag);
pFonts->SetNewFor<CPDF_Reference>(*csNameTag, pDocument,
pFont->GetFontDict()->GetObjNum());
}
FX_Charset GetNativeCharSet() {
return FX_GetCharsetFromCodePage(FX_GetACP());
}
CPDF_Dictionary* InitDict(CPDF_Document* pDocument) {
CPDF_Dictionary* pFormDict = pDocument->NewIndirect<CPDF_Dictionary>();
pDocument->GetRoot()->SetNewFor<CPDF_Reference>("AcroForm", pDocument,
pFormDict->GetObjNum());
ByteString csBaseName;
FX_Charset charSet = GetNativeCharSet();
RetainPtr<CPDF_Font> pFont = AddStandardFont(pDocument);
if (pFont)
AddFont(pFormDict, pDocument, pFont, &csBaseName);
if (charSet != FX_Charset::kANSI) {
ByteString csFontName = GetNativeFontName(charSet, nullptr);
if (!pFont || csFontName != CFX_Font::kDefaultAnsiFontName) {
pFont = AddNativeFont(charSet, pDocument);
if (pFont) {
csBaseName.clear();
AddFont(pFormDict, pDocument, pFont, &csBaseName);
}
}
}
ByteString csDA;
if (pFont)
csDA = "/" + PDF_NameEncode(csBaseName) + " 0 Tf ";
csDA += "0 g";
pFormDict->SetNewFor<CPDF_String>("DA", csDA, /*bHex=*/false);
return pFormDict;
}
RetainPtr<CPDF_Font> GetNativeFont(CPDF_Dictionary* pFormDict,
CPDF_Document* pDocument,
FX_Charset charSet,
ByteString* csNameTag) {
CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR");
if (!pDR)
return nullptr;
CPDF_Dictionary* pFonts = pDR->GetDictFor("Font");
if (!ValidateFontResourceDict(pFonts))
return nullptr;
CPDF_DictionaryLocker locker(pFonts);
for (const auto& it : locker) {
const ByteString& csKey = it.first;
CPDF_Dictionary* pElement = ToDictionary(it.second->GetDirect());
if (!ValidateDictType(pElement, "Font"))
continue;
auto* pData = CPDF_DocPageData::FromDocument(pDocument);
RetainPtr<CPDF_Font> pFind = pData->GetFont(pElement);
if (!pFind)
continue;
CFX_SubstFont* pSubst = pFind->GetSubstFont();
if (!pSubst)
continue;
if (pSubst->m_Charset == charSet) {
*csNameTag = csKey;
return pFind;
}
}
return nullptr;
}
class CFieldNameExtractor {
public:
explicit CFieldNameExtractor(const WideString& full_name)
: m_FullName(full_name) {}
WideStringView GetNext() {
size_t start_pos = m_iCur;
while (m_iCur < m_FullName.GetLength() && m_FullName[m_iCur] != L'.')
++m_iCur;
size_t length = m_iCur - start_pos;
if (m_iCur < m_FullName.GetLength() && m_FullName[m_iCur] == L'.')
++m_iCur;
return m_FullName.AsStringView().Substr(start_pos, length);
}
protected:
const WideString m_FullName;
size_t m_iCur = 0;
};
} // namespace
class CFieldTree {
public:
class Node {
public:
Node() : m_level(0) {}
Node(const WideString& short_name, int level)
: m_ShortName(short_name), m_level(level) {}
~Node() = default;
void AddChildNode(std::unique_ptr<Node> pNode) {
m_Children.push_back(std::move(pNode));
}
size_t GetChildrenCount() const { return m_Children.size(); }
Node* GetChildAt(size_t i) { return m_Children[i].get(); }
const Node* GetChildAt(size_t i) const { return m_Children[i].get(); }
CPDF_FormField* GetFieldAtIndex(size_t index) {
size_t nFieldsToGo = index;
return GetFieldInternal(&nFieldsToGo);
}
size_t CountFields() const { return CountFieldsInternal(); }
void SetField(std::unique_ptr<CPDF_FormField> pField) {
m_pField = std::move(pField);
}
CPDF_FormField* GetField() const { return m_pField.get(); }
WideString GetShortName() const { return m_ShortName; }
int GetLevel() const { return m_level; }
private:
CPDF_FormField* GetFieldInternal(size_t* pFieldsToGo) {
if (m_pField) {
if (*pFieldsToGo == 0)
return m_pField.get();
--*pFieldsToGo;
}
for (size_t i = 0; i < GetChildrenCount(); ++i) {
CPDF_FormField* pField = GetChildAt(i)->GetFieldInternal(pFieldsToGo);
if (pField)
return pField;
}
return nullptr;
}
size_t CountFieldsInternal() const {
size_t count = 0;
if (m_pField)
++count;
for (size_t i = 0; i < GetChildrenCount(); ++i)
count += GetChildAt(i)->CountFieldsInternal();
return count;
}
std::vector<std::unique_ptr<Node>> m_Children;
WideString m_ShortName;
std::unique_ptr<CPDF_FormField> m_pField;
const int m_level;
};
CFieldTree();
~CFieldTree();
bool SetField(const WideString& full_name,
std::unique_ptr<CPDF_FormField> pField);
CPDF_FormField* GetField(const WideString& full_name);
Node* GetRoot() { return m_pRoot.get(); }
Node* FindNode(const WideString& full_name);
Node* AddChild(Node* pParent, const WideString& short_name);
Node* Lookup(Node* pParent, WideStringView short_name);
private:
std::unique_ptr<Node> m_pRoot;
};
CFieldTree::CFieldTree() : m_pRoot(std::make_unique<Node>()) {}
CFieldTree::~CFieldTree() = default;
CFieldTree::Node* CFieldTree::AddChild(Node* pParent,
const WideString& short_name) {
if (!pParent)
return nullptr;
int level = pParent->GetLevel() + 1;
if (level > nMaxRecursion)
return nullptr;
auto pNew = std::make_unique<Node>(short_name, pParent->GetLevel() + 1);
Node* pChild = pNew.get();
pParent->AddChildNode(std::move(pNew));
return pChild;
}
CFieldTree::Node* CFieldTree::Lookup(Node* pParent, WideStringView short_name) {
if (!pParent)
return nullptr;
for (size_t i = 0; i < pParent->GetChildrenCount(); ++i) {
Node* pNode = pParent->GetChildAt(i);
if (pNode->GetShortName() == short_name)
return pNode;
}
return nullptr;
}
bool CFieldTree::SetField(const WideString& full_name,
std::unique_ptr<CPDF_FormField> pField) {
if (full_name.IsEmpty())
return false;
Node* pNode = GetRoot();
Node* pLast = nullptr;
CFieldNameExtractor name_extractor(full_name);
while (true) {
WideStringView name_view = name_extractor.GetNext();
if (name_view.IsEmpty())
break;
pLast = pNode;
pNode = Lookup(pLast, name_view);
if (pNode)
continue;
pNode = AddChild(pLast, WideString(name_view));
if (!pNode)
return false;
}
if (pNode == GetRoot())
return false;
pNode->SetField(std::move(pField));
return true;
}
CPDF_FormField* CFieldTree::GetField(const WideString& full_name) {
if (full_name.IsEmpty())
return nullptr;
Node* pNode = GetRoot();
Node* pLast = nullptr;
CFieldNameExtractor name_extractor(full_name);
while (pNode) {
WideStringView name_view = name_extractor.GetNext();
if (name_view.IsEmpty())
break;
pLast = pNode;
pNode = Lookup(pLast, name_view);
}
return pNode ? pNode->GetField() : nullptr;
}
CFieldTree::Node* CFieldTree::FindNode(const WideString& full_name) {
if (full_name.IsEmpty())
return nullptr;
Node* pNode = GetRoot();
Node* pLast = nullptr;
CFieldNameExtractor name_extractor(full_name);
while (pNode) {
WideStringView name_view = name_extractor.GetNext();
if (name_view.IsEmpty())
break;
pLast = pNode;
pNode = Lookup(pLast, name_view);
}
return pNode;
}
CPDF_InteractiveForm::CPDF_InteractiveForm(CPDF_Document* pDocument)
: m_pDocument(pDocument), m_pFieldTree(std::make_unique<CFieldTree>()) {
CPDF_Dictionary* pRoot = m_pDocument->GetRoot();
if (!pRoot)
return;
m_pFormDict.Reset(pRoot->GetDictFor("AcroForm"));
if (!m_pFormDict)
return;
CPDF_Array* pFields = m_pFormDict->GetArrayFor("Fields");
if (!pFields)
return;
for (size_t i = 0; i < pFields->size(); ++i)
LoadField(pFields->GetMutableDictAt(i).Get(), 0);
}
CPDF_InteractiveForm::~CPDF_InteractiveForm() = default;
bool CPDF_InteractiveForm::s_bUpdateAP = true;
// static
bool CPDF_InteractiveForm::IsUpdateAPEnabled() {
return s_bUpdateAP;
}
// static
void CPDF_InteractiveForm::SetUpdateAP(bool bUpdateAP) {
s_bUpdateAP = bUpdateAP;
}
// static
RetainPtr<CPDF_Font> CPDF_InteractiveForm::AddNativeInteractiveFormFont(
CPDF_Document* pDocument,
ByteString* csNameTag) {
DCHECK(pDocument);
DCHECK(csNameTag);
CPDF_Dictionary* pFormDict = pDocument->GetRoot()->GetDictFor("AcroForm");
if (!pFormDict)
pFormDict = InitDict(pDocument);
FX_Charset charSet = GetNativeCharSet();
ByteString csTemp;
RetainPtr<CPDF_Font> pFont =
GetNativeFont(pFormDict, pDocument, charSet, &csTemp);
if (pFont) {
*csNameTag = std::move(csTemp);
return pFont;
}
ByteString csFontName = GetNativeFontName(charSet, nullptr);
if (FindFontFromDoc(pFormDict, pDocument, csFontName, pFont, csNameTag))
return pFont;
pFont = AddNativeFont(charSet, pDocument);
if (!pFont)
return nullptr;
AddFont(pFormDict, pDocument, pFont, csNameTag);
return pFont;
}
size_t CPDF_InteractiveForm::CountFields(const WideString& csFieldName) const {
if (csFieldName.IsEmpty())
return m_pFieldTree->GetRoot()->CountFields();
CFieldTree::Node* pNode = m_pFieldTree->FindNode(csFieldName);
return pNode ? pNode->CountFields() : 0;
}
CPDF_FormField* CPDF_InteractiveForm::GetField(
size_t index,
const WideString& csFieldName) const {
if (csFieldName.IsEmpty())
return m_pFieldTree->GetRoot()->GetFieldAtIndex(index);
CFieldTree::Node* pNode = m_pFieldTree->FindNode(csFieldName);
return pNode ? pNode->GetFieldAtIndex(index) : nullptr;
}
CPDF_FormField* CPDF_InteractiveForm::GetFieldByDict(
const CPDF_Dictionary* pFieldDict) const {
if (!pFieldDict)
return nullptr;
WideString csWName = CPDF_FormField::GetFullNameForDict(pFieldDict);
return m_pFieldTree->GetField(csWName);
}
const CPDF_FormControl* CPDF_InteractiveForm::GetControlAtPoint(
const CPDF_Page* pPage,
const CFX_PointF& point,
int* z_order) const {
const CPDF_Array* pAnnotList = pPage->GetDict()->GetArrayFor("Annots");
if (!pAnnotList)
return nullptr;
for (size_t i = pAnnotList->size(); i > 0; --i) {
size_t annot_index = i - 1;
const CPDF_Dictionary* pAnnot = pAnnotList->GetDictAt(annot_index);
if (!pAnnot)
continue;
const auto it = m_ControlMap.find(pAnnot);
if (it == m_ControlMap.end())
continue;
const CPDF_FormControl* pControl = it->second.get();
if (!pControl->GetRect().Contains(point))
continue;
if (z_order)
*z_order = static_cast<int>(annot_index);
return pControl;
}
return nullptr;
}
CPDF_FormControl* CPDF_InteractiveForm::GetControlByDict(
const CPDF_Dictionary* pWidgetDict) const {
const auto it = m_ControlMap.find(pWidgetDict);
return it != m_ControlMap.end() ? it->second.get() : nullptr;
}
bool CPDF_InteractiveForm::NeedConstructAP() const {
return m_pFormDict && m_pFormDict->GetBooleanFor("NeedAppearances", false);
}
int CPDF_InteractiveForm::CountFieldsInCalculationOrder() {
if (!m_pFormDict)
return 0;
CPDF_Array* pArray = m_pFormDict->GetArrayFor("CO");
return pArray ? fxcrt::CollectionSize<int>(*pArray) : 0;
}
CPDF_FormField* CPDF_InteractiveForm::GetFieldInCalculationOrder(int index) {
if (!m_pFormDict || index < 0)
return nullptr;
CPDF_Array* pArray = m_pFormDict->GetArrayFor("CO");
if (!pArray)
return nullptr;
const CPDF_Dictionary* pElement =
ToDictionary(pArray->GetDirectObjectAt(index));
return pElement ? GetFieldByDict(pElement) : nullptr;
}
int CPDF_InteractiveForm::FindFieldInCalculationOrder(
const CPDF_FormField* pField) {
if (!m_pFormDict)
return -1;
CPDF_Array* pArray = m_pFormDict->GetArrayFor("CO");
if (!pArray)
return -1;
absl::optional<size_t> maybe_found = pArray->Find(pField->GetDict());
if (!maybe_found.has_value())
return -1;
return pdfium::base::checked_cast<int>(maybe_found.value());
}
RetainPtr<CPDF_Font> CPDF_InteractiveForm::GetFormFont(
ByteString csNameTag) const {
ByteString csAlias = PDF_NameDecode(csNameTag.AsStringView());
if (!m_pFormDict || csAlias.IsEmpty())
return nullptr;
CPDF_Dictionary* pDR = m_pFormDict->GetDictFor("DR");
if (!pDR)
return nullptr;
CPDF_Dictionary* pFonts = pDR->GetDictFor("Font");
if (!ValidateFontResourceDict(pFonts))
return nullptr;
CPDF_Dictionary* pElement = pFonts->GetDictFor(csAlias);
if (!ValidateDictType(pElement, "Font"))
return nullptr;
return CPDF_DocPageData::FromDocument(m_pDocument)->GetFont(pElement);
}
CPDF_DefaultAppearance CPDF_InteractiveForm::GetDefaultAppearance() const {
if (!m_pFormDict)
return CPDF_DefaultAppearance();
return CPDF_DefaultAppearance(m_pFormDict->GetStringFor("DA"));
}
int CPDF_InteractiveForm::GetFormAlignment() const {
return m_pFormDict ? m_pFormDict->GetIntegerFor("Q", 0) : 0;
}
void CPDF_InteractiveForm::ResetForm(pdfium::span<CPDF_FormField*> fields,
bool bIncludeOrExclude) {
CFieldTree::Node* pRoot = m_pFieldTree->GetRoot();
const size_t nCount = pRoot->CountFields();
for (size_t i = 0; i < nCount; ++i) {
CPDF_FormField* pField = pRoot->GetFieldAtIndex(i);
if (!pField)
continue;
if (bIncludeOrExclude == pdfium::Contains(fields, pField))
pField->ResetField();
}
if (m_pFormNotify)
m_pFormNotify->AfterFormReset(this);
}
void CPDF_InteractiveForm::ResetForm() {
ResetForm(/*fields=*/{}, /*bIncludeOrExclude=*/false);
}
const std::vector<UnownedPtr<CPDF_FormControl>>&
CPDF_InteractiveForm::GetControlsForField(const CPDF_FormField* pField) {
return m_ControlLists[pField];
}
void CPDF_InteractiveForm::LoadField(CPDF_Dictionary* pFieldDict, int nLevel) {
if (nLevel > nMaxRecursion)
return;
if (!pFieldDict)
return;
uint32_t dwParentObjNum = pFieldDict->GetObjNum();
CPDF_Array* pKids = pFieldDict->GetArrayFor(pdfium::form_fields::kKids);
if (!pKids) {
AddTerminalField(pFieldDict);
return;
}
const CPDF_Dictionary* pFirstKid = pKids->GetDictAt(0);
if (!pFirstKid)
return;
if (pFirstKid->KeyExist(pdfium::form_fields::kT) ||
pFirstKid->KeyExist(pdfium::form_fields::kKids)) {
for (size_t i = 0; i < pKids->size(); i++) {
RetainPtr<CPDF_Dictionary> pChildDict = pKids->GetMutableDictAt(i);
if (pChildDict) {
if (pChildDict->GetObjNum() != dwParentObjNum)
LoadField(pChildDict.Get(), nLevel + 1);
}
}
} else {
AddTerminalField(pFieldDict);
}
}
void CPDF_InteractiveForm::FixPageFields(CPDF_Page* pPage) {
CPDF_Array* pAnnots = pPage->GetDict()->GetArrayFor("Annots");
if (!pAnnots)
return;
for (size_t i = 0; i < pAnnots->size(); i++) {
RetainPtr<CPDF_Dictionary> pAnnot = pAnnots->GetMutableDictAt(i);
if (pAnnot && pAnnot->GetNameFor("Subtype") == "Widget")
LoadField(pAnnot.Get(), 0);
}
}
void CPDF_InteractiveForm::AddTerminalField(CPDF_Dictionary* pFieldDict) {
if (!pFieldDict->KeyExist(pdfium::form_fields::kFT)) {
// Key "FT" is required for terminal fields, it is also inheritable.
CPDF_Dictionary* pParentDict =
pFieldDict->GetDictFor(pdfium::form_fields::kParent);
if (!pParentDict || !pParentDict->KeyExist(pdfium::form_fields::kFT))
return;
}
CPDF_Dictionary* pDict = pFieldDict;
WideString csWName = CPDF_FormField::GetFullNameForDict(pFieldDict);
if (csWName.IsEmpty())
return;
CPDF_FormField* pField = nullptr;
pField = m_pFieldTree->GetField(csWName);
if (!pField) {
CPDF_Dictionary* pParent = pFieldDict;
if (!pFieldDict->KeyExist(pdfium::form_fields::kT) &&
pFieldDict->GetNameFor("Subtype") == "Widget") {
pParent = pFieldDict->GetDictFor(pdfium::form_fields::kParent);
if (!pParent)
pParent = pFieldDict;
}
if (pParent && pParent != pFieldDict &&
!pParent->KeyExist(pdfium::form_fields::kFT)) {
if (pFieldDict->KeyExist(pdfium::form_fields::kFT)) {
CPDF_Object* pFTValue =
pFieldDict->GetDirectObjectFor(pdfium::form_fields::kFT);
if (pFTValue)
pParent->SetFor(pdfium::form_fields::kFT, pFTValue->Clone());
}
if (pFieldDict->KeyExist(pdfium::form_fields::kFf)) {
CPDF_Object* pFfValue =
pFieldDict->GetDirectObjectFor(pdfium::form_fields::kFf);
if (pFfValue)
pParent->SetFor(pdfium::form_fields::kFf, pFfValue->Clone());
}
}
auto newField = std::make_unique<CPDF_FormField>(this, pParent);
pField = newField.get();
CPDF_Object* pTObj = pDict->GetObjectFor(pdfium::form_fields::kT);
if (ToReference(pTObj)) {
RetainPtr<CPDF_Object> pClone = pTObj->CloneDirectObject();
if (pClone)
pDict->SetFor(pdfium::form_fields::kT, std::move(pClone));
else
pDict->SetNewFor<CPDF_Name>(pdfium::form_fields::kT, ByteString());
}
if (!m_pFieldTree->SetField(csWName, std::move(newField)))
return;
}
CPDF_Array* pKids = pFieldDict->GetArrayFor(pdfium::form_fields::kKids);
if (pKids) {
for (size_t i = 0; i < pKids->size(); i++) {
RetainPtr<CPDF_Dictionary> pKid = pKids->GetMutableDictAt(i);
if (pKid && pKid->GetNameFor("Subtype") == "Widget")
AddControl(pField, pKid.Get());
}
} else {
if (pFieldDict->GetNameFor("Subtype") == "Widget")
AddControl(pField, pFieldDict);
}
}
CPDF_FormControl* CPDF_InteractiveForm::AddControl(
CPDF_FormField* pField,
CPDF_Dictionary* pWidgetDict) {
DCHECK(pWidgetDict);
const auto it = m_ControlMap.find(pWidgetDict);
if (it != m_ControlMap.end())
return it->second.get();
auto pNew = std::make_unique<CPDF_FormControl>(pField, pWidgetDict);
CPDF_FormControl* pControl = pNew.get();
m_ControlMap[pWidgetDict] = std::move(pNew);
m_ControlLists[pField].emplace_back(pControl);
return pControl;
}
bool CPDF_InteractiveForm::CheckRequiredFields(
const std::vector<CPDF_FormField*>* fields,
bool bIncludeOrExclude) const {
CFieldTree::Node* pRoot = m_pFieldTree->GetRoot();
const size_t nCount = pRoot->CountFields();
for (size_t i = 0; i < nCount; ++i) {
CPDF_FormField* pField = pRoot->GetFieldAtIndex(i);
if (!pField)
continue;
int32_t iType = pField->GetType();
if (iType == CPDF_FormField::kPushButton ||
iType == CPDF_FormField::kCheckBox ||
iType == CPDF_FormField::kListBox) {
continue;
}
if (pField->IsNoExport())
continue;
bool bFind = true;
if (fields)
bFind = pdfium::Contains(*fields, pField);
if (bIncludeOrExclude == bFind) {
const CPDF_Dictionary* pFieldDict = pField->GetDict();
if (pField->IsRequired() &&
pFieldDict->GetStringFor(pdfium::form_fields::kV).IsEmpty()) {
return false;
}
}
}
return true;
}
std::unique_ptr<CFDF_Document> CPDF_InteractiveForm::ExportToFDF(
const WideString& pdf_path) const {
std::vector<CPDF_FormField*> fields;
CFieldTree::Node* pRoot = m_pFieldTree->GetRoot();
const size_t nCount = pRoot->CountFields();
for (size_t i = 0; i < nCount; ++i)
fields.push_back(pRoot->GetFieldAtIndex(i));
return ExportToFDF(pdf_path, fields, true);
}
std::unique_ptr<CFDF_Document> CPDF_InteractiveForm::ExportToFDF(
const WideString& pdf_path,
const std::vector<CPDF_FormField*>& fields,
bool bIncludeOrExclude) const {
std::unique_ptr<CFDF_Document> pDoc = CFDF_Document::CreateNewDoc();
if (!pDoc)
return nullptr;
CPDF_Dictionary* pMainDict = pDoc->GetRoot()->GetDictFor("FDF");
if (!pdf_path.IsEmpty()) {
auto pNewDict = pDoc->New<CPDF_Dictionary>();
pNewDict->SetNewFor<CPDF_Name>("Type", "Filespec");
CPDF_FileSpec filespec(pNewDict.Get());
filespec.SetFileName(pdf_path);
pMainDict->SetFor("F", pNewDict);
}
CPDF_Array* pFields = pMainDict->SetNewFor<CPDF_Array>("Fields");
CFieldTree::Node* pRoot = m_pFieldTree->GetRoot();
const size_t nCount = pRoot->CountFields();
for (size_t i = 0; i < nCount; ++i) {
CPDF_FormField* pField = pRoot->GetFieldAtIndex(i);
if (!pField || pField->GetType() == CPDF_FormField::kPushButton)
continue;
uint32_t dwFlags = pField->GetFieldFlags();
if (dwFlags & pdfium::form_flags::kNoExport)
continue;
if (bIncludeOrExclude != pdfium::Contains(fields, pField))
continue;
if ((dwFlags & pdfium::form_flags::kRequired) != 0 &&
pField->GetDict()->GetStringFor(pdfium::form_fields::kV).IsEmpty()) {
continue;
}
WideString fullname =
CPDF_FormField::GetFullNameForDict(pField->GetFieldDict());
auto pFieldDict = pDoc->New<CPDF_Dictionary>();
pFieldDict->SetNewFor<CPDF_String>(pdfium::form_fields::kT,
fullname.AsStringView());
if (pField->GetType() == CPDF_FormField::kCheckBox ||
pField->GetType() == CPDF_FormField::kRadioButton) {
WideString csExport = pField->GetCheckValue(false);
ByteString csBExport = PDF_EncodeText(csExport.AsStringView());
CPDF_Object* pOpt =
CPDF_FormField::GetFieldAttr(pField->GetDict(), "Opt");
if (pOpt) {
pFieldDict->SetNewFor<CPDF_String>(pdfium::form_fields::kV, csBExport,
false);
} else {
pFieldDict->SetNewFor<CPDF_Name>(pdfium::form_fields::kV, csBExport);
}
} else {
CPDF_Object* pV = CPDF_FormField::GetFieldAttr(pField->GetDict(),
pdfium::form_fields::kV);
if (pV)
pFieldDict->SetFor(pdfium::form_fields::kV, pV->CloneDirectObject());
}
pFields->Append(pFieldDict);
}
return pDoc;
}
void CPDF_InteractiveForm::SetNotifierIface(NotifierIface* pNotify) {
m_pFormNotify = pNotify;
}