Home | History | Annotate | Download | only in fpdfdoc
      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 
     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 
     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 
     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 
     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 
     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 
    100 CPDF_FormField::~CPDF_FormField() {}
    101 
    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 
    160 WideString CPDF_FormField::GetFullName() const {
    161   return FPDF_GetFullName(m_pDict.Get());
    162 }
    163 
    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 
    241 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 
    249 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 
    272 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 
    277 WideString CPDF_FormField::GetAlternateName() const {
    278   CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "TU");
    279   return pObj ? pObj->GetUnicodeText() : L"";
    280 }
    281 
    282 WideString CPDF_FormField::GetMappingName() const {
    283   CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "TM");
    284   return pObj ? pObj->GetUnicodeText() : L"";
    285 }
    286 
    287 uint32_t CPDF_FormField::GetFieldFlags() const {
    288   CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "Ff");
    289   return pObj ? pObj->GetInteger() : 0;
    290 }
    291 
    292 ByteString CPDF_FormField::GetDefaultStyle() const {
    293   CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "DS");
    294   return pObj ? pObj->GetString() : "";
    295 }
    296 
    297 WideString CPDF_FormField::GetRichTextString() const {
    298   CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "RV");
    299   return pObj ? pObj->GetUnicodeText() : L"";
    300 }
    301 
    302 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 
    333 WideString CPDF_FormField::GetValue() const {
    334   return GetValue(false);
    335 }
    336 
    337 WideString CPDF_FormField::GetDefaultValue() const {
    338   return GetValue(true);
    339 }
    340 
    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 
    402 bool CPDF_FormField::SetValue(const WideString& value, bool bNotify) {
    403   return SetValue(value, false, bNotify);
    404 }
    405 
    406 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 
    420 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 
    435 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 
    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 
    488 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 
    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 
    589 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 
    597 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 
    612 int CPDF_FormField::CountOptions() const {
    613   CPDF_Array* pArray = ToArray(FPDF_GetFieldAttr(m_pDict.Get(), "Opt"));
    614   return pArray ? pArray->GetCount() : 0;
    615 }
    616 
    617 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 
    632 WideString CPDF_FormField::GetOptionLabel(int index) const {
    633   return GetOptionText(index, 1);
    634 }
    635 
    636 WideString CPDF_FormField::GetOptionValue(int index) const {
    637   return GetOptionText(index, 0);
    638 }
    639 
    640 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 
    648 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
    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 
    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 
    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 
    761 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 
    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 
    796 int CPDF_FormField::GetTopVisibleIndex() const {
    797   CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict.Get(), "TI");
    798   return pObj ? pObj->GetInteger() : 0;
    799 }
    800 
    801 int CPDF_FormField::CountSelectedOptions() const {
    802   CPDF_Array* pArray = ToArray(FPDF_GetFieldAttr(m_pDict.Get(), "I"));
    803   return pArray ? pArray->GetCount() : 0;
    804 }
    805 
    806 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 
    817 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 
    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 
    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 
    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 
    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 
    939 void CPDF_FormField::NotifyAfterSelectionChange() {
    940   if (!m_pForm->GetFormNotify())
    941     return;
    942   m_pForm->GetFormNotify()->AfterSelectionChange(this);
    943 }
    944 
    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 
    951 void CPDF_FormField::NotifyAfterValueChange() {
    952   if (!m_pForm->GetFormNotify())
    953     return;
    954   m_pForm->GetFormNotify()->AfterValueChange(this);
    955 }
    956 
    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 
    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