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