1 // Copyright 2014 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_formfield.h"
8
9 #include <map>
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_name.h"
21 #include "core/fpdfapi/parser/cpdf_number.h"
22 #include "core/fpdfapi/parser/cpdf_stream.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_generateap.h"
29 #include "core/fpdfdoc/cpdf_interactiveform.h"
30 #include "core/fxcrt/stl_util.h"
31 #include "third_party/base/check.h"
32 #include "third_party/base/containers/contains.h"
33
34 namespace {
35
GetFieldAttrRecursive(const CPDF_Dictionary * pFieldDict,const ByteString & name,int nLevel)36 RetainPtr<const CPDF_Object> GetFieldAttrRecursive(
37 const CPDF_Dictionary* pFieldDict,
38 const ByteString& name,
39 int nLevel) {
40 static constexpr int kGetFieldMaxRecursion = 32;
41 if (!pFieldDict || nLevel > kGetFieldMaxRecursion)
42 return nullptr;
43
44 RetainPtr<const CPDF_Object> pAttr = pFieldDict->GetDirectObjectFor(name);
45 if (pAttr)
46 return pAttr;
47
48 return GetFieldAttrRecursive(
49 pFieldDict->GetDictFor(pdfium::form_fields::kParent).Get(), name,
50 nLevel + 1);
51 }
52
53 } // namespace
54
55 // static
IntToFormFieldType(int value)56 absl::optional<FormFieldType> CPDF_FormField::IntToFormFieldType(int value) {
57 if (value >= static_cast<int>(FormFieldType::kUnknown) &&
58 value < static_cast<int>(kFormFieldTypeCount)) {
59 return static_cast<FormFieldType>(value);
60 }
61 return absl::nullopt;
62 }
63
64 // static
GetFieldAttrForDict(const CPDF_Dictionary * pFieldDict,const ByteString & name)65 RetainPtr<const CPDF_Object> CPDF_FormField::GetFieldAttrForDict(
66 const CPDF_Dictionary* pFieldDict,
67 const ByteString& name) {
68 return GetFieldAttrRecursive(pFieldDict, name, 0);
69 }
70
71 // static
GetMutableFieldAttrForDict(CPDF_Dictionary * pFieldDict,const ByteString & name)72 RetainPtr<CPDF_Object> CPDF_FormField::GetMutableFieldAttrForDict(
73 CPDF_Dictionary* pFieldDict,
74 const ByteString& name) {
75 return pdfium::WrapRetain(const_cast<CPDF_Object*>(
76 GetFieldAttrRecursive(pFieldDict, name, 0).Get()));
77 }
78
79 // static
GetFullNameForDict(const CPDF_Dictionary * pFieldDict)80 WideString CPDF_FormField::GetFullNameForDict(
81 const CPDF_Dictionary* pFieldDict) {
82 WideString full_name;
83 std::set<const CPDF_Dictionary*> visited;
84 const CPDF_Dictionary* pLevel = pFieldDict;
85 while (pLevel) {
86 visited.insert(pLevel);
87 WideString short_name = pLevel->GetUnicodeTextFor(pdfium::form_fields::kT);
88 if (!short_name.IsEmpty()) {
89 if (full_name.IsEmpty())
90 full_name = std::move(short_name);
91 else
92 full_name = short_name + L'.' + full_name;
93 }
94 pLevel = pLevel->GetDictFor(pdfium::form_fields::kParent).Get();
95 if (pdfium::Contains(visited, pLevel))
96 break;
97 }
98 return full_name;
99 }
100
CPDF_FormField(CPDF_InteractiveForm * pForm,RetainPtr<CPDF_Dictionary> pDict)101 CPDF_FormField::CPDF_FormField(CPDF_InteractiveForm* pForm,
102 RetainPtr<CPDF_Dictionary> pDict)
103 : m_pForm(pForm), m_pDict(std::move(pDict)) {
104 InitFieldFlags();
105 }
106
107 CPDF_FormField::~CPDF_FormField() = default;
108
InitFieldFlags()109 void CPDF_FormField::InitFieldFlags() {
110 RetainPtr<const CPDF_Object> ft_attr =
111 GetFieldAttrInternal(pdfium::form_fields::kFT);
112 ByteString type_name = ft_attr ? ft_attr->GetString() : ByteString();
113 uint32_t flags = GetFieldFlags();
114 m_bRequired = flags & pdfium::form_flags::kRequired;
115 m_bNoExport = flags & pdfium::form_flags::kNoExport;
116
117 if (type_name == pdfium::form_fields::kBtn) {
118 if (flags & pdfium::form_flags::kButtonRadio) {
119 m_Type = kRadioButton;
120 m_bIsUnison = flags & pdfium::form_flags::kButtonRadiosInUnison;
121 } else if (flags & pdfium::form_flags::kButtonPushbutton) {
122 m_Type = kPushButton;
123 } else {
124 m_Type = kCheckBox;
125 m_bIsUnison = true;
126 }
127 } else if (type_name == pdfium::form_fields::kTx) {
128 if (flags & pdfium::form_flags::kTextFileSelect)
129 m_Type = kFile;
130 else if (flags & pdfium::form_flags::kTextRichText)
131 m_Type = kRichText;
132 else
133 m_Type = kText;
134 } else if (type_name == pdfium::form_fields::kCh) {
135 if (flags & pdfium::form_flags::kChoiceCombo) {
136 m_Type = kComboBox;
137 } else {
138 m_Type = kListBox;
139 m_bIsMultiSelectListBox = flags & pdfium::form_flags::kChoiceMultiSelect;
140 }
141 m_bUseSelectedIndices = UseSelectedIndicesObject();
142 } else if (type_name == pdfium::form_fields::kSig) {
143 m_Type = kSign;
144 }
145 }
146
GetFullName() const147 WideString CPDF_FormField::GetFullName() const {
148 return GetFullNameForDict(m_pDict.Get());
149 }
150
GetFieldAttr(const ByteString & name) const151 RetainPtr<const CPDF_Object> CPDF_FormField::GetFieldAttr(
152 const ByteString& name) const {
153 return GetFieldAttrInternal(name);
154 }
155
GetFieldDict() const156 RetainPtr<const CPDF_Dictionary> CPDF_FormField::GetFieldDict() const {
157 return pdfium::WrapRetain(GetFieldDictInternal());
158 }
159
ResetField()160 bool CPDF_FormField::ResetField() {
161 switch (m_Type) {
162 case kCheckBox:
163 case kRadioButton: {
164 int iCount = CountControls();
165 // TODO(weili): Check whether anything special needs to be done for
166 // |m_bIsUnison|.
167 for (int i = 0; i < iCount; i++) {
168 CheckControl(i, GetControl(i)->IsDefaultChecked(),
169 NotificationOption::kDoNotNotify);
170 }
171 m_pForm->NotifyAfterCheckedStatusChange(this);
172 break;
173 }
174 case kComboBox:
175 case kListBox: {
176 ClearSelection(NotificationOption::kDoNotNotify);
177 WideString csValue;
178 int iIndex = GetDefaultSelectedItem();
179 if (iIndex >= 0)
180 csValue = GetOptionLabel(iIndex);
181 if (!NotifyListOrComboBoxBeforeChange(csValue)) {
182 return false;
183 }
184 SetItemSelection(iIndex, NotificationOption::kDoNotNotify);
185 NotifyListOrComboBoxAfterChange();
186 break;
187 }
188 case kText:
189 case kRichText:
190 case kFile:
191 default: {
192 WideString csDValue;
193 WideString csValue;
194 {
195 // Limit scope of |pDV| and |pV| because they may get invalidated
196 // during notification below.
197 RetainPtr<const CPDF_Object> pDV = GetDefaultValueObject();
198 if (pDV)
199 csDValue = pDV->GetUnicodeText();
200
201 RetainPtr<const CPDF_Object> pV = GetValueObject();
202 if (pV)
203 csValue = pV->GetUnicodeText();
204 }
205
206 bool bHasRV = !!GetFieldAttrInternal(pdfium::form_fields::kRV);
207 if (!bHasRV && (csDValue == csValue))
208 return false;
209
210 if (!m_pForm->NotifyBeforeValueChange(this, csDValue))
211 return false;
212
213 {
214 // Limit scope of |pDV| because it may get invalidated during
215 // notification below.
216 RetainPtr<const CPDF_Object> pDV = GetDefaultValueObject();
217 if (pDV) {
218 RetainPtr<CPDF_Object> pClone = pDV->Clone();
219 if (!pClone)
220 return false;
221
222 m_pDict->SetFor(pdfium::form_fields::kV, std::move(pClone));
223 if (bHasRV) {
224 m_pDict->SetFor(pdfium::form_fields::kRV, pDV->Clone());
225 }
226 } else {
227 m_pDict->RemoveFor(pdfium::form_fields::kV);
228 m_pDict->RemoveFor(pdfium::form_fields::kRV);
229 }
230 }
231 m_pForm->NotifyAfterValueChange(this);
232 break;
233 }
234 }
235 return true;
236 }
237
CountControls() const238 int CPDF_FormField::CountControls() const {
239 return fxcrt::CollectionSize<int>(GetControls());
240 }
241
GetControl(int index) const242 CPDF_FormControl* CPDF_FormField::GetControl(int index) const {
243 return GetControls()[index];
244 }
245
GetControlIndex(const CPDF_FormControl * pControl) const246 int CPDF_FormField::GetControlIndex(const CPDF_FormControl* pControl) const {
247 if (!pControl)
248 return -1;
249
250 const auto& controls = GetControls();
251 auto it = std::find(controls.begin(), controls.end(), pControl);
252 if (it == controls.end())
253 return -1;
254
255 return pdfium::base::checked_cast<int>(it - controls.begin());
256 }
257
GetFieldType() const258 FormFieldType CPDF_FormField::GetFieldType() const {
259 switch (m_Type) {
260 case kPushButton:
261 return FormFieldType::kPushButton;
262 case kCheckBox:
263 return FormFieldType::kCheckBox;
264 case kRadioButton:
265 return FormFieldType::kRadioButton;
266 case kComboBox:
267 return FormFieldType::kComboBox;
268 case kListBox:
269 return FormFieldType::kListBox;
270 case kText:
271 case kRichText:
272 case kFile:
273 return FormFieldType::kTextField;
274 case kSign:
275 return FormFieldType::kSignature;
276 default:
277 return FormFieldType::kUnknown;
278 }
279 }
280
GetAdditionalAction() const281 CPDF_AAction CPDF_FormField::GetAdditionalAction() const {
282 RetainPtr<const CPDF_Object> pObj =
283 GetFieldAttrInternal(pdfium::form_fields::kAA);
284 return CPDF_AAction(pObj ? pObj->GetDict() : nullptr);
285 }
286
GetAlternateName() const287 WideString CPDF_FormField::GetAlternateName() const {
288 RetainPtr<const CPDF_Object> pObj =
289 GetFieldAttrInternal(pdfium::form_fields::kTU);
290 return pObj ? pObj->GetUnicodeText() : WideString();
291 }
292
GetMappingName() const293 WideString CPDF_FormField::GetMappingName() const {
294 RetainPtr<const CPDF_Object> pObj =
295 GetFieldAttrInternal(pdfium::form_fields::kTM);
296 return pObj ? pObj->GetUnicodeText() : WideString();
297 }
298
GetFieldFlags() const299 uint32_t CPDF_FormField::GetFieldFlags() const {
300 RetainPtr<const CPDF_Object> pObj =
301 GetFieldAttrInternal(pdfium::form_fields::kFf);
302 return pObj ? pObj->GetInteger() : 0;
303 }
304
SetFieldFlags(uint32_t dwFlags)305 void CPDF_FormField::SetFieldFlags(uint32_t dwFlags) {
306 m_pDict->SetNewFor<CPDF_Number>(pdfium::form_fields::kFf,
307 static_cast<int>(dwFlags));
308 }
309
GetValue(bool bDefault) const310 WideString CPDF_FormField::GetValue(bool bDefault) const {
311 if (GetType() == kCheckBox || GetType() == kRadioButton)
312 return GetCheckValue(bDefault);
313
314 RetainPtr<const CPDF_Object> pValue =
315 bDefault ? GetDefaultValueObject() : GetValueObject();
316 if (!pValue) {
317 if (!bDefault && m_Type != kText)
318 pValue = GetDefaultValueObject();
319 if (!pValue)
320 return WideString();
321 }
322
323 switch (pValue->GetType()) {
324 case CPDF_Object::kString:
325 case CPDF_Object::kStream:
326 return pValue->GetUnicodeText();
327 case CPDF_Object::kArray: {
328 RetainPtr<const CPDF_Object> pNewValue =
329 pValue->AsArray()->GetDirectObjectAt(0);
330 if (pNewValue)
331 return pNewValue->GetUnicodeText();
332 break;
333 }
334 default:
335 break;
336 }
337 return WideString();
338 }
339
GetValue() const340 WideString CPDF_FormField::GetValue() const {
341 return GetValue(false);
342 }
343
GetDefaultValue() const344 WideString CPDF_FormField::GetDefaultValue() const {
345 return GetValue(true);
346 }
347
SetValue(const WideString & value,bool bDefault,NotificationOption notify)348 bool CPDF_FormField::SetValue(const WideString& value,
349 bool bDefault,
350 NotificationOption notify) {
351 switch (m_Type) {
352 case kCheckBox:
353 case kRadioButton: {
354 SetCheckValue(value, bDefault, notify);
355 return true;
356 }
357 case kFile:
358 case kRichText:
359 case kText:
360 case kComboBox: {
361 WideString csValue = value;
362 if (notify == NotificationOption::kNotify &&
363 !m_pForm->NotifyBeforeValueChange(this, csValue)) {
364 return false;
365 }
366 ByteString key(bDefault ? pdfium::form_fields::kDV
367 : pdfium::form_fields::kV);
368 m_pDict->SetNewFor<CPDF_String>(key, csValue.AsStringView());
369 int iIndex = FindOption(csValue);
370 if (iIndex < 0) {
371 if (m_Type == kRichText && !bDefault) {
372 m_pDict->SetFor(pdfium::form_fields::kRV,
373 m_pDict->GetObjectFor(key)->Clone());
374 }
375 m_pDict->RemoveFor("I");
376 } else {
377 if (!bDefault) {
378 ClearSelection(NotificationOption::kDoNotNotify);
379 SetItemSelection(iIndex, NotificationOption::kDoNotNotify);
380 }
381 }
382 if (notify == NotificationOption::kNotify)
383 m_pForm->NotifyAfterValueChange(this);
384 break;
385 }
386 case kListBox: {
387 int iIndex = FindOption(value);
388 if (iIndex < 0)
389 return false;
390
391 if (bDefault && iIndex == GetDefaultSelectedItem())
392 return false;
393
394 if (notify == NotificationOption::kNotify &&
395 !m_pForm->NotifyBeforeSelectionChange(this, value)) {
396 return false;
397 }
398 if (!bDefault) {
399 ClearSelection(NotificationOption::kDoNotNotify);
400 SetItemSelection(iIndex, NotificationOption::kDoNotNotify);
401 }
402 if (notify == NotificationOption::kNotify)
403 m_pForm->NotifyAfterSelectionChange(this);
404 break;
405 }
406 default:
407 break;
408 }
409 return true;
410 }
411
SetValue(const WideString & value,NotificationOption notify)412 bool CPDF_FormField::SetValue(const WideString& value,
413 NotificationOption notify) {
414 return SetValue(value, false, notify);
415 }
416
GetMaxLen() const417 int CPDF_FormField::GetMaxLen() const {
418 RetainPtr<const CPDF_Object> pObj = GetFieldAttrInternal("MaxLen");
419 if (pObj)
420 return pObj->GetInteger();
421
422 for (auto& pControl : GetControls()) {
423 if (!pControl)
424 continue;
425
426 RetainPtr<const CPDF_Dictionary> pWidgetDict = pControl->GetWidgetDict();
427 if (pWidgetDict->KeyExist("MaxLen"))
428 return pWidgetDict->GetIntegerFor("MaxLen");
429 }
430 return 0;
431 }
432
CountSelectedItems() const433 int CPDF_FormField::CountSelectedItems() const {
434 const CPDF_Object* pValue = GetValueOrSelectedIndicesObject();
435 if (!pValue)
436 return 0;
437
438 if (pValue->IsString() || pValue->IsNumber())
439 return pValue->GetString().IsEmpty() ? 0 : 1;
440 const CPDF_Array* pArray = pValue->AsArray();
441 return pArray ? fxcrt::CollectionSize<int>(*pArray) : 0;
442 }
443
GetSelectedIndex(int index) const444 int CPDF_FormField::GetSelectedIndex(int index) const {
445 const CPDF_Object* pValue = GetValueOrSelectedIndicesObject();
446 if (!pValue)
447 return -1;
448
449 if (pValue->IsNumber())
450 return pValue->GetInteger();
451
452 WideString sel_value;
453 if (pValue->IsString()) {
454 if (index != 0)
455 return -1;
456 sel_value = pValue->GetUnicodeText();
457 } else {
458 const CPDF_Array* pArray = pValue->AsArray();
459 if (!pArray || index < 0)
460 return -1;
461
462 RetainPtr<const CPDF_Object> elementValue =
463 pArray->GetDirectObjectAt(index);
464 sel_value = elementValue ? elementValue->GetUnicodeText() : WideString();
465 }
466 if (index < CountSelectedOptions()) {
467 int iOptIndex = GetSelectedOptionIndex(index);
468 WideString csOpt = GetOptionValue(iOptIndex);
469 if (csOpt == sel_value)
470 return iOptIndex;
471 }
472 for (int i = 0; i < CountOptions(); i++) {
473 if (sel_value == GetOptionValue(i))
474 return i;
475 }
476 return -1;
477 }
478
ClearSelection(NotificationOption notify)479 bool CPDF_FormField::ClearSelection(NotificationOption notify) {
480 if (notify == NotificationOption::kNotify) {
481 WideString csValue;
482 int iIndex = GetSelectedIndex(0);
483 if (iIndex >= 0)
484 csValue = GetOptionLabel(iIndex);
485 if (!NotifyListOrComboBoxBeforeChange(csValue))
486 return false;
487 }
488 m_pDict->RemoveFor(pdfium::form_fields::kV);
489 m_pDict->RemoveFor("I");
490 if (notify == NotificationOption::kNotify)
491 NotifyListOrComboBoxAfterChange();
492 return true;
493 }
494
IsItemSelected(int index) const495 bool CPDF_FormField::IsItemSelected(int index) const {
496 DCHECK(GetType() == kComboBox || GetType() == kListBox);
497 if (index < 0 || index >= CountOptions())
498 return false;
499
500 // First consider the /I entry if it is valid, then fall back to the /V entry.
501 return m_bUseSelectedIndices ? IsSelectedIndex(index)
502 : IsSelectedOption(GetOptionValue(index));
503 }
504
SetItemSelection(int index,NotificationOption notify)505 bool CPDF_FormField::SetItemSelection(int index, NotificationOption notify) {
506 DCHECK(GetType() == kComboBox || GetType() == kListBox);
507 if (index < 0 || index >= CountOptions())
508 return false;
509
510 WideString opt_value = GetOptionValue(index);
511 if (notify == NotificationOption::kNotify &&
512 !NotifyListOrComboBoxBeforeChange(opt_value)) {
513 return false;
514 }
515
516 SetItemSelectionSelected(index, opt_value);
517
518 // UseSelectedIndicesObject() has a non-trivial linearithmic run-time, so run
519 // only if necessary.
520 if (!m_bUseSelectedIndices)
521 m_bUseSelectedIndices = UseSelectedIndicesObject();
522
523 if (notify == NotificationOption::kNotify)
524 NotifyListOrComboBoxAfterChange();
525 return true;
526 }
527
SetItemSelectionSelected(int index,const WideString & opt_value)528 void CPDF_FormField::SetItemSelectionSelected(int index,
529 const WideString& opt_value) {
530 if (GetType() != kListBox) {
531 m_pDict->SetNewFor<CPDF_String>(pdfium::form_fields::kV,
532 opt_value.AsStringView());
533 auto pI = m_pDict->SetNewFor<CPDF_Array>("I");
534 pI->AppendNew<CPDF_Number>(index);
535 return;
536 }
537
538 SelectOption(index);
539 if (!m_bIsMultiSelectListBox) {
540 m_pDict->SetNewFor<CPDF_String>(pdfium::form_fields::kV,
541 opt_value.AsStringView());
542 return;
543 }
544
545 auto pArray = m_pDict->SetNewFor<CPDF_Array>(pdfium::form_fields::kV);
546 for (int i = 0; i < CountOptions(); i++) {
547 if (i == index || IsItemSelected(i))
548 pArray->AppendNew<CPDF_String>(GetOptionValue(i).AsStringView());
549 }
550 }
551
GetDefaultSelectedItem() const552 int CPDF_FormField::GetDefaultSelectedItem() const {
553 DCHECK(GetType() == kComboBox || GetType() == kListBox);
554 RetainPtr<const CPDF_Object> pValue = GetDefaultValueObject();
555 if (!pValue)
556 return -1;
557 WideString csDV = pValue->GetUnicodeText();
558 if (csDV.IsEmpty())
559 return -1;
560 for (int i = 0; i < CountOptions(); i++) {
561 if (csDV == GetOptionValue(i))
562 return i;
563 }
564 return -1;
565 }
566
CountOptions() const567 int CPDF_FormField::CountOptions() const {
568 RetainPtr<const CPDF_Array> pArray = ToArray(GetFieldAttrInternal("Opt"));
569 return pArray ? fxcrt::CollectionSize<int>(*pArray) : 0;
570 }
571
GetOptionText(int index,int sub_index) const572 WideString CPDF_FormField::GetOptionText(int index, int sub_index) const {
573 RetainPtr<const CPDF_Array> pArray = ToArray(GetFieldAttrInternal("Opt"));
574 if (!pArray)
575 return WideString();
576
577 RetainPtr<const CPDF_Object> pOption = pArray->GetDirectObjectAt(index);
578 if (!pOption)
579 return WideString();
580
581 const CPDF_Array* pOptionArray = pOption->AsArray();
582 if (pOptionArray)
583 pOption = pOptionArray->GetDirectObjectAt(sub_index);
584
585 if (!pOption)
586 return WideString();
587
588 const CPDF_String* pString = pOption->AsString();
589 return pString ? pString->GetUnicodeText() : WideString();
590 }
591
GetOptionLabel(int index) const592 WideString CPDF_FormField::GetOptionLabel(int index) const {
593 return GetOptionText(index, 1);
594 }
595
GetOptionValue(int index) const596 WideString CPDF_FormField::GetOptionValue(int index) const {
597 return GetOptionText(index, 0);
598 }
599
FindOption(const WideString & csOptValue) const600 int CPDF_FormField::FindOption(const WideString& csOptValue) const {
601 for (int i = 0; i < CountOptions(); i++) {
602 if (GetOptionValue(i) == csOptValue)
603 return i;
604 }
605 return -1;
606 }
607
CheckControl(int iControlIndex,bool bChecked,NotificationOption notify)608 bool CPDF_FormField::CheckControl(int iControlIndex,
609 bool bChecked,
610 NotificationOption notify) {
611 DCHECK(GetType() == kCheckBox || GetType() == kRadioButton);
612 CPDF_FormControl* pControl = GetControl(iControlIndex);
613 if (!pControl)
614 return false;
615 if (!bChecked && pControl->IsChecked() == bChecked)
616 return false;
617
618 const WideString csWExport = pControl->GetExportValue();
619 int iCount = CountControls();
620 for (int i = 0; i < iCount; i++) {
621 CPDF_FormControl* pCtrl = GetControl(i);
622 if (m_bIsUnison) {
623 WideString csEValue = pCtrl->GetExportValue();
624 if (csEValue == csWExport) {
625 if (pCtrl->GetOnStateName() == pControl->GetOnStateName())
626 pCtrl->CheckControl(bChecked);
627 else if (bChecked)
628 pCtrl->CheckControl(false);
629 } else if (bChecked) {
630 pCtrl->CheckControl(false);
631 }
632 } else {
633 if (i == iControlIndex)
634 pCtrl->CheckControl(bChecked);
635 else if (bChecked)
636 pCtrl->CheckControl(false);
637 }
638 }
639
640 RetainPtr<const CPDF_Object> pOpt = GetFieldAttrInternal("Opt");
641 if (!ToArray(pOpt)) {
642 ByteString csBExport = PDF_EncodeText(csWExport.AsStringView());
643 if (bChecked) {
644 m_pDict->SetNewFor<CPDF_Name>(pdfium::form_fields::kV, csBExport);
645 } else {
646 ByteString csV;
647 const CPDF_Object* pV = GetValueObject();
648 if (pV)
649 csV = pV->GetString();
650 if (csV == csBExport)
651 m_pDict->SetNewFor<CPDF_Name>(pdfium::form_fields::kV, "Off");
652 }
653 } else if (bChecked) {
654 m_pDict->SetNewFor<CPDF_Name>(pdfium::form_fields::kV,
655 ByteString::FormatInteger(iControlIndex));
656 }
657 if (notify == NotificationOption::kNotify)
658 m_pForm->NotifyAfterCheckedStatusChange(this);
659 return true;
660 }
661
GetCheckValue(bool bDefault) const662 WideString CPDF_FormField::GetCheckValue(bool bDefault) const {
663 DCHECK(GetType() == kCheckBox || GetType() == kRadioButton);
664 WideString csExport = L"Off";
665 int iCount = CountControls();
666 for (int i = 0; i < iCount; i++) {
667 CPDF_FormControl* pControl = GetControl(i);
668 bool bChecked =
669 bDefault ? pControl->IsDefaultChecked() : pControl->IsChecked();
670 if (bChecked) {
671 csExport = pControl->GetExportValue();
672 break;
673 }
674 }
675 return csExport;
676 }
677
SetCheckValue(const WideString & value,bool bDefault,NotificationOption notify)678 bool CPDF_FormField::SetCheckValue(const WideString& value,
679 bool bDefault,
680 NotificationOption notify) {
681 DCHECK(GetType() == kCheckBox || GetType() == kRadioButton);
682 int iCount = CountControls();
683 for (int i = 0; i < iCount; i++) {
684 CPDF_FormControl* pControl = GetControl(i);
685 WideString csExport = pControl->GetExportValue();
686 bool val = csExport == value;
687 if (!bDefault) {
688 CheckControl(GetControlIndex(pControl), val,
689 NotificationOption::kDoNotNotify);
690 }
691 if (val)
692 break;
693 }
694 if (notify == NotificationOption::kNotify)
695 m_pForm->NotifyAfterCheckedStatusChange(this);
696 return true;
697 }
698
GetTopVisibleIndex() const699 int CPDF_FormField::GetTopVisibleIndex() const {
700 RetainPtr<const CPDF_Object> pObj = GetFieldAttrInternal("TI");
701 return pObj ? pObj->GetInteger() : 0;
702 }
703
CountSelectedOptions() const704 int CPDF_FormField::CountSelectedOptions() const {
705 RetainPtr<const CPDF_Array> pArray = ToArray(GetSelectedIndicesObject());
706 return pArray ? fxcrt::CollectionSize<int>(*pArray) : 0;
707 }
708
GetSelectedOptionIndex(int index) const709 int CPDF_FormField::GetSelectedOptionIndex(int index) const {
710 if (index < 0)
711 return 0;
712
713 RetainPtr<const CPDF_Array> pArray = ToArray(GetSelectedIndicesObject());
714 if (!pArray)
715 return -1;
716
717 return index < fxcrt::CollectionSize<int>(*pArray)
718 ? pArray->GetIntegerAt(index)
719 : -1;
720 }
721
IsSelectedOption(const WideString & wsOptValue) const722 bool CPDF_FormField::IsSelectedOption(const WideString& wsOptValue) const {
723 RetainPtr<const CPDF_Object> pValueObject = GetValueObject();
724 if (!pValueObject)
725 return false;
726
727 const CPDF_Array* pValueArray = pValueObject->AsArray();
728 if (pValueArray) {
729 CPDF_ArrayLocker locker(pValueArray);
730 for (const auto& pObj : locker) {
731 if (pObj->IsString() && pObj->GetUnicodeText() == wsOptValue)
732 return true;
733 }
734 }
735
736 return pValueObject->IsString() &&
737 pValueObject->GetUnicodeText() == wsOptValue;
738 }
739
IsSelectedIndex(int iOptIndex) const740 bool CPDF_FormField::IsSelectedIndex(int iOptIndex) const {
741 RetainPtr<const CPDF_Object> pSelectedIndicesObject =
742 GetSelectedIndicesObject();
743 if (!pSelectedIndicesObject)
744 return false;
745
746 const CPDF_Array* pSelectedIndicesArray = pSelectedIndicesObject->AsArray();
747 if (pSelectedIndicesArray) {
748 CPDF_ArrayLocker locker(pSelectedIndicesArray);
749 for (const auto& pObj : locker) {
750 if (pObj->IsNumber() && pObj->GetInteger() == iOptIndex)
751 return true;
752 }
753 }
754
755 return pSelectedIndicesObject->IsNumber() &&
756 pSelectedIndicesObject->GetInteger() == iOptIndex;
757 }
758
SelectOption(int iOptIndex)759 void CPDF_FormField::SelectOption(int iOptIndex) {
760 RetainPtr<CPDF_Array> pArray = m_pDict->GetOrCreateArrayFor("I");
761 for (size_t i = 0; i < pArray->size(); i++) {
762 int iFind = pArray->GetIntegerAt(i);
763 if (iFind == iOptIndex)
764 return;
765
766 if (iFind > iOptIndex) {
767 pArray->InsertNewAt<CPDF_Number>(i, iOptIndex);
768 return;
769 }
770 }
771 pArray->AppendNew<CPDF_Number>(iOptIndex);
772 }
773
UseSelectedIndicesObject() const774 bool CPDF_FormField::UseSelectedIndicesObject() const {
775 DCHECK(GetType() == kComboBox || GetType() == kListBox);
776
777 RetainPtr<const CPDF_Object> pSelectedIndicesObject =
778 GetSelectedIndicesObject();
779 if (!pSelectedIndicesObject)
780 return false;
781
782 // If there's not value object, then just use the indices object.
783 RetainPtr<const CPDF_Object> pValueObject = GetValueObject();
784 if (!pValueObject)
785 return true;
786
787 // Verify that the selected indices object is either an array or a number and
788 // count the number of indices.
789 size_t selected_indices_size;
790 const CPDF_Array* pSelectedIndicesArray = pSelectedIndicesObject->AsArray();
791 if (pSelectedIndicesArray)
792 selected_indices_size = pSelectedIndicesArray->size();
793 else if (pSelectedIndicesObject->IsNumber())
794 selected_indices_size = 1;
795 else
796 return false;
797
798 // Verify that the number of values is equal to |selected_indices_size|. Then,
799 // count the number of occurrences of each of the distinct values in the
800 // values object.
801 std::map<WideString, size_t> values;
802 const CPDF_Array* pValueArray = pValueObject->AsArray();
803 if (pValueArray) {
804 if (pValueArray->size() != selected_indices_size)
805 return false;
806 CPDF_ArrayLocker locker(pValueArray);
807 for (const auto& pObj : locker) {
808 if (pObj->IsString())
809 values[pObj->GetUnicodeText()]++;
810 }
811 } else if (pValueObject->IsString()) {
812 if (selected_indices_size != 1)
813 return false;
814 values[pValueObject->GetUnicodeText()]++;
815 }
816
817 // Validate each index in the selected indices object. Then, verify that items
818 // identified by selected indices entry do not differ from those in the values
819 // entry of the field dictionary.
820 const int num_options = CountOptions();
821 if (pSelectedIndicesArray) {
822 CPDF_ArrayLocker locker(pSelectedIndicesArray);
823 for (const auto& pObj : locker) {
824 if (!pObj->IsNumber())
825 return false;
826
827 int index = pObj->GetInteger();
828 if (index < 0 || index >= num_options)
829 return false;
830
831 WideString wsOptValue = GetOptionValue(index);
832 auto it = values.find(wsOptValue);
833 if (it == values.end())
834 return false;
835
836 it->second--;
837 if (it->second == 0)
838 values.erase(it);
839 }
840
841 return values.empty();
842 }
843
844 DCHECK(pSelectedIndicesObject->IsNumber());
845 int index = pSelectedIndicesObject->GetInteger();
846 if (index < 0 || index >= num_options)
847 return false;
848
849 return pdfium::Contains(values, GetOptionValue(index));
850 }
851
NotifyListOrComboBoxBeforeChange(const WideString & value)852 bool CPDF_FormField::NotifyListOrComboBoxBeforeChange(const WideString& value) {
853 switch (GetType()) {
854 case kListBox:
855 return m_pForm->NotifyBeforeSelectionChange(this, value);
856 case kComboBox:
857 return m_pForm->NotifyBeforeValueChange(this, value);
858 default:
859 return true;
860 }
861 }
862
NotifyListOrComboBoxAfterChange()863 void CPDF_FormField::NotifyListOrComboBoxAfterChange() {
864 switch (GetType()) {
865 case kListBox:
866 m_pForm->NotifyAfterSelectionChange(this);
867 break;
868 case kComboBox:
869 m_pForm->NotifyAfterValueChange(this);
870 break;
871 default:
872 break;
873 }
874 }
875
GetFieldAttrInternal(const ByteString & name) const876 RetainPtr<const CPDF_Object> CPDF_FormField::GetFieldAttrInternal(
877 const ByteString& name) const {
878 return GetFieldAttrRecursive(m_pDict.Get(), name, 0);
879 }
880
GetFieldDictInternal() const881 const CPDF_Dictionary* CPDF_FormField::GetFieldDictInternal() const {
882 return m_pDict.Get();
883 }
884
GetDefaultValueObject() const885 RetainPtr<const CPDF_Object> CPDF_FormField::GetDefaultValueObject() const {
886 return GetFieldAttrInternal(pdfium::form_fields::kDV);
887 }
888
GetValueObject() const889 RetainPtr<const CPDF_Object> CPDF_FormField::GetValueObject() const {
890 return GetFieldAttrInternal(pdfium::form_fields::kV);
891 }
892
GetSelectedIndicesObject() const893 RetainPtr<const CPDF_Object> CPDF_FormField::GetSelectedIndicesObject() const {
894 DCHECK(GetType() == kComboBox || GetType() == kListBox);
895 return GetFieldAttrInternal("I");
896 }
897
GetValueOrSelectedIndicesObject() const898 RetainPtr<const CPDF_Object> CPDF_FormField::GetValueOrSelectedIndicesObject()
899 const {
900 DCHECK(GetType() == kComboBox || GetType() == kListBox);
901 RetainPtr<const CPDF_Object> pValue = GetValueObject();
902 return pValue ? pValue : GetSelectedIndicesObject();
903 }
904
GetControls() const905 const std::vector<UnownedPtr<CPDF_FormControl>>& CPDF_FormField::GetControls()
906 const {
907 return m_pForm->GetControlsForField(this);
908 }
909