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