1 // Copyright 2016 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "core/fpdfdoc/cpdf_formcontrol.h"
8
9 #include <array>
10 #include <iterator>
11 #include <utility>
12
13 #include "constants/form_fields.h"
14 #include "core/fpdfapi/font/cpdf_font.h"
15 #include "core/fpdfapi/page/cpdf_docpagedata.h"
16 #include "core/fpdfapi/parser/cpdf_array.h"
17 #include "core/fpdfapi/parser/cpdf_dictionary.h"
18 #include "core/fpdfapi/parser/cpdf_name.h"
19 #include "core/fpdfapi/parser/cpdf_stream.h"
20 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
21 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
22 #include "core/fpdfdoc/cpdf_interactiveform.h"
23 #include "core/fxcrt/check.h"
24
25 namespace {
26
27 constexpr std::array<char, 5> kHighlightModes = {{'N', 'I', 'O', 'P', 'T'}};
28
29 // Order of |kHighlightModes| must match order of HighlightingMode enum.
30 static_assert(kHighlightModes[CPDF_FormControl::kNone] == 'N',
31 "HighlightingMode mismatch");
32 static_assert(kHighlightModes[CPDF_FormControl::kInvert] == 'I',
33 "HighlightingMode mismatch");
34 static_assert(kHighlightModes[CPDF_FormControl::kOutline] == 'O',
35 "HighlightingMode mismatch");
36 static_assert(kHighlightModes[CPDF_FormControl::kPush] == 'P',
37 "HighlightingMode mismatch");
38 static_assert(kHighlightModes[CPDF_FormControl::kToggle] == 'T',
39 "HighlightingMode mismatch");
40
41 } // namespace
42
CPDF_FormControl(CPDF_FormField * pField,RetainPtr<CPDF_Dictionary> pWidgetDict,CPDF_InteractiveForm * pForm)43 CPDF_FormControl::CPDF_FormControl(CPDF_FormField* pField,
44 RetainPtr<CPDF_Dictionary> pWidgetDict,
45 CPDF_InteractiveForm* pForm)
46 : m_pField(pField), m_pWidgetDict(std::move(pWidgetDict)), m_pForm(pForm) {
47 DCHECK(m_pWidgetDict);
48 }
49
50 CPDF_FormControl::~CPDF_FormControl() = default;
51
GetRect() const52 CFX_FloatRect CPDF_FormControl::GetRect() const {
53 return m_pWidgetDict->GetRectFor("Rect");
54 }
55
GetOnStateName() const56 ByteString CPDF_FormControl::GetOnStateName() const {
57 DCHECK(GetType() == CPDF_FormField::kCheckBox ||
58 GetType() == CPDF_FormField::kRadioButton);
59 RetainPtr<const CPDF_Dictionary> pAP = m_pWidgetDict->GetDictFor("AP");
60 if (!pAP)
61 return ByteString();
62
63 RetainPtr<const CPDF_Dictionary> pN = pAP->GetDictFor("N");
64 if (!pN)
65 return ByteString();
66
67 CPDF_DictionaryLocker locker(pN);
68 for (const auto& it : locker) {
69 if (it.first != "Off")
70 return it.first;
71 }
72 return ByteString();
73 }
74
GetCheckedAPState() const75 ByteString CPDF_FormControl::GetCheckedAPState() const {
76 DCHECK(GetType() == CPDF_FormField::kCheckBox ||
77 GetType() == CPDF_FormField::kRadioButton);
78 ByteString csOn = GetOnStateName();
79 if (ToArray(m_pField->GetFieldAttr("Opt")))
80 csOn = ByteString::FormatInteger(m_pField->GetControlIndex(this));
81 if (csOn.IsEmpty())
82 csOn = "Yes";
83 return csOn;
84 }
85
GetExportValue() const86 WideString CPDF_FormControl::GetExportValue() const {
87 DCHECK(GetType() == CPDF_FormField::kCheckBox ||
88 GetType() == CPDF_FormField::kRadioButton);
89 ByteString csOn = GetOnStateName();
90 RetainPtr<const CPDF_Array> pArray = ToArray(m_pField->GetFieldAttr("Opt"));
91 if (pArray)
92 csOn = pArray->GetByteStringAt(m_pField->GetControlIndex(this));
93 if (csOn.IsEmpty())
94 csOn = "Yes";
95 return PDF_DecodeText(csOn.unsigned_span());
96 }
97
IsChecked() const98 bool CPDF_FormControl::IsChecked() const {
99 DCHECK(GetType() == CPDF_FormField::kCheckBox ||
100 GetType() == CPDF_FormField::kRadioButton);
101 ByteString csOn = GetOnStateName();
102 ByteString csAS = m_pWidgetDict->GetByteStringFor("AS");
103 return csAS == csOn;
104 }
105
IsDefaultChecked() const106 bool CPDF_FormControl::IsDefaultChecked() const {
107 DCHECK(GetType() == CPDF_FormField::kCheckBox ||
108 GetType() == CPDF_FormField::kRadioButton);
109 RetainPtr<const CPDF_Object> pDV = m_pField->GetFieldAttr("DV");
110 if (!pDV)
111 return false;
112
113 ByteString csDV = pDV->GetString();
114 ByteString csOn = GetOnStateName();
115 return (csDV == csOn);
116 }
117
CheckControl(bool bChecked)118 void CPDF_FormControl::CheckControl(bool bChecked) {
119 DCHECK(GetType() == CPDF_FormField::kCheckBox ||
120 GetType() == CPDF_FormField::kRadioButton);
121 ByteString csOldAS = m_pWidgetDict->GetByteStringFor("AS", "Off");
122 ByteString csAS = "Off";
123 if (bChecked)
124 csAS = GetOnStateName();
125 if (csOldAS == csAS)
126 return;
127 m_pWidgetDict->SetNewFor<CPDF_Name>("AS", csAS);
128 }
129
GetHighlightingMode() const130 CPDF_FormControl::HighlightingMode CPDF_FormControl::GetHighlightingMode()
131 const {
132 ByteString csH = m_pWidgetDict->GetByteStringFor("H", "I");
133 for (size_t i = 0; i < std::size(kHighlightModes); ++i) {
134 // TODO(tsepez): disambiguate string ctors.
135 if (csH == ByteStringView(kHighlightModes[i])) {
136 return static_cast<HighlightingMode>(i);
137 }
138 }
139 return kInvert;
140 }
141
GetMK() const142 CPDF_ApSettings CPDF_FormControl::GetMK() const {
143 return CPDF_ApSettings(m_pWidgetDict->GetMutableDictFor("MK"));
144 }
145
HasMKEntry(const ByteString & csEntry) const146 bool CPDF_FormControl::HasMKEntry(const ByteString& csEntry) const {
147 return GetMK().HasMKEntry(csEntry);
148 }
149
GetRotation() const150 int CPDF_FormControl::GetRotation() const {
151 return GetMK().GetRotation();
152 }
153
GetColorARGB(const ByteString & csEntry)154 CFX_Color::TypeAndARGB CPDF_FormControl::GetColorARGB(
155 const ByteString& csEntry) {
156 return GetMK().GetColorARGB(csEntry);
157 }
158
GetOriginalColorComponent(int index,const ByteString & csEntry)159 float CPDF_FormControl::GetOriginalColorComponent(int index,
160 const ByteString& csEntry) {
161 return GetMK().GetOriginalColorComponent(index, csEntry);
162 }
163
GetOriginalColor(const ByteString & csEntry)164 CFX_Color CPDF_FormControl::GetOriginalColor(const ByteString& csEntry) {
165 return GetMK().GetOriginalColor(csEntry);
166 }
167
GetCaption(const ByteString & csEntry) const168 WideString CPDF_FormControl::GetCaption(const ByteString& csEntry) const {
169 return GetMK().GetCaption(csEntry);
170 }
171
GetIcon(const ByteString & csEntry)172 RetainPtr<CPDF_Stream> CPDF_FormControl::GetIcon(const ByteString& csEntry) {
173 return GetMK().GetIcon(csEntry);
174 }
175
GetIconFit() const176 CPDF_IconFit CPDF_FormControl::GetIconFit() const {
177 return GetMK().GetIconFit();
178 }
179
GetTextPosition() const180 int CPDF_FormControl::GetTextPosition() const {
181 return GetMK().GetTextPosition();
182 }
183
GetDefaultAppearance() const184 CPDF_DefaultAppearance CPDF_FormControl::GetDefaultAppearance() const {
185 if (m_pWidgetDict->KeyExist(pdfium::form_fields::kDA)) {
186 return CPDF_DefaultAppearance(
187 m_pWidgetDict->GetByteStringFor(pdfium::form_fields::kDA));
188 }
189 RetainPtr<const CPDF_Object> pObj =
190 m_pField->GetFieldAttr(pdfium::form_fields::kDA);
191 if (pObj)
192 return CPDF_DefaultAppearance(pObj->GetString());
193
194 return m_pForm->GetDefaultAppearance();
195 }
196
GetDefaultControlFontName() const197 std::optional<WideString> CPDF_FormControl::GetDefaultControlFontName() const {
198 RetainPtr<CPDF_Font> pFont = GetDefaultControlFont();
199 if (!pFont)
200 return std::nullopt;
201
202 return WideString::FromDefANSI(pFont->GetBaseFontName().AsStringView());
203 }
204
GetDefaultControlFont() const205 RetainPtr<CPDF_Font> CPDF_FormControl::GetDefaultControlFont() const {
206 float fFontSize;
207 CPDF_DefaultAppearance cDA = GetDefaultAppearance();
208 std::optional<ByteString> csFontNameTag = cDA.GetFont(&fFontSize);
209 if (!csFontNameTag.has_value() || csFontNameTag->IsEmpty())
210 return nullptr;
211
212 RetainPtr<CPDF_Dictionary> pDRDict = ToDictionary(
213 CPDF_FormField::GetMutableFieldAttrForDict(m_pWidgetDict.Get(), "DR"));
214 if (pDRDict) {
215 RetainPtr<CPDF_Dictionary> pFonts = pDRDict->GetMutableDictFor("Font");
216 if (ValidateFontResourceDict(pFonts.Get())) {
217 RetainPtr<CPDF_Dictionary> pElement =
218 pFonts->GetMutableDictFor(csFontNameTag.value());
219 if (pElement) {
220 RetainPtr<CPDF_Font> pFont =
221 m_pForm->GetFontForElement(std::move(pElement));
222 if (pFont)
223 return pFont;
224 }
225 }
226 }
227 RetainPtr<CPDF_Font> pFormFont = m_pForm->GetFormFont(csFontNameTag.value());
228 if (pFormFont)
229 return pFormFont;
230
231 RetainPtr<CPDF_Dictionary> pPageDict = m_pWidgetDict->GetMutableDictFor("P");
232 RetainPtr<CPDF_Dictionary> pDict = ToDictionary(
233 CPDF_FormField::GetMutableFieldAttrForDict(pPageDict.Get(), "Resources"));
234 if (!pDict)
235 return nullptr;
236
237 RetainPtr<CPDF_Dictionary> pFonts = pDict->GetMutableDictFor("Font");
238 if (!ValidateFontResourceDict(pFonts.Get()))
239 return nullptr;
240
241 RetainPtr<CPDF_Dictionary> pElement =
242 pFonts->GetMutableDictFor(csFontNameTag.value());
243 if (!pElement)
244 return nullptr;
245
246 return m_pForm->GetFontForElement(std::move(pElement));
247 }
248
GetControlAlignment() const249 int CPDF_FormControl::GetControlAlignment() const {
250 if (m_pWidgetDict->KeyExist(pdfium::form_fields::kQ))
251 return m_pWidgetDict->GetIntegerFor(pdfium::form_fields::kQ, 0);
252
253 RetainPtr<const CPDF_Object> pObj =
254 m_pField->GetFieldAttr(pdfium::form_fields::kQ);
255 if (pObj)
256 return pObj->GetInteger();
257
258 return m_pForm->GetFormAlignment();
259 }
260