1 // Copyright 2016 PDFium Authors. All rights reserved.
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 <algorithm>
10
11 #include "core/fpdfapi/page/cpdf_form.h"
12 #include "core/fpdfapi/parser/cpdf_array.h"
13 #include "core/fpdfapi/parser/cpdf_document.h"
14 #include "core/fpdfapi/parser/cpdf_name.h"
15 #include "core/fpdfapi/parser/cpdf_stream.h"
16 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
17 #include "core/fpdfapi/render/cpdf_rendercontext.h"
18 #include "core/fpdfdoc/cpdf_interform.h"
19 #include "core/fxge/cfx_renderdevice.h"
20
21 namespace {
22
23 const FX_CHAR* const g_sHighlightingMode[] = {
24 // Must match order of HighlightingMode enum.
25 "N", "I", "O", "P", "T"};
26
27 } // namespace
28
CPDF_FormControl(CPDF_FormField * pField,CPDF_Dictionary * pWidgetDict)29 CPDF_FormControl::CPDF_FormControl(CPDF_FormField* pField,
30 CPDF_Dictionary* pWidgetDict)
31 : m_pField(pField),
32 m_pWidgetDict(pWidgetDict),
33 m_pForm(m_pField->m_pForm) {}
34
GetOnStateName() const35 CFX_ByteString CPDF_FormControl::GetOnStateName() const {
36 ASSERT(GetType() == CPDF_FormField::CheckBox ||
37 GetType() == CPDF_FormField::RadioButton);
38 CFX_ByteString csOn;
39 CPDF_Dictionary* pAP = m_pWidgetDict->GetDictFor("AP");
40 if (!pAP)
41 return csOn;
42
43 CPDF_Dictionary* pN = pAP->GetDictFor("N");
44 if (!pN)
45 return csOn;
46
47 for (const auto& it : *pN) {
48 if (it.first != "Off")
49 return it.first;
50 }
51 return CFX_ByteString();
52 }
53
SetOnStateName(const CFX_ByteString & csOn)54 void CPDF_FormControl::SetOnStateName(const CFX_ByteString& csOn) {
55 ASSERT(GetType() == CPDF_FormField::CheckBox ||
56 GetType() == CPDF_FormField::RadioButton);
57 CFX_ByteString csValue = csOn;
58 if (csValue.IsEmpty())
59 csValue = "Yes";
60 else if (csValue == "Off")
61 csValue = "Yes";
62
63 CFX_ByteString csAS = m_pWidgetDict->GetStringFor("AS", "Off");
64 if (csAS != "Off")
65 m_pWidgetDict->SetNewFor<CPDF_Name>("AS", csValue);
66
67 CPDF_Dictionary* pAP = m_pWidgetDict->GetDictFor("AP");
68 if (!pAP)
69 return;
70
71 for (const auto& it : *pAP) {
72 CPDF_Object* pObj1 = it.second.get();
73 if (!pObj1)
74 continue;
75
76 CPDF_Object* pObjDirect1 = pObj1->GetDirect();
77 CPDF_Dictionary* pSubDict = pObjDirect1->AsDictionary();
78 if (!pSubDict)
79 continue;
80
81 auto subdict_it = pSubDict->begin();
82 while (subdict_it != pSubDict->end()) {
83 const CFX_ByteString& csKey2 = subdict_it->first;
84 CPDF_Object* pObj2 = subdict_it->second.get();
85 ++subdict_it;
86 if (!pObj2)
87 continue;
88 if (csKey2 != "Off") {
89 pSubDict->ReplaceKey(csKey2, csValue);
90 break;
91 }
92 }
93 }
94 }
95
GetCheckedAPState()96 CFX_ByteString CPDF_FormControl::GetCheckedAPState() {
97 ASSERT(GetType() == CPDF_FormField::CheckBox ||
98 GetType() == CPDF_FormField::RadioButton);
99 CFX_ByteString csOn = GetOnStateName();
100 if (GetType() == CPDF_FormField::RadioButton ||
101 GetType() == CPDF_FormField::CheckBox) {
102 if (ToArray(FPDF_GetFieldAttr(m_pField->m_pDict, "Opt"))) {
103 int iIndex = m_pField->GetControlIndex(this);
104 csOn.Format("%d", iIndex);
105 }
106 }
107 if (csOn.IsEmpty())
108 csOn = "Yes";
109 return csOn;
110 }
111
GetExportValue() const112 CFX_WideString CPDF_FormControl::GetExportValue() const {
113 ASSERT(GetType() == CPDF_FormField::CheckBox ||
114 GetType() == CPDF_FormField::RadioButton);
115 CFX_ByteString csOn = GetOnStateName();
116 if (GetType() == CPDF_FormField::RadioButton ||
117 GetType() == CPDF_FormField::CheckBox) {
118 if (CPDF_Array* pArray =
119 ToArray(FPDF_GetFieldAttr(m_pField->m_pDict, "Opt"))) {
120 int iIndex = m_pField->GetControlIndex(this);
121 csOn = pArray->GetStringAt(iIndex);
122 }
123 }
124 if (csOn.IsEmpty())
125 csOn = "Yes";
126 return PDF_DecodeText(csOn);
127 }
128
IsChecked() const129 bool CPDF_FormControl::IsChecked() const {
130 ASSERT(GetType() == CPDF_FormField::CheckBox ||
131 GetType() == CPDF_FormField::RadioButton);
132 CFX_ByteString csOn = GetOnStateName();
133 CFX_ByteString csAS = m_pWidgetDict->GetStringFor("AS");
134 return csAS == csOn;
135 }
136
IsDefaultChecked() const137 bool CPDF_FormControl::IsDefaultChecked() const {
138 ASSERT(GetType() == CPDF_FormField::CheckBox ||
139 GetType() == CPDF_FormField::RadioButton);
140 CPDF_Object* pDV = FPDF_GetFieldAttr(m_pField->m_pDict, "DV");
141 if (!pDV)
142 return false;
143
144 CFX_ByteString csDV = pDV->GetString();
145 CFX_ByteString csOn = GetOnStateName();
146 return (csDV == csOn);
147 }
148
CheckControl(bool bChecked)149 void CPDF_FormControl::CheckControl(bool bChecked) {
150 ASSERT(GetType() == CPDF_FormField::CheckBox ||
151 GetType() == CPDF_FormField::RadioButton);
152 CFX_ByteString csOn = GetOnStateName();
153 CFX_ByteString csOldAS = m_pWidgetDict->GetStringFor("AS", "Off");
154 CFX_ByteString csAS = "Off";
155 if (bChecked)
156 csAS = csOn;
157 if (csOldAS == csAS)
158 return;
159 m_pWidgetDict->SetNewFor<CPDF_Name>("AS", csAS);
160 }
161
DrawControl(CFX_RenderDevice * pDevice,CFX_Matrix * pMatrix,CPDF_Page * pPage,CPDF_Annot::AppearanceMode mode,const CPDF_RenderOptions * pOptions)162 void CPDF_FormControl::DrawControl(CFX_RenderDevice* pDevice,
163 CFX_Matrix* pMatrix,
164 CPDF_Page* pPage,
165 CPDF_Annot::AppearanceMode mode,
166 const CPDF_RenderOptions* pOptions) {
167 if (m_pWidgetDict->GetIntegerFor("F") & ANNOTFLAG_HIDDEN)
168 return;
169
170 CPDF_Stream* pStream = FPDFDOC_GetAnnotAP(m_pWidgetDict, mode);
171 if (!pStream)
172 return;
173
174 CFX_FloatRect form_bbox = pStream->GetDict()->GetRectFor("BBox");
175 CFX_Matrix form_matrix = pStream->GetDict()->GetMatrixFor("Matrix");
176 form_matrix.TransformRect(form_bbox);
177 CFX_FloatRect arect = m_pWidgetDict->GetRectFor("Rect");
178 CFX_Matrix matrix;
179 matrix.MatchRect(arect, form_bbox);
180 matrix.Concat(*pMatrix);
181 CPDF_Form form(m_pField->m_pForm->m_pDocument,
182 m_pField->m_pForm->m_pFormDict->GetDictFor("DR"), pStream);
183 form.ParseContent(nullptr, nullptr, nullptr);
184 CPDF_RenderContext context(pPage);
185 context.AppendLayer(&form, &matrix);
186 context.Render(pDevice, pOptions, nullptr);
187 }
188
GetHighlightingMode()189 CPDF_FormControl::HighlightingMode CPDF_FormControl::GetHighlightingMode() {
190 if (!m_pWidgetDict)
191 return Invert;
192
193 CFX_ByteString csH = m_pWidgetDict->GetStringFor("H", "I");
194 for (size_t i = 0; i < FX_ArraySize(g_sHighlightingMode); ++i) {
195 if (csH == g_sHighlightingMode[i])
196 return static_cast<HighlightingMode>(i);
197 }
198 return Invert;
199 }
200
GetMK() const201 CPDF_ApSettings CPDF_FormControl::GetMK() const {
202 return CPDF_ApSettings(m_pWidgetDict ? m_pWidgetDict->GetDictFor("MK")
203 : nullptr);
204 }
205
HasMKEntry(const CFX_ByteString & csEntry) const206 bool CPDF_FormControl::HasMKEntry(const CFX_ByteString& csEntry) const {
207 return GetMK().HasMKEntry(csEntry);
208 }
209
GetRotation()210 int CPDF_FormControl::GetRotation() {
211 return GetMK().GetRotation();
212 }
213
GetColor(int & iColorType,const CFX_ByteString & csEntry)214 FX_ARGB CPDF_FormControl::GetColor(int& iColorType,
215 const CFX_ByteString& csEntry) {
216 return GetMK().GetColor(iColorType, csEntry);
217 }
218
GetOriginalColor(int index,const CFX_ByteString & csEntry)219 FX_FLOAT CPDF_FormControl::GetOriginalColor(int index,
220 const CFX_ByteString& csEntry) {
221 return GetMK().GetOriginalColor(index, csEntry);
222 }
223
GetOriginalColor(int & iColorType,FX_FLOAT fc[4],const CFX_ByteString & csEntry)224 void CPDF_FormControl::GetOriginalColor(int& iColorType,
225 FX_FLOAT fc[4],
226 const CFX_ByteString& csEntry) {
227 GetMK().GetOriginalColor(iColorType, fc, csEntry);
228 }
229
GetCaption(const CFX_ByteString & csEntry)230 CFX_WideString CPDF_FormControl::GetCaption(const CFX_ByteString& csEntry) {
231 return GetMK().GetCaption(csEntry);
232 }
233
GetIcon(const CFX_ByteString & csEntry)234 CPDF_Stream* CPDF_FormControl::GetIcon(const CFX_ByteString& csEntry) {
235 return GetMK().GetIcon(csEntry);
236 }
237
GetIconFit()238 CPDF_IconFit CPDF_FormControl::GetIconFit() {
239 return GetMK().GetIconFit();
240 }
241
GetTextPosition()242 int CPDF_FormControl::GetTextPosition() {
243 return GetMK().GetTextPosition();
244 }
245
GetAction()246 CPDF_Action CPDF_FormControl::GetAction() {
247 if (!m_pWidgetDict)
248 return CPDF_Action();
249
250 if (m_pWidgetDict->KeyExist("A"))
251 return CPDF_Action(m_pWidgetDict->GetDictFor("A"));
252
253 CPDF_Object* pObj = FPDF_GetFieldAttr(m_pField->m_pDict, "A");
254 if (!pObj)
255 return CPDF_Action();
256
257 return CPDF_Action(pObj->GetDict());
258 }
259
GetAdditionalAction()260 CPDF_AAction CPDF_FormControl::GetAdditionalAction() {
261 if (!m_pWidgetDict)
262 return CPDF_AAction();
263
264 if (m_pWidgetDict->KeyExist("AA"))
265 return CPDF_AAction(m_pWidgetDict->GetDictFor("AA"));
266 return m_pField->GetAdditionalAction();
267 }
268
GetDefaultAppearance()269 CPDF_DefaultAppearance CPDF_FormControl::GetDefaultAppearance() {
270 if (!m_pWidgetDict)
271 return CPDF_DefaultAppearance();
272
273 if (m_pWidgetDict->KeyExist("DA"))
274 return CPDF_DefaultAppearance(m_pWidgetDict->GetStringFor("DA"));
275
276 CPDF_Object* pObj = FPDF_GetFieldAttr(m_pField->m_pDict, "DA");
277 if (pObj)
278 return CPDF_DefaultAppearance(pObj->GetString());
279 return m_pField->m_pForm->GetDefaultAppearance();
280 }
281
GetDefaultControlFont()282 CPDF_Font* CPDF_FormControl::GetDefaultControlFont() {
283 CPDF_DefaultAppearance cDA = GetDefaultAppearance();
284 CFX_ByteString csFontNameTag;
285 FX_FLOAT fFontSize;
286 cDA.GetFont(csFontNameTag, fFontSize);
287 if (csFontNameTag.IsEmpty())
288 return nullptr;
289
290 CPDF_Object* pObj = FPDF_GetFieldAttr(m_pWidgetDict, "DR");
291 if (CPDF_Dictionary* pDict = ToDictionary(pObj)) {
292 CPDF_Dictionary* pFonts = pDict->GetDictFor("Font");
293 if (pFonts) {
294 CPDF_Dictionary* pElement = pFonts->GetDictFor(csFontNameTag);
295 if (pElement) {
296 CPDF_Font* pFont = m_pField->m_pForm->m_pDocument->LoadFont(pElement);
297 if (pFont)
298 return pFont;
299 }
300 }
301 }
302 if (CPDF_Font* pFormFont = m_pField->m_pForm->GetFormFont(csFontNameTag))
303 return pFormFont;
304
305 CPDF_Dictionary* pPageDict = m_pWidgetDict->GetDictFor("P");
306 pObj = FPDF_GetFieldAttr(pPageDict, "Resources");
307 if (CPDF_Dictionary* pDict = ToDictionary(pObj)) {
308 CPDF_Dictionary* pFonts = pDict->GetDictFor("Font");
309 if (pFonts) {
310 CPDF_Dictionary* pElement = pFonts->GetDictFor(csFontNameTag);
311 if (pElement) {
312 CPDF_Font* pFont = m_pField->m_pForm->m_pDocument->LoadFont(pElement);
313 if (pFont)
314 return pFont;
315 }
316 }
317 }
318 return nullptr;
319 }
320
GetControlAlignment()321 int CPDF_FormControl::GetControlAlignment() {
322 if (!m_pWidgetDict)
323 return 0;
324 if (m_pWidgetDict->KeyExist("Q"))
325 return m_pWidgetDict->GetIntegerFor("Q", 0);
326
327 CPDF_Object* pObj = FPDF_GetFieldAttr(m_pField->m_pDict, "Q");
328 if (pObj)
329 return pObj->GetInteger();
330 return m_pField->m_pForm->GetFormAlignment();
331 }
332