1 // Copyright 2014 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_formfield.h"
8
9 #include <memory>
10 #include <set>
11 #include <utility>
12
13 #include "constants/form_fields.h"
14 #include "constants/form_flags.h"
15 #include "core/fpdfapi/font/cpdf_font.h"
16 #include "core/fpdfapi/page/cpdf_docpagedata.h"
17 #include "core/fpdfapi/parser/cfdf_document.h"
18 #include "core/fpdfapi/parser/cpdf_array.h"
19 #include "core/fpdfapi/parser/cpdf_dictionary.h"
20 #include "core/fpdfapi/parser/cpdf_document.h"
21 #include "core/fpdfapi/parser/cpdf_name.h"
22 #include "core/fpdfapi/parser/cpdf_number.h"
23 #include "core/fpdfapi/parser/cpdf_string.h"
24 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
25 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
26 #include "core/fpdfdoc/cpdf_defaultappearance.h"
27 #include "core/fpdfdoc/cpdf_formcontrol.h"
28 #include "core/fpdfdoc/cpdf_interactiveform.h"
29 #include "core/fpdfdoc/cpvt_generateap.h"
30 #include "third_party/base/ptr_util.h"
31 #include "third_party/base/stl_util.h"
32
33 namespace {
34
GetFieldAttrRecursive(const CPDF_Dictionary * pFieldDict,const ByteString & name,int nLevel)35 const CPDF_Object* GetFieldAttrRecursive(const CPDF_Dictionary* pFieldDict,
36 const ByteString& name,
37 int nLevel) {
38 static constexpr int kGetFieldMaxRecursion = 32;
39 if (!pFieldDict || nLevel > kGetFieldMaxRecursion)
40 return nullptr;
41
42 const CPDF_Object* pAttr = pFieldDict->GetDirectObjectFor(name);
43 if (pAttr)
44 return pAttr;
45
46 return GetFieldAttrRecursive(
47 pFieldDict->GetDictFor(pdfium::form_fields::kParent), name, nLevel + 1);
48 }
49
50 } // namespace
51
52 // static
IntToFormFieldType(int value)53 Optional<FormFieldType> CPDF_FormField::IntToFormFieldType(int value) {
54 if (value >= static_cast<int>(FormFieldType::kUnknown) &&
55 value < static_cast<int>(kFormFieldTypeCount)) {
56 return {static_cast<FormFieldType>(value)};
57 }
58 return {};
59 }
60
61 // static
GetFieldAttr(const CPDF_Dictionary * pFieldDict,const ByteString & name)62 const CPDF_Object* CPDF_FormField::GetFieldAttr(
63 const CPDF_Dictionary* pFieldDict,
64 const ByteString& name) {
65 return GetFieldAttrRecursive(pFieldDict, name, 0);
66 }
67
68 // static
GetFieldAttr(CPDF_Dictionary * pFieldDict,const ByteString & name)69 CPDF_Object* CPDF_FormField::GetFieldAttr(CPDF_Dictionary* pFieldDict,
70 const ByteString& name) {
71 return const_cast<CPDF_Object*>(GetFieldAttrRecursive(
72 static_cast<const CPDF_Dictionary*>(pFieldDict), name, 0));
73 }
74
75 // static
GetFullNameForDict(CPDF_Dictionary * pFieldDict)76 WideString CPDF_FormField::GetFullNameForDict(CPDF_Dictionary* pFieldDict) {
77 WideString full_name;
78 std::set<CPDF_Dictionary*> visited;
79 CPDF_Dictionary* pLevel = pFieldDict;
80 while (pLevel) {
81 visited.insert(pLevel);
82 WideString short_name = pLevel->GetUnicodeTextFor(pdfium::form_fields::kT);
83 if (!short_name.IsEmpty()) {
84 if (full_name.IsEmpty())
85 full_name = std::move(short_name);
86 else
87 full_name = short_name + L'.' + full_name;
88 }
89 pLevel = pLevel->GetDictFor(pdfium::form_fields::kParent);
90 if (pdfium::ContainsKey(visited, pLevel))
91 break;
92 }
93 return full_name;
94 }
95
CPDF_FormField(CPDF_InteractiveForm * pForm,CPDF_Dictionary * pDict)96 CPDF_FormField::CPDF_FormField(CPDF_InteractiveForm* pForm,
97 CPDF_Dictionary* pDict)
98 : m_pForm(pForm), m_pDict(pDict) {
99 InitFieldFlags();
100 }
101
102 CPDF_FormField::~CPDF_FormField() = default;
103
InitFieldFlags()104 void CPDF_FormField::InitFieldFlags() {
105 const CPDF_Object* ft_attr =
106 GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kFT);
107 ByteString type_name = ft_attr ? ft_attr->GetString() : ByteString();
108 uint32_t flags = GetFieldFlags();
109 m_bRequired = flags & pdfium::form_flags::kRequired;
110 m_bNoExport = flags & pdfium::form_flags::kNoExport;
111
112 if (type_name == pdfium::form_fields::kBtn) {
113 if (flags & pdfium::form_flags::kButtonRadio) {
114 m_Type = kRadioButton;
115 m_bIsUnison = flags & pdfium::form_flags::kButtonRadiosInUnison;
116 } else if (flags & pdfium::form_flags::kButtonPushbutton) {
117 m_Type = kPushButton;
118 } else {
119 m_Type = kCheckBox;
120 m_bIsUnison = true;
121 }
122 } else if (type_name == pdfium::form_fields::kTx) {
123 if (flags & pdfium::form_flags::kTextFileSelect)
124 m_Type = kFile;
125 else if (flags & pdfium::form_flags::kTextRichText)
126 m_Type = kRichText;
127 else
128 m_Type = kText;
129 LoadDA();
130 } else if (type_name == pdfium::form_fields::kCh) {
131 if (flags & pdfium::form_flags::kChoiceCombo) {
132 m_Type = kComboBox;
133 } else {
134 m_Type = kListBox;
135 m_bIsMultiSelectListBox = flags & pdfium::form_flags::kChoiceMultiSelect;
136 }
137 LoadDA();
138 } else if (type_name == pdfium::form_fields::kSig) {
139 m_Type = kSign;
140 }
141 }
142
GetFullName() const143 WideString CPDF_FormField::GetFullName() const {
144 return GetFullNameForDict(m_pDict.Get());
145 }
146
ResetField(NotificationOption notify)147 bool CPDF_FormField::ResetField(NotificationOption notify) {
148 switch (m_Type) {
149 case kCheckBox:
150 case kRadioButton: {
151 int iCount = CountControls();
152 // TODO(weili): Check whether anything special needs to be done for
153 // |m_bIsUnison|.
154 for (int i = 0; i < iCount; i++) {
155 CheckControl(i, GetControl(i)->IsDefaultChecked(),
156 NotificationOption::kDoNotNotify);
157 }
158 if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify())
159 m_pForm->GetFormNotify()->AfterCheckedStatusChange(this);
160 break;
161 }
162 case kComboBox:
163 case kListBox: {
164 ClearSelection(NotificationOption::kDoNotNotify);
165 WideString csValue;
166 int iIndex = GetDefaultSelectedItem();
167 if (iIndex >= 0)
168 csValue = GetOptionLabel(iIndex);
169 if (notify == NotificationOption::kNotify &&
170 !NotifyListOrComboBoxBeforeChange(csValue)) {
171 return false;
172 }
173 SetItemSelection(iIndex, true, NotificationOption::kDoNotNotify);
174 if (notify == NotificationOption::kNotify)
175 NotifyListOrComboBoxAfterChange();
176 break;
177 }
178 case kText:
179 case kRichText:
180 case kFile:
181 default: {
182 const CPDF_Object* pDV = GetDefaultValueObject();
183 WideString csDValue;
184 if (pDV)
185 csDValue = pDV->GetUnicodeText();
186
187 WideString csValue;
188 {
189 // Limit the scope of |pV| because it may get invalidated below.
190 const CPDF_Object* pV = GetValueObject();
191 if (pV)
192 csValue = pV->GetUnicodeText();
193 }
194
195 bool bHasRV = !!GetFieldAttr(m_pDict.Get(), "RV");
196 if (!bHasRV && (csDValue == csValue))
197 return false;
198
199 if (notify == NotificationOption::kNotify &&
200 !NotifyBeforeValueChange(csDValue)) {
201 return false;
202 }
203 if (pDV) {
204 RetainPtr<CPDF_Object> pClone = pDV->Clone();
205 if (!pClone)
206 return false;
207
208 m_pDict->SetFor(pdfium::form_fields::kV, std::move(pClone));
209 if (bHasRV) {
210 m_pDict->SetFor("RV", pDV->Clone());
211 }
212 } else {
213 m_pDict->RemoveFor(pdfium::form_fields::kV);
214 m_pDict->RemoveFor("RV");
215 }
216 if (notify == NotificationOption::kNotify)
217 NotifyAfterValueChange();
218 break;
219 }
220 }
221 return true;
222 }
223
CountControls() const224 int CPDF_FormField::CountControls() const {
225 return pdfium::CollectionSize<int>(GetControls());
226 }
227
GetControl(int index) const228 CPDF_FormControl* CPDF_FormField::GetControl(int index) const {
229 return GetControls()[index].Get();
230 }
231
GetControlIndex(const CPDF_FormControl * pControl) const232 int CPDF_FormField::GetControlIndex(const CPDF_FormControl* pControl) const {
233 if (!pControl)
234 return -1;
235
236 const auto& controls = GetControls();
237 auto it = std::find(controls.begin(), controls.end(), pControl);
238 return it != controls.end() ? it - controls.begin() : -1;
239 }
240
GetFieldType() const241 FormFieldType CPDF_FormField::GetFieldType() const {
242 switch (m_Type) {
243 case kPushButton:
244 return FormFieldType::kPushButton;
245 case kCheckBox:
246 return FormFieldType::kCheckBox;
247 case kRadioButton:
248 return FormFieldType::kRadioButton;
249 case kComboBox:
250 return FormFieldType::kComboBox;
251 case kListBox:
252 return FormFieldType::kListBox;
253 case kText:
254 case kRichText:
255 case kFile:
256 return FormFieldType::kTextField;
257 case kSign:
258 return FormFieldType::kSignature;
259 default:
260 return FormFieldType::kUnknown;
261 }
262 }
263
GetAdditionalAction() const264 CPDF_AAction CPDF_FormField::GetAdditionalAction() const {
265 CPDF_Object* pObj = GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kAA);
266 return CPDF_AAction(pObj ? pObj->GetDict() : nullptr);
267 }
268
GetAlternateName() const269 WideString CPDF_FormField::GetAlternateName() const {
270 const CPDF_Object* pObj =
271 GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kTU);
272 return pObj ? pObj->GetUnicodeText() : WideString();
273 }
274
GetMappingName() const275 WideString CPDF_FormField::GetMappingName() const {
276 const CPDF_Object* pObj =
277 GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kTM);
278 return pObj ? pObj->GetUnicodeText() : WideString();
279 }
280
GetFieldFlags() const281 uint32_t CPDF_FormField::GetFieldFlags() const {
282 const CPDF_Object* pObj =
283 GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kFf);
284 return pObj ? pObj->GetInteger() : 0;
285 }
286
GetDefaultStyle() const287 ByteString CPDF_FormField::GetDefaultStyle() const {
288 const CPDF_Object* pObj = GetFieldAttr(m_pDict.Get(), "DS");
289 return pObj ? pObj->GetString() : ByteString();
290 }
291
SetOpt(RetainPtr<CPDF_Object> pOpt)292 void CPDF_FormField::SetOpt(RetainPtr<CPDF_Object> pOpt) {
293 m_pDict->SetFor("Opt", std::move(pOpt));
294 }
295
GetValue(bool bDefault) const296 WideString CPDF_FormField::GetValue(bool bDefault) const {
297 if (GetType() == kCheckBox || GetType() == kRadioButton)
298 return GetCheckValue(bDefault);
299
300 const CPDF_Object* pValue =
301 bDefault ? GetDefaultValueObject() : GetValueObject();
302 if (!pValue) {
303 if (!bDefault && m_Type != kText)
304 pValue = GetDefaultValueObject();
305 if (!pValue)
306 return WideString();
307 }
308
309 switch (pValue->GetType()) {
310 case CPDF_Object::kString:
311 case CPDF_Object::kStream:
312 return pValue->GetUnicodeText();
313 case CPDF_Object::kArray:
314 pValue = pValue->AsArray()->GetDirectObjectAt(0);
315 if (pValue)
316 return pValue->GetUnicodeText();
317 break;
318 default:
319 break;
320 }
321 return WideString();
322 }
323
GetValue() const324 WideString CPDF_FormField::GetValue() const {
325 return GetValue(false);
326 }
327
GetDefaultValue() const328 WideString CPDF_FormField::GetDefaultValue() const {
329 return GetValue(true);
330 }
331
SetValue(const WideString & value,bool bDefault,NotificationOption notify)332 bool CPDF_FormField::SetValue(const WideString& value,
333 bool bDefault,
334 NotificationOption notify) {
335 switch (m_Type) {
336 case kCheckBox:
337 case kRadioButton: {
338 SetCheckValue(value, bDefault, notify);
339 return true;
340 }
341 case kFile:
342 case kRichText:
343 case kText:
344 case kComboBox: {
345 WideString csValue = value;
346 if (notify == NotificationOption::kNotify &&
347 !NotifyBeforeValueChange(csValue)) {
348 return false;
349 }
350 ByteString key(bDefault ? pdfium::form_fields::kDV
351 : pdfium::form_fields::kV);
352 m_pDict->SetNewFor<CPDF_String>(key, csValue);
353 int iIndex = FindOption(csValue);
354 if (iIndex < 0) {
355 if (m_Type == kRichText && !bDefault) {
356 m_pDict->SetFor("RV", m_pDict->GetObjectFor(key)->Clone());
357 }
358 m_pDict->RemoveFor("I");
359 } else {
360 if (!bDefault) {
361 ClearSelection(NotificationOption::kDoNotNotify);
362 SetItemSelection(iIndex, true, NotificationOption::kDoNotNotify);
363 }
364 }
365 if (notify == NotificationOption::kNotify)
366 NotifyAfterValueChange();
367 break;
368 }
369 case kListBox: {
370 int iIndex = FindOption(value);
371 if (iIndex < 0)
372 return false;
373
374 if (bDefault && iIndex == GetDefaultSelectedItem())
375 return false;
376
377 if (notify == NotificationOption::kNotify &&
378 !NotifyBeforeSelectionChange(value)) {
379 return false;
380 }
381 if (!bDefault) {
382 ClearSelection(NotificationOption::kDoNotNotify);
383 SetItemSelection(iIndex, true, NotificationOption::kDoNotNotify);
384 }
385 if (notify == NotificationOption::kNotify)
386 NotifyAfterSelectionChange();
387 break;
388 }
389 default:
390 break;
391 }
392 return true;
393 }
394
SetValue(const WideString & value,NotificationOption notify)395 bool CPDF_FormField::SetValue(const WideString& value,
396 NotificationOption notify) {
397 return SetValue(value, false, notify);
398 }
399
GetMaxLen() const400 int CPDF_FormField::GetMaxLen() const {
401 if (const CPDF_Object* pObj = GetFieldAttr(m_pDict.Get(), "MaxLen"))
402 return pObj->GetInteger();
403
404 for (auto& pControl : GetControls()) {
405 if (!pControl)
406 continue;
407
408 const CPDF_Dictionary* pWidgetDict = pControl->GetWidget();
409 if (pWidgetDict->KeyExist("MaxLen"))
410 return pWidgetDict->GetIntegerFor("MaxLen");
411 }
412 return 0;
413 }
414
CountSelectedItems() const415 int CPDF_FormField::CountSelectedItems() const {
416 const CPDF_Object* pValue = GetValueOrSelectedIndicesObject();
417 if (!pValue)
418 return 0;
419
420 if (pValue->IsString() || pValue->IsNumber())
421 return pValue->GetString().IsEmpty() ? 0 : 1;
422 const CPDF_Array* pArray = pValue->AsArray();
423 return pArray ? pArray->size() : 0;
424 }
425
GetSelectedIndex(int index) const426 int CPDF_FormField::GetSelectedIndex(int index) const {
427 const CPDF_Object* pValue = GetValueOrSelectedIndicesObject();
428 if (!pValue)
429 return -1;
430
431 if (pValue->IsNumber())
432 return pValue->GetInteger();
433
434 WideString sel_value;
435 if (pValue->IsString()) {
436 if (index != 0)
437 return -1;
438 sel_value = pValue->GetUnicodeText();
439 } else {
440 const CPDF_Array* pArray = pValue->AsArray();
441 if (!pArray || index < 0)
442 return -1;
443
444 const CPDF_Object* elementValue = pArray->GetDirectObjectAt(index);
445 sel_value = elementValue ? elementValue->GetUnicodeText() : WideString();
446 }
447 if (index < CountSelectedOptions()) {
448 int iOptIndex = GetSelectedOptionIndex(index);
449 WideString csOpt = GetOptionValue(iOptIndex);
450 if (csOpt == sel_value)
451 return iOptIndex;
452 }
453 for (int i = 0; i < CountOptions(); i++) {
454 if (sel_value == GetOptionValue(i))
455 return i;
456 }
457 return -1;
458 }
459
ClearSelection(NotificationOption notify)460 bool CPDF_FormField::ClearSelection(NotificationOption notify) {
461 if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify()) {
462 WideString csValue;
463 int iIndex = GetSelectedIndex(0);
464 if (iIndex >= 0)
465 csValue = GetOptionLabel(iIndex);
466 if (!NotifyListOrComboBoxBeforeChange(csValue))
467 return false;
468 }
469 m_pDict->RemoveFor(pdfium::form_fields::kV);
470 m_pDict->RemoveFor("I");
471 if (notify == NotificationOption::kNotify)
472 NotifyListOrComboBoxAfterChange();
473 return true;
474 }
475
IsItemSelected(int index) const476 bool CPDF_FormField::IsItemSelected(int index) const {
477 ASSERT(GetType() == kComboBox || GetType() == kListBox);
478 if (index < 0 || index >= CountOptions())
479 return false;
480 if (IsOptionSelected(index))
481 return true;
482
483 WideString opt_value = GetOptionValue(index);
484 const CPDF_Object* pValue = GetValueOrSelectedIndicesObject();
485 if (!pValue)
486 return false;
487
488 if (pValue->IsString())
489 return pValue->GetUnicodeText() == opt_value;
490
491 if (pValue->IsNumber()) {
492 if (pValue->GetString().IsEmpty())
493 return false;
494 return (pValue->GetInteger() == index);
495 }
496
497 const CPDF_Array* pArray = pValue->AsArray();
498 if (!pArray)
499 return false;
500
501 for (int i = 0; i < CountSelectedOptions(); ++i) {
502 if (GetSelectedOptionIndex(i) == index) {
503 const CPDF_Object* pDirectObj = pArray->GetDirectObjectAt(i);
504 return pDirectObj && pDirectObj->GetUnicodeText() == opt_value;
505 }
506 }
507 return false;
508 }
509
SetItemSelection(int index,bool bSelected,NotificationOption notify)510 bool CPDF_FormField::SetItemSelection(int index,
511 bool bSelected,
512 NotificationOption notify) {
513 ASSERT(GetType() == kComboBox || GetType() == kListBox);
514 if (index < 0 || index >= CountOptions())
515 return false;
516
517 WideString opt_value = GetOptionValue(index);
518 if (notify == NotificationOption::kNotify &&
519 !NotifyListOrComboBoxBeforeChange(opt_value)) {
520 return false;
521 }
522
523 if (bSelected)
524 SetItemSelectionSelected(index, opt_value);
525 else
526 SetItemSelectionUnselected(index, opt_value);
527
528 if (notify == NotificationOption::kNotify)
529 NotifyListOrComboBoxAfterChange();
530 return true;
531 }
532
SetItemSelectionSelected(int index,const WideString & opt_value)533 void CPDF_FormField::SetItemSelectionSelected(int index,
534 const WideString& opt_value) {
535 if (GetType() != kListBox) {
536 m_pDict->SetNewFor<CPDF_String>(pdfium::form_fields::kV, opt_value);
537 CPDF_Array* pI = m_pDict->SetNewFor<CPDF_Array>("I");
538 pI->AddNew<CPDF_Number>(index);
539 return;
540 }
541
542 SelectOption(index, true, NotificationOption::kDoNotNotify);
543 if (!m_bIsMultiSelectListBox) {
544 m_pDict->SetNewFor<CPDF_String>(pdfium::form_fields::kV, opt_value);
545 return;
546 }
547
548 CPDF_Array* pArray = m_pDict->SetNewFor<CPDF_Array>(pdfium::form_fields::kV);
549 for (int i = 0; i < CountOptions(); i++) {
550 if (i == index || IsItemSelected(i))
551 pArray->AddNew<CPDF_String>(GetOptionValue(i));
552 }
553 }
554
SetItemSelectionUnselected(int index,const WideString & opt_value)555 void CPDF_FormField::SetItemSelectionUnselected(int index,
556 const WideString& opt_value) {
557 const CPDF_Object* pValue = GetValueObject();
558 if (!pValue)
559 return;
560
561 if (GetType() != kListBox) {
562 m_pDict->RemoveFor(pdfium::form_fields::kV);
563 m_pDict->RemoveFor("I");
564 return;
565 }
566
567 SelectOption(index, false, NotificationOption::kDoNotNotify);
568 if (pValue->IsString()) {
569 if (pValue->GetUnicodeText() == opt_value) {
570 m_pDict->RemoveFor(pdfium::form_fields::kV);
571 }
572 return;
573 }
574
575 if (!pValue->IsArray())
576 return;
577
578 auto pArray = pdfium::MakeRetain<CPDF_Array>();
579 for (int i = 0; i < CountOptions(); i++) {
580 if (i != index && IsItemSelected(i))
581 pArray->AddNew<CPDF_String>(GetOptionValue(i));
582 }
583 if (pArray->size() > 0) {
584 m_pDict->SetFor(pdfium::form_fields::kV, pArray);
585 }
586 }
587
IsItemDefaultSelected(int index) const588 bool CPDF_FormField::IsItemDefaultSelected(int index) const {
589 ASSERT(GetType() == kComboBox || GetType() == kListBox);
590 if (index < 0 || index >= CountOptions())
591 return false;
592 int iDVIndex = GetDefaultSelectedItem();
593 return iDVIndex >= 0 && iDVIndex == index;
594 }
595
GetDefaultSelectedItem() const596 int CPDF_FormField::GetDefaultSelectedItem() const {
597 ASSERT(GetType() == kComboBox || GetType() == kListBox);
598 const CPDF_Object* pValue = GetDefaultValueObject();
599 if (!pValue)
600 return -1;
601 WideString csDV = pValue->GetUnicodeText();
602 if (csDV.IsEmpty())
603 return -1;
604 for (int i = 0; i < CountOptions(); i++) {
605 if (csDV == GetOptionValue(i))
606 return i;
607 }
608 return -1;
609 }
610
CountOptions() const611 int CPDF_FormField::CountOptions() const {
612 const CPDF_Array* pArray = ToArray(GetFieldAttr(m_pDict.Get(), "Opt"));
613 return pArray ? pArray->size() : 0;
614 }
615
GetOptionText(int index,int sub_index) const616 WideString CPDF_FormField::GetOptionText(int index, int sub_index) const {
617 const CPDF_Array* pArray = ToArray(GetFieldAttr(m_pDict.Get(), "Opt"));
618 if (!pArray)
619 return WideString();
620
621 const CPDF_Object* pOption = pArray->GetDirectObjectAt(index);
622 if (!pOption)
623 return WideString();
624 if (const CPDF_Array* pOptionArray = pOption->AsArray())
625 pOption = pOptionArray->GetDirectObjectAt(sub_index);
626
627 const CPDF_String* pString = ToString(pOption);
628 return pString ? pString->GetUnicodeText() : WideString();
629 }
630
GetOptionLabel(int index) const631 WideString CPDF_FormField::GetOptionLabel(int index) const {
632 return GetOptionText(index, 1);
633 }
634
GetOptionValue(int index) const635 WideString CPDF_FormField::GetOptionValue(int index) const {
636 return GetOptionText(index, 0);
637 }
638
FindOption(const WideString & csOptValue) const639 int CPDF_FormField::FindOption(const WideString& csOptValue) const {
640 for (int i = 0; i < CountOptions(); i++) {
641 if (GetOptionValue(i) == csOptValue)
642 return i;
643 }
644 return -1;
645 }
646
CheckControl(int iControlIndex,bool bChecked,NotificationOption notify)647 bool CPDF_FormField::CheckControl(int iControlIndex,
648 bool bChecked,
649 NotificationOption notify) {
650 ASSERT(GetType() == kCheckBox || GetType() == kRadioButton);
651 CPDF_FormControl* pControl = GetControl(iControlIndex);
652 if (!pControl)
653 return false;
654 if (!bChecked && pControl->IsChecked() == bChecked)
655 return false;
656
657 const WideString csWExport = pControl->GetExportValue();
658 int iCount = CountControls();
659 for (int i = 0; i < iCount; i++) {
660 CPDF_FormControl* pCtrl = GetControl(i);
661 if (m_bIsUnison) {
662 WideString csEValue = pCtrl->GetExportValue();
663 if (csEValue == csWExport) {
664 if (pCtrl->GetOnStateName() == pControl->GetOnStateName())
665 pCtrl->CheckControl(bChecked);
666 else if (bChecked)
667 pCtrl->CheckControl(false);
668 } else if (bChecked) {
669 pCtrl->CheckControl(false);
670 }
671 } else {
672 if (i == iControlIndex)
673 pCtrl->CheckControl(bChecked);
674 else if (bChecked)
675 pCtrl->CheckControl(false);
676 }
677 }
678
679 const CPDF_Object* pOpt = GetFieldAttr(m_pDict.Get(), "Opt");
680 if (!ToArray(pOpt)) {
681 ByteString csBExport = PDF_EncodeText(csWExport);
682 if (bChecked) {
683 m_pDict->SetNewFor<CPDF_Name>(pdfium::form_fields::kV, csBExport);
684 } else {
685 ByteString csV;
686 const CPDF_Object* pV = GetValueObject();
687 if (pV)
688 csV = pV->GetString();
689 if (csV == csBExport)
690 m_pDict->SetNewFor<CPDF_Name>(pdfium::form_fields::kV, "Off");
691 }
692 } else if (bChecked) {
693 m_pDict->SetNewFor<CPDF_Name>(pdfium::form_fields::kV,
694 ByteString::Format("%d", iControlIndex));
695 }
696 if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify())
697 m_pForm->GetFormNotify()->AfterCheckedStatusChange(this);
698 return true;
699 }
700
GetCheckValue(bool bDefault) const701 WideString CPDF_FormField::GetCheckValue(bool bDefault) const {
702 ASSERT(GetType() == kCheckBox || GetType() == kRadioButton);
703 WideString csExport = L"Off";
704 int iCount = CountControls();
705 for (int i = 0; i < iCount; i++) {
706 CPDF_FormControl* pControl = GetControl(i);
707 bool bChecked =
708 bDefault ? pControl->IsDefaultChecked() : pControl->IsChecked();
709 if (bChecked) {
710 csExport = pControl->GetExportValue();
711 break;
712 }
713 }
714 return csExport;
715 }
716
SetCheckValue(const WideString & value,bool bDefault,NotificationOption notify)717 bool CPDF_FormField::SetCheckValue(const WideString& value,
718 bool bDefault,
719 NotificationOption notify) {
720 ASSERT(GetType() == kCheckBox || GetType() == kRadioButton);
721 int iCount = CountControls();
722 for (int i = 0; i < iCount; i++) {
723 CPDF_FormControl* pControl = GetControl(i);
724 WideString csExport = pControl->GetExportValue();
725 bool val = csExport == value;
726 if (!bDefault) {
727 CheckControl(GetControlIndex(pControl), val,
728 NotificationOption::kDoNotNotify);
729 }
730 if (val)
731 break;
732 }
733 if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify())
734 m_pForm->GetFormNotify()->AfterCheckedStatusChange(this);
735 return true;
736 }
737
GetTopVisibleIndex() const738 int CPDF_FormField::GetTopVisibleIndex() const {
739 const CPDF_Object* pObj = GetFieldAttr(m_pDict.Get(), "TI");
740 return pObj ? pObj->GetInteger() : 0;
741 }
742
CountSelectedOptions() const743 int CPDF_FormField::CountSelectedOptions() const {
744 const CPDF_Array* pArray = ToArray(GetSelectedIndicesObject());
745 return pArray ? pArray->size() : 0;
746 }
747
GetSelectedOptionIndex(int index) const748 int CPDF_FormField::GetSelectedOptionIndex(int index) const {
749 const CPDF_Array* pArray = ToArray(GetSelectedIndicesObject());
750 if (!pArray)
751 return -1;
752
753 int iCount = pArray->size();
754 if (iCount < 0 || index >= iCount)
755 return -1;
756 return pArray->GetIntegerAt(index);
757 }
758
IsOptionSelected(int iOptIndex) const759 bool CPDF_FormField::IsOptionSelected(int iOptIndex) const {
760 const CPDF_Array* pArray = ToArray(GetSelectedIndicesObject());
761 if (!pArray)
762 return false;
763
764 CPDF_ArrayLocker locker(pArray);
765 for (const auto& pObj : locker) {
766 if (pObj->GetInteger() == iOptIndex)
767 return true;
768 }
769 return false;
770 }
771
SelectOption(int iOptIndex,bool bSelected,NotificationOption notify)772 bool CPDF_FormField::SelectOption(int iOptIndex,
773 bool bSelected,
774 NotificationOption notify) {
775 CPDF_Array* pArray = m_pDict->GetArrayFor("I");
776 if (!pArray) {
777 if (!bSelected)
778 return true;
779
780 pArray = m_pDict->SetNewFor<CPDF_Array>("I");
781 }
782
783 bool bReturn = false;
784 for (size_t i = 0; i < pArray->size(); i++) {
785 int iFind = pArray->GetIntegerAt(i);
786 if (iFind == iOptIndex) {
787 if (bSelected)
788 return true;
789
790 if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify()) {
791 WideString csValue = GetOptionLabel(iOptIndex);
792 if (!NotifyListOrComboBoxBeforeChange(csValue))
793 return false;
794 }
795 pArray->RemoveAt(i);
796 bReturn = true;
797 break;
798 }
799
800 if (iFind > iOptIndex) {
801 if (!bSelected)
802 continue;
803
804 if (notify == NotificationOption::kNotify && m_pForm->GetFormNotify()) {
805 WideString csValue = GetOptionLabel(iOptIndex);
806 if (!NotifyListOrComboBoxBeforeChange(csValue))
807 return false;
808 }
809 pArray->InsertNewAt<CPDF_Number>(i, iOptIndex);
810 bReturn = true;
811 break;
812 }
813 }
814 if (!bReturn) {
815 if (bSelected)
816 pArray->AddNew<CPDF_Number>(iOptIndex);
817 if (pArray->IsEmpty())
818 m_pDict->RemoveFor("I");
819 }
820 if (notify == NotificationOption::kNotify)
821 NotifyListOrComboBoxAfterChange();
822
823 return true;
824 }
825
LoadDA()826 void CPDF_FormField::LoadDA() {
827 CPDF_Dictionary* pFormDict = m_pForm->GetFormDict();
828 if (!pFormDict)
829 return;
830
831 ByteString DA;
832 if (const CPDF_Object* pObj = GetFieldAttr(m_pDict.Get(), "DA"))
833 DA = pObj->GetString();
834
835 if (DA.IsEmpty())
836 DA = pFormDict->GetStringFor("DA");
837
838 if (DA.IsEmpty())
839 return;
840
841 CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR");
842 if (!pDR)
843 return;
844
845 CPDF_Dictionary* pFont = pDR->GetDictFor("Font");
846 if (!ValidateFontResourceDict(pFont))
847 return;
848
849 CPDF_DefaultAppearance appearance(DA);
850 Optional<ByteString> font_name = appearance.GetFont(&m_FontSize);
851 if (!font_name)
852 return;
853
854 CPDF_Dictionary* pFontDict = pFont->GetDictFor(*font_name);
855 if (!pFontDict)
856 return;
857
858 auto* pData = CPDF_DocPageData::FromDocument(m_pForm->GetDocument());
859 m_pFont = pData->GetFont(pFontDict);
860 }
861
NotifyBeforeSelectionChange(const WideString & value)862 bool CPDF_FormField::NotifyBeforeSelectionChange(const WideString& value) {
863 auto* pNotify = m_pForm->GetFormNotify();
864 return !pNotify || pNotify->BeforeSelectionChange(this, value);
865 }
866
NotifyAfterSelectionChange()867 void CPDF_FormField::NotifyAfterSelectionChange() {
868 auto* pNotify = m_pForm->GetFormNotify();
869 if (pNotify)
870 pNotify->AfterSelectionChange(this);
871 }
872
NotifyBeforeValueChange(const WideString & value)873 bool CPDF_FormField::NotifyBeforeValueChange(const WideString& value) {
874 auto* pNotify = m_pForm->GetFormNotify();
875 return !pNotify || pNotify->BeforeValueChange(this, value);
876 }
877
NotifyAfterValueChange()878 void CPDF_FormField::NotifyAfterValueChange() {
879 auto* pNotify = m_pForm->GetFormNotify();
880 if (pNotify)
881 pNotify->AfterValueChange(this);
882 }
883
NotifyListOrComboBoxBeforeChange(const WideString & value)884 bool CPDF_FormField::NotifyListOrComboBoxBeforeChange(const WideString& value) {
885 switch (GetType()) {
886 case kListBox:
887 return NotifyBeforeSelectionChange(value);
888 case kComboBox:
889 return NotifyBeforeValueChange(value);
890 default:
891 return true;
892 }
893 }
894
NotifyListOrComboBoxAfterChange()895 void CPDF_FormField::NotifyListOrComboBoxAfterChange() {
896 switch (GetType()) {
897 case kListBox:
898 NotifyAfterSelectionChange();
899 break;
900 case kComboBox:
901 NotifyAfterValueChange();
902 break;
903 default:
904 break;
905 }
906 }
907
GetDefaultValueObject() const908 const CPDF_Object* CPDF_FormField::GetDefaultValueObject() const {
909 return GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kDV);
910 }
911
GetValueObject() const912 const CPDF_Object* CPDF_FormField::GetValueObject() const {
913 return GetFieldAttr(m_pDict.Get(), pdfium::form_fields::kV);
914 }
915
GetSelectedIndicesObject() const916 const CPDF_Object* CPDF_FormField::GetSelectedIndicesObject() const {
917 ASSERT(GetType() == kComboBox || GetType() == kListBox);
918 return GetFieldAttr(m_pDict.Get(), "I");
919 }
920
GetValueOrSelectedIndicesObject() const921 const CPDF_Object* CPDF_FormField::GetValueOrSelectedIndicesObject() const {
922 ASSERT(GetType() == kComboBox || GetType() == kListBox);
923 const CPDF_Object* pValue = GetValueObject();
924 return pValue ? pValue : GetSelectedIndicesObject();
925 }
926
GetControls() const927 const std::vector<UnownedPtr<CPDF_FormControl>>& CPDF_FormField::GetControls()
928 const {
929 return m_pForm->GetControlsForField(this);
930 }
931