// 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/fpdfdoc/cpdf_interactiveform.h" #include #include #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_stream.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/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(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++; } RetainPtr 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 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 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(const CPDF_Dictionary* pFormDict, const CPDF_Font* pFont, ByteString* csNameTag) { RetainPtr pDR = pFormDict->GetDictFor("DR"); if (!pDR) return false; RetainPtr pFonts = pDR->GetDictFor("Font"); // TODO(tsepez): this eventually locks the dict, pass locker instead. if (!ValidateFontResourceDict(pFonts.Get())) return false; CPDF_DictionaryLocker locker(std::move(pFonts)); for (const auto& it : locker) { const ByteString& csKey = it.first; RetainPtr pElement = ToDictionary(it.second->GetDirect()); if (!ValidateDictType(pElement.Get(), "Font")) continue; if (pFont->FontDictIs(pElement)) { *csNameTag = csKey; return true; } } return false; } bool FindFontFromDoc(const CPDF_Dictionary* pFormDict, CPDF_Document* pDocument, ByteString csFontName, RetainPtr& pFont, ByteString* csNameTag) { if (csFontName.IsEmpty()) return false; RetainPtr pDR = pFormDict->GetDictFor("DR"); if (!pDR) return false; RetainPtr pFonts = pDR->GetDictFor("Font"); if (!ValidateFontResourceDict(pFonts.Get())) return false; csFontName.Remove(' '); CPDF_DictionaryLocker locker(pFonts); for (const auto& it : locker) { const ByteString& csKey = it.first; RetainPtr pElement = ToDictionary(it.second->GetMutableDirect()); if (!ValidateDictType(pElement.Get(), "Font")) continue; auto* pData = CPDF_DocPageData::FromDocument(pDocument); pFont = pData->GetFont(std::move(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& pFont, ByteString* csNameTag) { DCHECK(pFormDict); DCHECK(pFont); ByteString csTag; if (FindFont(pFormDict, pFont.Get(), &csTag)) { *csNameTag = std::move(csTag); return; } RetainPtr pDR = pFormDict->GetOrCreateDictFor("DR"); RetainPtr pFonts = pDR->GetOrCreateDictFor("Font"); if (csNameTag->IsEmpty()) *csNameTag = pFont->GetBaseFontName(); csNameTag->Remove(' '); *csNameTag = GenerateNewFontResourceName(pDR.Get(), *csNameTag); pFonts->SetNewFor(*csNameTag, pDocument, pFont->GetFontDictObjNum()); } FX_Charset GetNativeCharSet() { return FX_GetCharsetFromCodePage(FX_GetACP()); } RetainPtr InitDict(CPDF_Document* pDocument) { auto pFormDict = pDocument->NewIndirect(); pDocument->GetMutableRoot()->SetNewFor( "AcroForm", pDocument, pFormDict->GetObjNum()); ByteString csBaseName; FX_Charset charSet = GetNativeCharSet(); RetainPtr pFont = AddStandardFont(pDocument); if (pFont) { AddFont(pFormDict.Get(), 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.Get(), pDocument, pFont, &csBaseName); } } } ByteString csDA; if (pFont) csDA = "/" + PDF_NameEncode(csBaseName) + " 0 Tf "; csDA += "0 g"; pFormDict->SetNewFor("DA", csDA, /*bHex=*/false); return pFormDict; } RetainPtr GetNativeFont(const CPDF_Dictionary* pFormDict, CPDF_Document* pDocument, FX_Charset charSet, ByteString* csNameTag) { RetainPtr pDR = pFormDict->GetDictFor("DR"); if (!pDR) return nullptr; RetainPtr pFonts = pDR->GetDictFor("Font"); if (!ValidateFontResourceDict(pFonts.Get())) return nullptr; CPDF_DictionaryLocker locker(pFonts); for (const auto& it : locker) { const ByteString& csKey = it.first; RetainPtr pElement = ToDictionary(it.second->GetMutableDirect()); if (!ValidateDictType(pElement.Get(), "Font")) continue; auto* pData = CPDF_DocPageData::FromDocument(pDocument); RetainPtr pFind = pData->GetFont(std::move(pElement)); if (!pFind) continue; auto maybe_charset = pFind->GetSubstFontCharset(); if (maybe_charset.has_value() && maybe_charset.value() == 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 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 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> m_Children; WideString m_ShortName; std::unique_ptr m_pField; const int m_level; }; CFieldTree(); ~CFieldTree(); bool SetField(const WideString& full_name, std::unique_ptr 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 m_pRoot; }; CFieldTree::CFieldTree() : m_pRoot(std::make_unique()) {} 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(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 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()) { RetainPtr pRoot = m_pDocument->GetMutableRoot(); if (!pRoot) return; m_pFormDict = pRoot->GetMutableDictFor("AcroForm"); if (!m_pFormDict) return; RetainPtr pFields = m_pFormDict->GetMutableArrayFor("Fields"); if (!pFields) return; for (size_t i = 0; i < pFields->size(); ++i) LoadField(pFields->GetMutableDictAt(i), 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_InteractiveForm::AddNativeInteractiveFormFont( CPDF_Document* pDocument, ByteString* csNameTag) { DCHECK(pDocument); DCHECK(csNameTag); RetainPtr pFormDict = pDocument->GetMutableRoot()->GetMutableDictFor("AcroForm"); if (!pFormDict) pFormDict = InitDict(pDocument); FX_Charset charSet = GetNativeCharSet(); ByteString csTemp; RetainPtr pFont = GetNativeFont(pFormDict.Get(), pDocument, charSet, &csTemp); if (pFont) { *csNameTag = std::move(csTemp); return pFont; } ByteString csFontName = GetNativeFontName(charSet, nullptr); if (FindFontFromDoc(pFormDict.Get(), pDocument, csFontName, pFont, csNameTag)) return pFont; pFont = AddNativeFont(charSet, pDocument); if (!pFont) return nullptr; AddFont(pFormDict.Get(), 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 { RetainPtr pAnnotList = pPage->GetAnnotsArray(); if (!pAnnotList) return nullptr; for (size_t i = pAnnotList->size(); i > 0; --i) { size_t annot_index = i - 1; RetainPtr pAnnot = pAnnotList->GetDictAt(annot_index); if (!pAnnot) continue; const auto it = m_ControlMap.find(pAnnot.Get()); 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(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; RetainPtr pArray = m_pFormDict->GetArrayFor("CO"); return pArray ? fxcrt::CollectionSize(*pArray) : 0; } CPDF_FormField* CPDF_InteractiveForm::GetFieldInCalculationOrder(int index) { if (!m_pFormDict || index < 0) return nullptr; RetainPtr pArray = m_pFormDict->GetArrayFor("CO"); if (!pArray) return nullptr; RetainPtr pElement = ToDictionary(pArray->GetDirectObjectAt(index)); return pElement ? GetFieldByDict(pElement.Get()) : nullptr; } int CPDF_InteractiveForm::FindFieldInCalculationOrder( const CPDF_FormField* pField) { if (!m_pFormDict) return -1; RetainPtr pArray = m_pFormDict->GetArrayFor("CO"); if (!pArray) return -1; absl::optional maybe_found = pArray->Find(pField->GetFieldDict()); if (!maybe_found.has_value()) return -1; return pdfium::base::checked_cast(maybe_found.value()); } RetainPtr CPDF_InteractiveForm::GetFormFont( ByteString csNameTag) const { ByteString csAlias = PDF_NameDecode(csNameTag.AsStringView()); if (!m_pFormDict || csAlias.IsEmpty()) return nullptr; RetainPtr pDR = m_pFormDict->GetMutableDictFor("DR"); if (!pDR) return nullptr; RetainPtr pFonts = pDR->GetMutableDictFor("Font"); if (!ValidateFontResourceDict(pFonts.Get())) return nullptr; RetainPtr pElement = pFonts->GetMutableDictFor(csAlias); if (!ValidateDictType(pElement.Get(), "Font")) return nullptr; return GetFontForElement(std::move(pElement)); } RetainPtr CPDF_InteractiveForm::GetFontForElement( RetainPtr pElement) const { auto* pData = CPDF_DocPageData::FromDocument(m_pDocument); return pData->GetFont(std::move(pElement)); } CPDF_DefaultAppearance CPDF_InteractiveForm::GetDefaultAppearance() const { if (!m_pFormDict) return CPDF_DefaultAppearance(); return CPDF_DefaultAppearance(m_pFormDict->GetByteStringFor("DA")); } int CPDF_InteractiveForm::GetFormAlignment() const { return m_pFormDict ? m_pFormDict->GetIntegerFor("Q", 0) : 0; } void CPDF_InteractiveForm::ResetForm(pdfium::span 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>& CPDF_InteractiveForm::GetControlsForField(const CPDF_FormField* pField) { return m_ControlLists[pdfium::WrapUnowned(pField)]; } void CPDF_InteractiveForm::LoadField(RetainPtr pFieldDict, int nLevel) { if (nLevel > nMaxRecursion) return; if (!pFieldDict) return; uint32_t dwParentObjNum = pFieldDict->GetObjNum(); RetainPtr pKids = pFieldDict->GetMutableArrayFor(pdfium::form_fields::kKids); if (!pKids) { AddTerminalField(std::move(pFieldDict)); return; } RetainPtr pFirstKid = pKids->GetDictAt(0); if (!pFirstKid) return; if (!pFirstKid->KeyExist(pdfium::form_fields::kT) && !pFirstKid->KeyExist(pdfium::form_fields::kKids)) { AddTerminalField(std::move(pFieldDict)); return; } for (size_t i = 0; i < pKids->size(); i++) { RetainPtr pChildDict = pKids->GetMutableDictAt(i); if (pChildDict && pChildDict->GetObjNum() != dwParentObjNum) LoadField(std::move(pChildDict), nLevel + 1); } } void CPDF_InteractiveForm::FixPageFields(CPDF_Page* pPage) { RetainPtr pAnnots = pPage->GetMutableAnnotsArray(); if (!pAnnots) return; for (size_t i = 0; i < pAnnots->size(); i++) { RetainPtr pAnnot = pAnnots->GetMutableDictAt(i); if (pAnnot && pAnnot->GetNameFor("Subtype") == "Widget") LoadField(std::move(pAnnot), 0); } } void CPDF_InteractiveForm::AddTerminalField( RetainPtr pFieldDict) { if (!pFieldDict->KeyExist(pdfium::form_fields::kFT)) { // Key "FT" is required for terminal fields, it is also inheritable. RetainPtr pParentDict = pFieldDict->GetDictFor(pdfium::form_fields::kParent); if (!pParentDict || !pParentDict->KeyExist(pdfium::form_fields::kFT)) return; } WideString csWName = CPDF_FormField::GetFullNameForDict(pFieldDict.Get()); if (csWName.IsEmpty()) return; CPDF_FormField* pField = nullptr; pField = m_pFieldTree->GetField(csWName); if (!pField) { RetainPtr pParent(pFieldDict); if (!pFieldDict->KeyExist(pdfium::form_fields::kT) && pFieldDict->GetNameFor("Subtype") == "Widget") { pParent = pFieldDict->GetMutableDictFor(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)) { RetainPtr pFTValue = pFieldDict->GetDirectObjectFor(pdfium::form_fields::kFT); if (pFTValue) pParent->SetFor(pdfium::form_fields::kFT, pFTValue->Clone()); } if (pFieldDict->KeyExist(pdfium::form_fields::kFf)) { RetainPtr pFfValue = pFieldDict->GetDirectObjectFor(pdfium::form_fields::kFf); if (pFfValue) pParent->SetFor(pdfium::form_fields::kFf, pFfValue->Clone()); } } auto newField = std::make_unique(this, std::move(pParent)); pField = newField.get(); RetainPtr pTObj = pFieldDict->GetObjectFor(pdfium::form_fields::kT); if (ToReference(pTObj)) { RetainPtr pClone = pTObj->CloneDirectObject(); if (pClone) pFieldDict->SetFor(pdfium::form_fields::kT, std::move(pClone)); else pFieldDict->SetNewFor(pdfium::form_fields::kT, ByteString()); } if (!m_pFieldTree->SetField(csWName, std::move(newField))) return; } RetainPtr pKids = pFieldDict->GetMutableArrayFor(pdfium::form_fields::kKids); if (!pKids) { if (pFieldDict->GetNameFor("Subtype") == "Widget") AddControl(pField, std::move(pFieldDict)); return; } for (size_t i = 0; i < pKids->size(); i++) { RetainPtr pKid = pKids->GetMutableDictAt(i); if (pKid && pKid->GetNameFor("Subtype") == "Widget") AddControl(pField, std::move(pKid)); } } CPDF_FormControl* CPDF_InteractiveForm::AddControl( CPDF_FormField* pField, RetainPtr pWidgetDict) { DCHECK(pWidgetDict); const auto it = m_ControlMap.find(pWidgetDict.Get()); if (it != m_ControlMap.end()) return it->second.get(); auto pNew = std::make_unique(pField, pWidgetDict, this); CPDF_FormControl* pControl = pNew.get(); m_ControlMap[pWidgetDict] = std::move(pNew); m_ControlLists[pdfium::WrapUnowned(pField)].emplace_back(pControl); return pControl; } bool CPDF_InteractiveForm::CheckRequiredFields( const std::vector* 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) { RetainPtr pFieldDict = pField->GetFieldDict(); if (pField->IsRequired() && pFieldDict->GetByteStringFor(pdfium::form_fields::kV).IsEmpty()) { return false; } } } return true; } std::unique_ptr CPDF_InteractiveForm::ExportToFDF( const WideString& pdf_path) const { std::vector 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 CPDF_InteractiveForm::ExportToFDF( const WideString& pdf_path, const std::vector& fields, bool bIncludeOrExclude) const { std::unique_ptr pDoc = CFDF_Document::CreateNewDoc(); if (!pDoc) return nullptr; RetainPtr pMainDict = pDoc->GetMutableRoot()->GetMutableDictFor("FDF"); if (!pdf_path.IsEmpty()) { auto pNewDict = pDoc->New(); pNewDict->SetNewFor("Type", "Filespec"); WideString wsStr = CPDF_FileSpec::EncodeFileName(pdf_path); pNewDict->SetNewFor(pdfium::stream::kF, wsStr.ToDefANSI(), false); pNewDict->SetNewFor("UF", wsStr.AsStringView()); pMainDict->SetFor("F", pNewDict); } auto pFields = pMainDict->SetNewFor("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->GetFieldDict() ->GetByteStringFor(pdfium::form_fields::kV) .IsEmpty()) { continue; } WideString fullname = CPDF_FormField::GetFullNameForDict(pField->GetFieldDict()); auto pFieldDict = pDoc->New(); pFieldDict->SetNewFor(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()); RetainPtr pOpt = pField->GetFieldAttr("Opt"); if (pOpt) { pFieldDict->SetNewFor(pdfium::form_fields::kV, csBExport, false); } else { pFieldDict->SetNewFor(pdfium::form_fields::kV, csBExport); } } else { RetainPtr pV = pField->GetFieldAttr(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; } bool CPDF_InteractiveForm::NotifyBeforeValueChange(CPDF_FormField* pField, const WideString& csValue) { return !m_pFormNotify || m_pFormNotify->BeforeValueChange(pField, csValue); } void CPDF_InteractiveForm::NotifyAfterValueChange(CPDF_FormField* pField) { if (m_pFormNotify) m_pFormNotify->AfterValueChange(pField); } bool CPDF_InteractiveForm::NotifyBeforeSelectionChange( CPDF_FormField* pField, const WideString& csValue) { return !m_pFormNotify || m_pFormNotify->BeforeSelectionChange(pField, csValue); } void CPDF_InteractiveForm::NotifyAfterSelectionChange(CPDF_FormField* pField) { if (m_pFormNotify) m_pFormNotify->AfterSelectionChange(pField); } void CPDF_InteractiveForm::NotifyAfterCheckedStatusChange( CPDF_FormField* pField) { if (m_pFormNotify) m_pFormNotify->AfterCheckedStatusChange(pField); }