• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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