• 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 "core/fpdfapi/parser/cfdf_document.h"
14 #include "core/fpdfapi/parser/cpdf_array.h"
15 #include "core/fpdfapi/parser/cpdf_document.h"
16 #include "core/fpdfapi/parser/cpdf_name.h"
17 #include "core/fpdfapi/parser/cpdf_number.h"
18 #include "core/fpdfapi/parser/cpdf_simple_parser.h"
19 #include "core/fpdfapi/parser/cpdf_string.h"
20 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
21 #include "core/fpdfdoc/cpdf_formcontrol.h"
22 #include "core/fpdfdoc/cpdf_interform.h"
23 #include "core/fpdfdoc/cpvt_generateap.h"
24 #include "third_party/base/stl_util.h"
25 
26 namespace {
27 
28 const int kFormListMultiSelect = 0x100;
29 
30 const int kFormComboEdit = 0x100;
31 
32 const int kFormRadioNoToggleOff = 0x100;
33 const int kFormRadioUnison = 0x200;
34 
35 const int kFormTextMultiLine = 0x100;
36 const int kFormTextPassword = 0x200;
37 const int kFormTextNoScroll = 0x400;
38 const int kFormTextComb = 0x800;
39 
IsUnison(CPDF_FormField * pField)40 bool IsUnison(CPDF_FormField* pField) {
41   if (pField->GetType() == CPDF_FormField::CheckBox)
42     return true;
43   return (pField->GetFieldFlags() & 0x2000000) != 0;
44 }
45 
46 }  // namespace
47 
IntToFormFieldType(int value)48 Optional<FormFieldType> IntToFormFieldType(int value) {
49   if (value >= static_cast<int>(FormFieldType::kUnknown) &&
50       value < static_cast<int>(kFormFieldTypeCount)) {
51     return {static_cast<FormFieldType>(value)};
52   }
53   return {};
54 }
55 
FPDF_GetFieldAttr(const CPDF_Dictionary * pFieldDict,const char * name,int nLevel)56 CPDF_Object* FPDF_GetFieldAttr(const CPDF_Dictionary* pFieldDict,
57                                const char* name,
58                                int nLevel) {
59   static constexpr int kGetFieldMaxRecursion = 32;
60   if (!pFieldDict || nLevel > kGetFieldMaxRecursion)
61     return nullptr;
62 
63   CPDF_Object* pAttr = pFieldDict->GetDirectObjectFor(name);
64   if (pAttr)
65     return pAttr;
66 
67   CPDF_Dictionary* pParent = pFieldDict->GetDictFor("Parent");
68   return pParent ? FPDF_GetFieldAttr(pParent, name, nLevel + 1) : nullptr;
69 }
70 
FPDF_GetFullName(CPDF_Dictionary * pFieldDict)71 WideString FPDF_GetFullName(CPDF_Dictionary* pFieldDict) {
72   WideString full_name;
73   std::set<CPDF_Dictionary*> visited;
74   CPDF_Dictionary* pLevel = pFieldDict;
75   while (pLevel) {
76     visited.insert(pLevel);
77     WideString short_name = pLevel->GetUnicodeTextFor("T");
78     if (!short_name.IsEmpty()) {
79       if (full_name.IsEmpty())
80         full_name = short_name;
81       else
82         full_name = short_name + L"." + full_name;
83     }
84     pLevel = pLevel->GetDictFor("Parent");
85     if (pdfium::ContainsKey(visited, pLevel))
86       break;
87   }
88   return full_name;
89 }
90 
CPDF_FormField(CPDF_InterForm * pForm,CPDF_Dictionary * pDict)91 CPDF_FormField::CPDF_FormField(CPDF_InterForm* pForm, CPDF_Dictionary* pDict)
92     : m_Type(Unknown),
93       m_pForm(pForm),
94       m_pDict(pDict),
95       m_FontSize(0),
96       m_pFont(nullptr) {
97   SyncFieldFlags();
98 }
99 
~CPDF_FormField()100 CPDF_FormField::~CPDF_FormField() {}
101 
SyncFieldFlags()102 void CPDF_FormField::SyncFieldFlags() {
103   CPDF_Object* ft_attr = FPDF_GetFieldAttr(m_pDict.Get(), "FT");
104   ByteString type_name = ft_attr ? ft_attr->GetString() : ByteString();
105   CPDF_Object* ff_attr = FPDF_GetFieldAttr(m_pDict.Get(), "Ff");
106   uint32_t flags = ff_attr ? ff_attr->GetInteger() : 0;
107   m_Flags = 0;
108   if (flags & FORMFLAG_READONLY)
109     m_Flags |= FORMFLAG_READONLY;
110   if (flags & FORMFLAG_REQUIRED)
111     m_Flags |= FORMFLAG_REQUIRED;
112   if (flags & FORMFLAG_NOEXPORT)
113     m_Flags |= FORMFLAG_NOEXPORT;
114 
115   if (type_name == "Btn") {
116     if (flags & 0x8000) {
117       m_Type = RadioButton;
118       if (flags & 0x4000)
119         m_Flags |= kFormRadioNoToggleOff;
120       if (flags & 0x2000000)
121         m_Flags |= kFormRadioUnison;
122     } else if (flags & 0x10000) {
123       m_Type = PushButton;
124     } else {
125       m_Type = CheckBox;
126     }
127   } else if (type_name == "Tx") {
128     if (flags & 0x100000) {
129       m_Type = File;
130     } else if (flags & 0x2000000) {
131       m_Type = RichText;
132     } else {
133       m_Type = Text;
134       if (flags & 0x1000)
135         m_Flags |= kFormTextMultiLine;
136       if (flags & 0x2000)
137         m_Flags |= kFormTextPassword;
138       if (flags & 0x800000)
139         m_Flags |= kFormTextNoScroll;
140       if (flags & 0x100000)
141         m_Flags |= kFormTextComb;
142     }
143     LoadDA();
144   } else if (type_name == "Ch") {
145     if (flags & 0x20000) {
146       m_Type = ComboBox;
147       if (flags & 0x40000)
148         m_Flags |= kFormComboEdit;
149     } else {
150       m_Type = ListBox;
151       if (flags & 0x200000)
152         m_Flags |= kFormListMultiSelect;
153     }
154     LoadDA();
155   } else if (type_name == "Sig") {
156     m_Type = Sign;
157   }
158 }
159 
GetFullName() const160 WideString CPDF_FormField::GetFullName() const {
161   return FPDF_GetFullName(m_pDict.Get());
162 }
163 
ResetField(bool bNotify)164 bool CPDF_FormField::ResetField(bool bNotify) {
165   switch (m_Type) {
166     case CPDF_FormField::CheckBox:
167     case CPDF_FormField::RadioButton: {
168       int iCount = CountControls();
169       if (iCount) {
170         // TODO(weili): Check whether anything special needs to be done for
171         // unison field. Otherwise, merge these branches.
172         if (IsUnison(this)) {
173           for (int i = 0; i < iCount; i++)
174             CheckControl(i, GetControl(i)->IsDefaultChecked(), false);
175         } else {
176           for (int i = 0; i < iCount; i++)
177             CheckControl(i, GetControl(i)->IsDefaultChecked(), false);
178         }
179       }
180       if (bNotify && m_pForm->GetFormNotify())
181         m_pForm->GetFormNotify()->AfterCheckedStatusChange(this);
182       break;
183     }
184     case CPDF_FormField::ComboBox:
185     case CPDF_FormField::ListBox: {
186       WideString csValue;
187       ClearSelection();
188       int iIndex = GetDefaultSelectedItem();
189       if (iIndex >= 0)
190         csValue = GetOptionLabel(iIndex);
191 
192       if (bNotify && !NotifyListOrComboBoxBeforeChange(csValue))
193         return false;
194 
195       SetItemSelection(iIndex, true);
196       if (bNotify)
197         NotifyListOrComboBoxAfterChange();
198       break;
199     }
200     case CPDF_FormField::Text:
201     case CPDF_FormField::RichText:
202     case CPDF_FormField::File:
203     default: {
204       CPDF_Object* pDV = FPDF_GetFieldAttr(m_pDict.Get(), "DV");
205       WideString csDValue;
206       if (pDV)
207         csDValue = pDV->GetUnicodeText();
208 
209       CPDF_Object* pV = FPDF_GetFieldAttr(m_pDict.Get(), "V");
210       WideString csValue;
211       if (pV)
212         csValue = pV->GetUnicodeText();
213 
214       CPDF_Object* pRV = FPDF_GetFieldAttr(m_pDict.Get(), "RV");
215       if (!pRV && (csDValue == csValue))
216         return false;
217 
218       if (bNotify && !NotifyBeforeValueChange(csDValue))
219         return false;
220 
221       if (pDV) {
222         std::unique_ptr<CPDF_Object> pClone = pDV->Clone();
223         if (!pClone)
224           return false;
225 
226         m_pDict->SetFor("V", std::move(pClone));
227         if (pRV)
228           m_pDict->SetFor("RV", pDV->Clone());
229       } else {
230         m_pDict->RemoveFor("V");
231         m_pDict->RemoveFor("RV");
232       }
233       if (bNotify)
234         NotifyAfterValueChange();
235       break;
236     }
237   }
238   return true;
239 }
240 
GetControlIndex(const CPDF_FormControl * pControl) const241 int CPDF_FormField::GetControlIndex(const CPDF_FormControl* pControl) const {
242   if (!pControl)
243     return -1;
244 
245   auto it = std::find(m_ControlList.begin(), m_ControlList.end(), pControl);
246   return it != m_ControlList.end() ? it - m_ControlList.begin() : -1;
247 }
248 
GetFieldType() const249 FormFieldType CPDF_FormField::GetFieldType() const {
250   switch (m_Type) {
251     case PushButton:
252       return FormFieldType::kPushButton;
253     case CheckBox:
254       return FormFieldType::kCheckBox;
255     case RadioButton:
256       return FormFieldType::kRadioButton;
257     case ComboBox:
258       return FormFieldType::kComboBox;
259     case ListBox:
260       return FormFieldType::kListBox;
261     case Text:
262     case RichText:
263     case File:
264       return FormFieldType::kTextField;
265     case Sign:
266       return FormFieldType::kSignature;
267     default:
268       return FormFieldType::kUnknown;
269   }
270 }
271 
GetAdditionalAction() const272 CPDF_AAction CPDF_FormField::GetAdditionalAction() const {
273   CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "AA");
274   return CPDF_AAction(pObj ? pObj->GetDict() : nullptr);
275 }
276 
GetAlternateName() const277 WideString CPDF_FormField::GetAlternateName() const {
278   CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "TU");
279   return pObj ? pObj->GetUnicodeText() : L"";
280 }
281 
GetMappingName() const282 WideString CPDF_FormField::GetMappingName() const {
283   CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "TM");
284   return pObj ? pObj->GetUnicodeText() : L"";
285 }
286 
GetFieldFlags() const287 uint32_t CPDF_FormField::GetFieldFlags() const {
288   CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "Ff");
289   return pObj ? pObj->GetInteger() : 0;
290 }
291 
GetDefaultStyle() const292 ByteString CPDF_FormField::GetDefaultStyle() const {
293   CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "DS");
294   return pObj ? pObj->GetString() : "";
295 }
296 
GetRichTextString() const297 WideString CPDF_FormField::GetRichTextString() const {
298   CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "RV");
299   return pObj ? pObj->GetUnicodeText() : L"";
300 }
301 
GetValue(bool bDefault) const302 WideString CPDF_FormField::GetValue(bool bDefault) const {
303   if (GetType() == CheckBox || GetType() == RadioButton)
304     return GetCheckValue(bDefault);
305 
306   CPDF_Object* pValue = FPDF_GetFieldAttr(m_pDict.Get(), bDefault ? "DV" : "V");
307   if (!pValue) {
308     if (!bDefault) {
309       if (m_Type == RichText)
310         pValue = FPDF_GetFieldAttr(m_pDict.Get(), "V");
311       if (!pValue && m_Type != Text)
312         pValue = FPDF_GetFieldAttr(m_pDict.Get(), "DV");
313     }
314     if (!pValue)
315       return WideString();
316   }
317 
318   switch (pValue->GetType()) {
319     case CPDF_Object::STRING:
320     case CPDF_Object::STREAM:
321       return pValue->GetUnicodeText();
322     case CPDF_Object::ARRAY:
323       pValue = pValue->AsArray()->GetDirectObjectAt(0);
324       if (pValue)
325         return pValue->GetUnicodeText();
326       break;
327     default:
328       break;
329   }
330   return WideString();
331 }
332 
GetValue() const333 WideString CPDF_FormField::GetValue() const {
334   return GetValue(false);
335 }
336 
GetDefaultValue() const337 WideString CPDF_FormField::GetDefaultValue() const {
338   return GetValue(true);
339 }
340 
SetValue(const WideString & value,bool bDefault,bool bNotify)341 bool CPDF_FormField::SetValue(const WideString& value,
342                               bool bDefault,
343                               bool bNotify) {
344   switch (m_Type) {
345     case CheckBox:
346     case RadioButton: {
347       SetCheckValue(value, bDefault, bNotify);
348       return true;
349     }
350     case File:
351     case RichText:
352     case Text:
353     case ComboBox: {
354       WideString csValue = value;
355       if (bNotify && !NotifyBeforeValueChange(csValue))
356         return false;
357 
358       ByteString key(bDefault ? "DV" : "V");
359       int iIndex = FindOptionValue(csValue);
360       if (iIndex < 0) {
361         ByteString bsEncodeText = PDF_EncodeText(csValue);
362         m_pDict->SetNewFor<CPDF_String>(key, bsEncodeText, false);
363         if (m_Type == RichText && !bDefault)
364           m_pDict->SetNewFor<CPDF_String>("RV", bsEncodeText, false);
365         m_pDict->RemoveFor("I");
366       } else {
367         m_pDict->SetNewFor<CPDF_String>(key, PDF_EncodeText(csValue), false);
368         if (!bDefault) {
369           ClearSelection();
370           SetItemSelection(iIndex, true);
371         }
372       }
373       if (bNotify)
374         NotifyAfterValueChange();
375       break;
376     }
377     case ListBox: {
378       int iIndex = FindOptionValue(value);
379       if (iIndex < 0)
380         return false;
381 
382       if (bDefault && iIndex == GetDefaultSelectedItem())
383         return false;
384 
385       if (bNotify && !NotifyBeforeSelectionChange(value))
386         return false;
387 
388       if (!bDefault) {
389         ClearSelection();
390         SetItemSelection(iIndex, true);
391       }
392       if (bNotify)
393         NotifyAfterSelectionChange();
394       break;
395     }
396     default:
397       break;
398   }
399   return true;
400 }
401 
SetValue(const WideString & value,bool bNotify)402 bool CPDF_FormField::SetValue(const WideString& value, bool bNotify) {
403   return SetValue(value, false, bNotify);
404 }
405 
GetMaxLen() const406 int CPDF_FormField::GetMaxLen() const {
407   if (CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "MaxLen"))
408     return pObj->GetInteger();
409 
410   for (auto& pControl : m_ControlList) {
411     if (!pControl)
412       continue;
413     CPDF_Dictionary* pWidgetDict = pControl->GetWidget();
414     if (pWidgetDict->KeyExist("MaxLen"))
415       return pWidgetDict->GetIntegerFor("MaxLen");
416   }
417   return 0;
418 }
419 
CountSelectedItems() const420 int CPDF_FormField::CountSelectedItems() const {
421   CPDF_Object* pValue = FPDF_GetFieldAttr(m_pDict.Get(), "V");
422   if (!pValue) {
423     pValue = FPDF_GetFieldAttr(m_pDict.Get(), "I");
424     if (!pValue)
425       return 0;
426   }
427 
428   if (pValue->IsString() || pValue->IsNumber())
429     return pValue->GetString().IsEmpty() ? 0 : 1;
430   if (CPDF_Array* pArray = pValue->AsArray())
431     return pArray->GetCount();
432   return 0;
433 }
434 
GetSelectedIndex(int index) const435 int CPDF_FormField::GetSelectedIndex(int index) const {
436   CPDF_Object* pValue = FPDF_GetFieldAttr(m_pDict.Get(), "V");
437   if (!pValue) {
438     pValue = FPDF_GetFieldAttr(m_pDict.Get(), "I");
439     if (!pValue)
440       return -1;
441   }
442   if (pValue->IsNumber())
443     return pValue->GetInteger();
444 
445   WideString sel_value;
446   if (pValue->IsString()) {
447     if (index != 0)
448       return -1;
449     sel_value = pValue->GetUnicodeText();
450   } else {
451     CPDF_Array* pArray = pValue->AsArray();
452     if (!pArray || index < 0)
453       return -1;
454 
455     CPDF_Object* elementValue = pArray->GetDirectObjectAt(index);
456     sel_value = elementValue ? elementValue->GetUnicodeText() : WideString();
457   }
458   if (index < CountSelectedOptions()) {
459     int iOptIndex = GetSelectedOptionIndex(index);
460     WideString csOpt = GetOptionValue(iOptIndex);
461     if (csOpt == sel_value)
462       return iOptIndex;
463   }
464   for (int i = 0; i < CountOptions(); i++) {
465     if (sel_value == GetOptionValue(i))
466       return i;
467   }
468   return -1;
469 }
470 
ClearSelection(bool bNotify)471 bool CPDF_FormField::ClearSelection(bool bNotify) {
472   if (bNotify && m_pForm->GetFormNotify()) {
473     WideString csValue;
474     int iIndex = GetSelectedIndex(0);
475     if (iIndex >= 0)
476       csValue = GetOptionLabel(iIndex);
477 
478     if (!NotifyListOrComboBoxBeforeChange(csValue))
479       return false;
480   }
481   m_pDict->RemoveFor("V");
482   m_pDict->RemoveFor("I");
483   if (bNotify)
484     NotifyListOrComboBoxAfterChange();
485   return true;
486 }
487 
IsItemSelected(int index) const488 bool CPDF_FormField::IsItemSelected(int index) const {
489   ASSERT(GetType() == ComboBox || GetType() == ListBox);
490   if (index < 0 || index >= CountOptions())
491     return false;
492   if (IsOptionSelected(index))
493     return true;
494 
495   WideString opt_value = GetOptionValue(index);
496   CPDF_Object* pValue = FPDF_GetFieldAttr(m_pDict.Get(), "V");
497   if (!pValue) {
498     pValue = FPDF_GetFieldAttr(m_pDict.Get(), "I");
499     if (!pValue)
500       return false;
501   }
502 
503   if (pValue->IsString())
504     return pValue->GetUnicodeText() == opt_value;
505 
506   if (pValue->IsNumber()) {
507     if (pValue->GetString().IsEmpty())
508       return false;
509     return (pValue->GetInteger() == index);
510   }
511 
512   CPDF_Array* pArray = pValue->AsArray();
513   if (!pArray)
514     return false;
515 
516   int iPos = -1;
517   for (int j = 0; j < CountSelectedOptions(); j++) {
518     if (GetSelectedOptionIndex(j) == index) {
519       iPos = j;
520       break;
521     }
522   }
523   for (int i = 0; i < static_cast<int>(pArray->GetCount()); i++)
524     if (pArray->GetDirectObjectAt(i)->GetUnicodeText() == opt_value &&
525         i == iPos) {
526       return true;
527     }
528   return false;
529 }
530 
SetItemSelection(int index,bool bSelected,bool bNotify)531 bool CPDF_FormField::SetItemSelection(int index, bool bSelected, bool bNotify) {
532   ASSERT(GetType() == ComboBox || GetType() == ListBox);
533   if (index < 0 || index >= CountOptions())
534     return false;
535 
536   WideString opt_value = GetOptionValue(index);
537   if (bNotify && !NotifyListOrComboBoxBeforeChange(opt_value))
538     return false;
539 
540   if (bSelected) {
541     if (GetType() == ListBox) {
542       SelectOption(index, true);
543       if (!(m_Flags & kFormListMultiSelect)) {
544         m_pDict->SetNewFor<CPDF_String>("V", PDF_EncodeText(opt_value), false);
545       } else {
546         CPDF_Array* pArray = m_pDict->SetNewFor<CPDF_Array>("V");
547         for (int i = 0; i < CountOptions(); i++) {
548           if (i == index || IsItemSelected(i)) {
549             opt_value = GetOptionValue(i);
550             pArray->AddNew<CPDF_String>(PDF_EncodeText(opt_value), false);
551           }
552         }
553       }
554     } else {
555       m_pDict->SetNewFor<CPDF_String>("V", PDF_EncodeText(opt_value), false);
556       CPDF_Array* pI = m_pDict->SetNewFor<CPDF_Array>("I");
557       pI->AddNew<CPDF_Number>(index);
558     }
559   } else {
560     CPDF_Object* pValue = FPDF_GetFieldAttr(m_pDict.Get(), "V");
561     if (pValue) {
562       if (GetType() == ListBox) {
563         SelectOption(index, false);
564         if (pValue->IsString()) {
565           if (pValue->GetUnicodeText() == opt_value)
566             m_pDict->RemoveFor("V");
567         } else if (pValue->IsArray()) {
568           auto pArray = pdfium::MakeUnique<CPDF_Array>();
569           for (int i = 0; i < CountOptions(); i++) {
570             if (i != index && IsItemSelected(i)) {
571               opt_value = GetOptionValue(i);
572               pArray->AddNew<CPDF_String>(PDF_EncodeText(opt_value), false);
573             }
574           }
575           if (pArray->GetCount() > 0)
576             m_pDict->SetFor("V", std::move(pArray));
577         }
578       } else {
579         m_pDict->RemoveFor("V");
580         m_pDict->RemoveFor("I");
581       }
582     }
583   }
584   if (bNotify)
585     NotifyListOrComboBoxAfterChange();
586   return true;
587 }
588 
IsItemDefaultSelected(int index) const589 bool CPDF_FormField::IsItemDefaultSelected(int index) const {
590   ASSERT(GetType() == ComboBox || GetType() == ListBox);
591   if (index < 0 || index >= CountOptions())
592     return false;
593   int iDVIndex = GetDefaultSelectedItem();
594   return iDVIndex >= 0 && iDVIndex == index;
595 }
596 
GetDefaultSelectedItem() const597 int CPDF_FormField::GetDefaultSelectedItem() const {
598   ASSERT(GetType() == ComboBox || GetType() == ListBox);
599   CPDF_Object* pValue = FPDF_GetFieldAttr(m_pDict.Get(), "DV");
600   if (!pValue)
601     return -1;
602   WideString csDV = pValue->GetUnicodeText();
603   if (csDV.IsEmpty())
604     return -1;
605   for (int i = 0; i < CountOptions(); i++) {
606     if (csDV == GetOptionValue(i))
607       return i;
608   }
609   return -1;
610 }
611 
CountOptions() const612 int CPDF_FormField::CountOptions() const {
613   CPDF_Array* pArray = ToArray(FPDF_GetFieldAttr(m_pDict.Get(), "Opt"));
614   return pArray ? pArray->GetCount() : 0;
615 }
616 
GetOptionText(int index,int sub_index) const617 WideString CPDF_FormField::GetOptionText(int index, int sub_index) const {
618   CPDF_Array* pArray = ToArray(FPDF_GetFieldAttr(m_pDict.Get(), "Opt"));
619   if (!pArray)
620     return WideString();
621 
622   CPDF_Object* pOption = pArray->GetDirectObjectAt(index);
623   if (!pOption)
624     return WideString();
625   if (CPDF_Array* pOptionArray = pOption->AsArray())
626     pOption = pOptionArray->GetDirectObjectAt(sub_index);
627 
628   CPDF_String* pString = ToString(pOption);
629   return pString ? pString->GetUnicodeText() : WideString();
630 }
631 
GetOptionLabel(int index) const632 WideString CPDF_FormField::GetOptionLabel(int index) const {
633   return GetOptionText(index, 1);
634 }
635 
GetOptionValue(int index) const636 WideString CPDF_FormField::GetOptionValue(int index) const {
637   return GetOptionText(index, 0);
638 }
639 
FindOption(WideString csOptLabel) const640 int CPDF_FormField::FindOption(WideString csOptLabel) const {
641   for (int i = 0; i < CountOptions(); i++) {
642     if (GetOptionValue(i) == csOptLabel)
643       return i;
644   }
645   return -1;
646 }
647 
FindOptionValue(const WideString & csOptValue) const648 int CPDF_FormField::FindOptionValue(const WideString& csOptValue) const {
649   for (int i = 0; i < CountOptions(); i++) {
650     if (GetOptionValue(i) == csOptValue)
651       return i;
652   }
653   return -1;
654 }
655 
656 #ifdef PDF_ENABLE_XFA
InsertOption(WideString csOptLabel,int index,bool bNotify)657 int CPDF_FormField::InsertOption(WideString csOptLabel,
658                                  int index,
659                                  bool bNotify) {
660   if (csOptLabel.IsEmpty())
661     return -1;
662 
663   if (bNotify && !NotifyListOrComboBoxBeforeChange(csOptLabel))
664     return -1;
665 
666   ByteString csStr = PDF_EncodeText(csOptLabel);
667   CPDF_Array* pOpt = ToArray(FPDF_GetFieldAttr(m_pDict.Get(), "Opt"));
668   if (!pOpt)
669     pOpt = m_pDict->SetNewFor<CPDF_Array>("Opt");
670 
671   int iCount = pdfium::base::checked_cast<int>(pOpt->GetCount());
672   if (index >= iCount) {
673     pOpt->AddNew<CPDF_String>(csStr, false);
674     index = iCount;
675   } else {
676     pOpt->InsertNewAt<CPDF_String>(index, csStr, false);
677   }
678 
679   if (bNotify)
680     NotifyListOrComboBoxAfterChange();
681   return index;
682 }
683 
ClearOptions(bool bNotify)684 bool CPDF_FormField::ClearOptions(bool bNotify) {
685   if (bNotify && m_pForm->GetFormNotify()) {
686     WideString csValue;
687     int iIndex = GetSelectedIndex(0);
688     if (iIndex >= 0)
689       csValue = GetOptionLabel(iIndex);
690     if (!NotifyListOrComboBoxBeforeChange(csValue))
691       return false;
692   }
693 
694   m_pDict->RemoveFor("Opt");
695   m_pDict->RemoveFor("V");
696   m_pDict->RemoveFor("DV");
697   m_pDict->RemoveFor("I");
698   m_pDict->RemoveFor("TI");
699 
700   if (bNotify)
701     NotifyListOrComboBoxAfterChange();
702 
703   return true;
704 }
705 #endif  // PDF_ENABLE_XFA
706 
CheckControl(int iControlIndex,bool bChecked,bool bNotify)707 bool CPDF_FormField::CheckControl(int iControlIndex,
708                                   bool bChecked,
709                                   bool bNotify) {
710   ASSERT(GetType() == CheckBox || GetType() == RadioButton);
711   CPDF_FormControl* pControl = GetControl(iControlIndex);
712   if (!pControl)
713     return false;
714   if (!bChecked && pControl->IsChecked() == bChecked)
715     return false;
716 
717   WideString csWExport = pControl->GetExportValue();
718   ByteString csBExport = PDF_EncodeText(csWExport);
719   int iCount = CountControls();
720   bool bUnison = IsUnison(this);
721   for (int i = 0; i < iCount; i++) {
722     CPDF_FormControl* pCtrl = GetControl(i);
723     if (bUnison) {
724       WideString csEValue = pCtrl->GetExportValue();
725       if (csEValue == csWExport) {
726         if (pCtrl->GetOnStateName() == pControl->GetOnStateName())
727           pCtrl->CheckControl(bChecked);
728         else if (bChecked)
729           pCtrl->CheckControl(false);
730       } else if (bChecked) {
731         pCtrl->CheckControl(false);
732       }
733     } else {
734       if (i == iControlIndex)
735         pCtrl->CheckControl(bChecked);
736       else if (bChecked)
737         pCtrl->CheckControl(false);
738     }
739   }
740 
741   CPDF_Object* pOpt = FPDF_GetFieldAttr(m_pDict.Get(), "Opt");
742   if (!ToArray(pOpt)) {
743     if (bChecked) {
744       m_pDict->SetNewFor<CPDF_Name>("V", csBExport);
745     } else {
746       ByteString csV;
747       CPDF_Object* pV = FPDF_GetFieldAttr(m_pDict.Get(), "V");
748       if (pV)
749         csV = pV->GetString();
750       if (csV == csBExport)
751         m_pDict->SetNewFor<CPDF_Name>("V", "Off");
752     }
753   } else if (bChecked) {
754     m_pDict->SetNewFor<CPDF_Name>("V", ByteString::Format("%d", iControlIndex));
755   }
756   if (bNotify && m_pForm->GetFormNotify())
757     m_pForm->GetFormNotify()->AfterCheckedStatusChange(this);
758   return true;
759 }
760 
GetCheckValue(bool bDefault) const761 WideString CPDF_FormField::GetCheckValue(bool bDefault) const {
762   ASSERT(GetType() == CheckBox || GetType() == RadioButton);
763   WideString csExport = L"Off";
764   int iCount = CountControls();
765   for (int i = 0; i < iCount; i++) {
766     CPDF_FormControl* pControl = GetControl(i);
767     bool bChecked =
768         bDefault ? pControl->IsDefaultChecked() : pControl->IsChecked();
769     if (bChecked) {
770       csExport = pControl->GetExportValue();
771       break;
772     }
773   }
774   return csExport;
775 }
776 
SetCheckValue(const WideString & value,bool bDefault,bool bNotify)777 bool CPDF_FormField::SetCheckValue(const WideString& value,
778                                    bool bDefault,
779                                    bool bNotify) {
780   ASSERT(GetType() == CheckBox || GetType() == RadioButton);
781   int iCount = CountControls();
782   for (int i = 0; i < iCount; i++) {
783     CPDF_FormControl* pControl = GetControl(i);
784     WideString csExport = pControl->GetExportValue();
785     bool val = csExport == value;
786     if (!bDefault)
787       CheckControl(GetControlIndex(pControl), val);
788     if (val)
789       break;
790   }
791   if (bNotify && m_pForm->GetFormNotify())
792     m_pForm->GetFormNotify()->AfterCheckedStatusChange(this);
793   return true;
794 }
795 
GetTopVisibleIndex() const796 int CPDF_FormField::GetTopVisibleIndex() const {
797   CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "TI");
798   return pObj ? pObj->GetInteger() : 0;
799 }
800 
CountSelectedOptions() const801 int CPDF_FormField::CountSelectedOptions() const {
802   CPDF_Array* pArray = ToArray(FPDF_GetFieldAttr(m_pDict.Get(), "I"));
803   return pArray ? pArray->GetCount() : 0;
804 }
805 
GetSelectedOptionIndex(int index) const806 int CPDF_FormField::GetSelectedOptionIndex(int index) const {
807   CPDF_Array* pArray = ToArray(FPDF_GetFieldAttr(m_pDict.Get(), "I"));
808   if (!pArray)
809     return -1;
810 
811   int iCount = pArray->GetCount();
812   if (iCount < 0 || index >= iCount)
813     return -1;
814   return pArray->GetIntegerAt(index);
815 }
816 
IsOptionSelected(int iOptIndex) const817 bool CPDF_FormField::IsOptionSelected(int iOptIndex) const {
818   CPDF_Array* pArray = ToArray(FPDF_GetFieldAttr(m_pDict.Get(), "I"));
819   if (!pArray)
820     return false;
821 
822   for (const auto& pObj : *pArray) {
823     if (pObj->GetInteger() == iOptIndex)
824       return true;
825   }
826   return false;
827 }
828 
SelectOption(int iOptIndex,bool bSelected,bool bNotify)829 bool CPDF_FormField::SelectOption(int iOptIndex, bool bSelected, bool bNotify) {
830   CPDF_Array* pArray = m_pDict->GetArrayFor("I");
831   if (!pArray) {
832     if (!bSelected)
833       return true;
834 
835     pArray = m_pDict->SetNewFor<CPDF_Array>("I");
836   }
837 
838   bool bReturn = false;
839   for (size_t i = 0; i < pArray->GetCount(); i++) {
840     int iFind = pArray->GetIntegerAt(i);
841     if (iFind == iOptIndex) {
842       if (bSelected)
843         return true;
844 
845       if (bNotify && m_pForm->GetFormNotify()) {
846         WideString csValue = GetOptionLabel(iOptIndex);
847         if (!NotifyListOrComboBoxBeforeChange(csValue))
848           return false;
849       }
850       pArray->RemoveAt(i);
851       bReturn = true;
852       break;
853     }
854 
855     if (iFind > iOptIndex) {
856       if (!bSelected)
857         continue;
858 
859       if (bNotify && m_pForm->GetFormNotify()) {
860         WideString csValue = GetOptionLabel(iOptIndex);
861         if (!NotifyListOrComboBoxBeforeChange(csValue))
862           return false;
863       }
864       pArray->InsertNewAt<CPDF_Number>(i, iOptIndex);
865       bReturn = true;
866       break;
867     }
868   }
869   if (!bReturn) {
870     if (bSelected)
871       pArray->AddNew<CPDF_Number>(iOptIndex);
872 
873     if (pArray->IsEmpty())
874       m_pDict->RemoveFor("I");
875   }
876   if (bNotify)
877     NotifyListOrComboBoxAfterChange();
878 
879   return true;
880 }
881 
ClearSelectedOptions(bool bNotify)882 bool CPDF_FormField::ClearSelectedOptions(bool bNotify) {
883   if (bNotify && m_pForm->GetFormNotify()) {
884     WideString csValue;
885     int iIndex = GetSelectedIndex(0);
886     if (iIndex >= 0)
887       csValue = GetOptionLabel(iIndex);
888 
889     if (!NotifyListOrComboBoxBeforeChange(csValue))
890       return false;
891   }
892   m_pDict->RemoveFor("I");
893   if (bNotify)
894     NotifyListOrComboBoxAfterChange();
895 
896   return true;
897 }
898 
LoadDA()899 void CPDF_FormField::LoadDA() {
900   CPDF_Dictionary* pFormDict = m_pForm->GetFormDict();
901   if (!pFormDict)
902     return;
903 
904   ByteString DA;
905   if (CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "DA"))
906     DA = pObj->GetString();
907 
908   if (DA.IsEmpty())
909     DA = pFormDict->GetStringFor("DA");
910 
911   if (DA.IsEmpty())
912     return;
913 
914   CPDF_Dictionary* pDR = pFormDict->GetDictFor("DR");
915   if (!pDR)
916     return;
917 
918   CPDF_Dictionary* pFont = pDR->GetDictFor("Font");
919   if (!pFont)
920     return;
921 
922   CPDF_SimpleParser syntax(DA.AsStringView());
923   syntax.FindTagParamFromStart("Tf", 2);
924   ByteString font_name(syntax.GetWord());
925   CPDF_Dictionary* pFontDict = pFont->GetDictFor(font_name);
926   if (!pFontDict)
927     return;
928 
929   m_pFont = m_pForm->GetDocument()->LoadFont(pFontDict);
930   m_FontSize = FX_atof(syntax.GetWord());
931 }
932 
NotifyBeforeSelectionChange(const WideString & value)933 bool CPDF_FormField::NotifyBeforeSelectionChange(const WideString& value) {
934   if (!m_pForm->GetFormNotify())
935     return true;
936   return m_pForm->GetFormNotify()->BeforeSelectionChange(this, value) >= 0;
937 }
938 
NotifyAfterSelectionChange()939 void CPDF_FormField::NotifyAfterSelectionChange() {
940   if (!m_pForm->GetFormNotify())
941     return;
942   m_pForm->GetFormNotify()->AfterSelectionChange(this);
943 }
944 
NotifyBeforeValueChange(const WideString & value)945 bool CPDF_FormField::NotifyBeforeValueChange(const WideString& value) {
946   if (!m_pForm->GetFormNotify())
947     return true;
948   return m_pForm->GetFormNotify()->BeforeValueChange(this, value) >= 0;
949 }
950 
NotifyAfterValueChange()951 void CPDF_FormField::NotifyAfterValueChange() {
952   if (!m_pForm->GetFormNotify())
953     return;
954   m_pForm->GetFormNotify()->AfterValueChange(this);
955 }
956 
NotifyListOrComboBoxBeforeChange(const WideString & value)957 bool CPDF_FormField::NotifyListOrComboBoxBeforeChange(const WideString& value) {
958   switch (GetType()) {
959     case ListBox:
960       return NotifyBeforeSelectionChange(value);
961     case ComboBox:
962       return NotifyBeforeValueChange(value);
963     default:
964       return true;
965   }
966 }
967 
NotifyListOrComboBoxAfterChange()968 void CPDF_FormField::NotifyListOrComboBoxAfterChange() {
969   switch (GetType()) {
970     case ListBox:
971       NotifyAfterSelectionChange();
972       break;
973     case ComboBox:
974       NotifyAfterValueChange();
975       break;
976     default:
977       break;
978   }
979 }
980