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 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->GetForm()) {}
34
~CPDF_FormControl()35 CPDF_FormControl::~CPDF_FormControl() {}
36
GetOnStateName() const37 ByteString CPDF_FormControl::GetOnStateName() const {
38 ASSERT(GetType() == CPDF_FormField::CheckBox ||
39 GetType() == CPDF_FormField::RadioButton);
40 ByteString csOn;
41 CPDF_Dictionary* pAP = m_pWidgetDict->GetDictFor("AP");
42 if (!pAP)
43 return csOn;
44
45 CPDF_Dictionary* pN = pAP->GetDictFor("N");
46 if (!pN)
47 return csOn;
48
49 for (const auto& it : *pN) {
50 if (it.first != "Off")
51 return it.first;
52 }
53 return ByteString();
54 }
55
SetOnStateName(const ByteString & csOn)56 void CPDF_FormControl::SetOnStateName(const ByteString& csOn) {
57 ASSERT(GetType() == CPDF_FormField::CheckBox ||
58 GetType() == CPDF_FormField::RadioButton);
59 ByteString csValue = csOn;
60 if (csValue.IsEmpty())
61 csValue = "Yes";
62 else if (csValue == "Off")
63 csValue = "Yes";
64
65 ByteString csAS = m_pWidgetDict->GetStringFor("AS", "Off");
66 if (csAS != "Off")
67 m_pWidgetDict->SetNewFor<CPDF_Name>("AS", csValue);
68
69 CPDF_Dictionary* pAP = m_pWidgetDict->GetDictFor("AP");
70 if (!pAP)
71 return;
72
73 for (const auto& it : *pAP) {
74 CPDF_Object* pObj1 = it.second.get();
75 if (!pObj1)
76 continue;
77
78 CPDF_Object* pObjDirect1 = pObj1->GetDirect();
79 CPDF_Dictionary* pSubDict = pObjDirect1->AsDictionary();
80 if (!pSubDict)
81 continue;
82
83 auto subdict_it = pSubDict->begin();
84 while (subdict_it != pSubDict->end()) {
85 const ByteString& csKey2 = subdict_it->first;
86 CPDF_Object* pObj2 = subdict_it->second.get();
87 ++subdict_it;
88 if (!pObj2)
89 continue;
90 if (csKey2 != "Off") {
91 pSubDict->ReplaceKey(csKey2, csValue);
92 break;
93 }
94 }
95 }
96 }
97
GetCheckedAPState()98 ByteString CPDF_FormControl::GetCheckedAPState() {
99 ASSERT(GetType() == CPDF_FormField::CheckBox ||
100 GetType() == CPDF_FormField::RadioButton);
101 ByteString csOn = GetOnStateName();
102 if (GetType() == CPDF_FormField::RadioButton ||
103 GetType() == CPDF_FormField::CheckBox) {
104 if (ToArray(FPDF_GetFieldAttr(m_pField->GetDict(), "Opt")))
105 csOn = ByteString::Format("%d", m_pField->GetControlIndex(this));
106 }
107 if (csOn.IsEmpty())
108 csOn = "Yes";
109 return csOn;
110 }
111
GetExportValue() const112 WideString CPDF_FormControl::GetExportValue() const {
113 ASSERT(GetType() == CPDF_FormField::CheckBox ||
114 GetType() == CPDF_FormField::RadioButton);
115 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->GetDict(), "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 ByteString csOn = GetOnStateName();
133 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->GetDict(), "DV");
141 if (!pDV)
142 return false;
143
144 ByteString csDV = pDV->GetString();
145 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 ByteString csOn = GetOnStateName();
153 ByteString csOldAS = m_pWidgetDict->GetStringFor("AS", "Off");
154 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.Get(), mode);
171 if (!pStream)
172 return;
173
174 CFX_Matrix form_matrix = pStream->GetDict()->GetMatrixFor("Matrix");
175 CFX_FloatRect form_bbox =
176 form_matrix.TransformRect(pStream->GetDict()->GetRectFor("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->GetForm()->GetDocument(),
182 m_pField->GetForm()->GetFormDict()->GetDictFor("DR"), pStream);
183 form.ParseContent();
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 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 ByteString & csEntry) const206 bool CPDF_FormControl::HasMKEntry(const 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 ByteString & csEntry)214 FX_ARGB CPDF_FormControl::GetColor(int& iColorType, const ByteString& csEntry) {
215 return GetMK().GetColor(iColorType, csEntry);
216 }
217
GetOriginalColor(int index,const ByteString & csEntry)218 float CPDF_FormControl::GetOriginalColor(int index, const ByteString& csEntry) {
219 return GetMK().GetOriginalColor(index, csEntry);
220 }
221
GetOriginalColor(int & iColorType,float fc[4],const ByteString & csEntry)222 void CPDF_FormControl::GetOriginalColor(int& iColorType,
223 float fc[4],
224 const ByteString& csEntry) {
225 GetMK().GetOriginalColor(iColorType, fc, csEntry);
226 }
227
GetCaption(const ByteString & csEntry)228 WideString CPDF_FormControl::GetCaption(const ByteString& csEntry) {
229 return GetMK().GetCaption(csEntry);
230 }
231
GetIcon(const ByteString & csEntry)232 CPDF_Stream* CPDF_FormControl::GetIcon(const ByteString& csEntry) {
233 return GetMK().GetIcon(csEntry);
234 }
235
GetIconFit()236 CPDF_IconFit CPDF_FormControl::GetIconFit() {
237 return GetMK().GetIconFit();
238 }
239
GetTextPosition()240 int CPDF_FormControl::GetTextPosition() {
241 return GetMK().GetTextPosition();
242 }
243
GetAction()244 CPDF_Action CPDF_FormControl::GetAction() {
245 if (!m_pWidgetDict)
246 return CPDF_Action(nullptr);
247
248 if (m_pWidgetDict->KeyExist("A"))
249 return CPDF_Action(m_pWidgetDict->GetDictFor("A"));
250
251 CPDF_Object* pObj = FPDF_GetFieldAttr(m_pField->GetDict(), "A");
252 return CPDF_Action(pObj ? pObj->GetDict() : nullptr);
253 }
254
GetAdditionalAction()255 CPDF_AAction CPDF_FormControl::GetAdditionalAction() {
256 if (!m_pWidgetDict)
257 return CPDF_AAction(nullptr);
258
259 if (m_pWidgetDict->KeyExist("AA"))
260 return CPDF_AAction(m_pWidgetDict->GetDictFor("AA"));
261 return m_pField->GetAdditionalAction();
262 }
263
GetDefaultAppearance()264 CPDF_DefaultAppearance CPDF_FormControl::GetDefaultAppearance() {
265 if (!m_pWidgetDict)
266 return CPDF_DefaultAppearance();
267
268 if (m_pWidgetDict->KeyExist("DA"))
269 return CPDF_DefaultAppearance(m_pWidgetDict->GetStringFor("DA"));
270
271 CPDF_Object* pObj = FPDF_GetFieldAttr(m_pField->GetDict(), "DA");
272 if (pObj)
273 return CPDF_DefaultAppearance(pObj->GetString());
274 return m_pField->GetForm()->GetDefaultAppearance();
275 }
276
GetDefaultControlFont()277 CPDF_Font* CPDF_FormControl::GetDefaultControlFont() {
278 float fFontSize;
279 CPDF_DefaultAppearance cDA = GetDefaultAppearance();
280 ByteString csFontNameTag = cDA.GetFont(&fFontSize);
281 if (csFontNameTag.IsEmpty())
282 return nullptr;
283
284 CPDF_Object* pObj = FPDF_GetFieldAttr(m_pWidgetDict.Get(), "DR");
285 if (CPDF_Dictionary* pDict = ToDictionary(pObj)) {
286 CPDF_Dictionary* pFonts = pDict->GetDictFor("Font");
287 if (pFonts) {
288 CPDF_Dictionary* pElement = pFonts->GetDictFor(csFontNameTag);
289 if (pElement) {
290 CPDF_Font* pFont =
291 m_pField->GetForm()->GetDocument()->LoadFont(pElement);
292 if (pFont)
293 return pFont;
294 }
295 }
296 }
297 if (CPDF_Font* pFormFont = m_pField->GetForm()->GetFormFont(csFontNameTag))
298 return pFormFont;
299
300 CPDF_Dictionary* pPageDict = m_pWidgetDict->GetDictFor("P");
301 pObj = FPDF_GetFieldAttr(pPageDict, "Resources");
302 if (CPDF_Dictionary* pDict = ToDictionary(pObj)) {
303 CPDF_Dictionary* pFonts = pDict->GetDictFor("Font");
304 if (pFonts) {
305 CPDF_Dictionary* pElement = pFonts->GetDictFor(csFontNameTag);
306 if (pElement) {
307 CPDF_Font* pFont =
308 m_pField->GetForm()->GetDocument()->LoadFont(pElement);
309 if (pFont)
310 return pFont;
311 }
312 }
313 }
314 return nullptr;
315 }
316
GetControlAlignment()317 int CPDF_FormControl::GetControlAlignment() {
318 if (!m_pWidgetDict)
319 return 0;
320 if (m_pWidgetDict->KeyExist("Q"))
321 return m_pWidgetDict->GetIntegerFor("Q", 0);
322
323 CPDF_Object* pObj = FPDF_GetFieldAttr(m_pField->GetDict(), "Q");
324 if (pObj)
325 return pObj->GetInteger();
326 return m_pField->GetForm()->GetFormAlignment();
327 }
328