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 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 
     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 
     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 
     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 
     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 
     97 CPDF_FormField::~CPDF_FormField() {}
     98 
     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 
    159 CFX_WideString CPDF_FormField::GetFullName() const {
    160   return FPDF_GetFullName(m_pDict);
    161 }
    162 
    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 
    240 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 
    248 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 
    272 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 
    277 CFX_WideString CPDF_FormField::GetAlternateName() const {
    278   CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict, "TU");
    279   return pObj ? pObj->GetUnicodeText() : L"";
    280 }
    281 
    282 CFX_WideString CPDF_FormField::GetMappingName() const {
    283   CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict, "TM");
    284   return pObj ? pObj->GetUnicodeText() : L"";
    285 }
    286 
    287 uint32_t CPDF_FormField::GetFieldFlags() const {
    288   CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict, "Ff");
    289   return pObj ? pObj->GetInteger() : 0;
    290 }
    291 
    292 CFX_ByteString CPDF_FormField::GetDefaultStyle() const {
    293   CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict, "DS");
    294   return pObj ? pObj->GetString() : "";
    295 }
    296 
    297 CFX_WideString CPDF_FormField::GetRichTextString() const {
    298   CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict, "RV");
    299   return pObj ? pObj->GetUnicodeText() : L"";
    300 }
    301 
    302 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 
    333 CFX_WideString CPDF_FormField::GetValue() const {
    334   return GetValue(false);
    335 }
    336 
    337 CFX_WideString CPDF_FormField::GetDefaultValue() const {
    338   return GetValue(true);
    339 }
    340 
    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 
    402 bool CPDF_FormField::SetValue(const CFX_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, "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 
    420 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 
    435 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 
    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 
    489 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 
    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 
    590 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 
    598 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 
    613 int CPDF_FormField::CountOptions() const {
    614   CPDF_Array* pArray = ToArray(FPDF_GetFieldAttr(m_pDict, "Opt"));
    615   return pArray ? pArray->GetCount() : 0;
    616 }
    617 
    618 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 
    633 CFX_WideString CPDF_FormField::GetOptionLabel(int index) const {
    634   return GetOptionText(index, 1);
    635 }
    636 
    637 CFX_WideString CPDF_FormField::GetOptionValue(int index) const {
    638   return GetOptionText(index, 0);
    639 }
    640 
    641 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 
    649 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
    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 
    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 
    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 
    765 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 
    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 
    800 int CPDF_FormField::GetTopVisibleIndex() const {
    801   CPDF_Object* pObj = FPDF_GetFieldAttr(m_pDict, "TI");
    802   return pObj ? pObj->GetInteger() : 0;
    803 }
    804 
    805 int CPDF_FormField::CountSelectedOptions() const {
    806   CPDF_Array* pArray = ToArray(FPDF_GetFieldAttr(m_pDict, "I"));
    807   return pArray ? pArray->GetCount() : 0;
    808 }
    809 
    810 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 
    821 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 
    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 
    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 
    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 
    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 
    943 void CPDF_FormField::NotifyAfterSelectionChange() {
    944   if (!m_pForm->m_pFormNotify)
    945     return;
    946   m_pForm->m_pFormNotify->AfterSelectionChange(this);
    947 }
    948 
    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 
    955 void CPDF_FormField::NotifyAfterValueChange() {
    956   if (!m_pForm->m_pFormNotify)
    957     return;
    958   m_pForm->m_pFormNotify->AfterValueChange(this);
    959 }
    960 
    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 
    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