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