• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "core/fpdfdoc/cpdf_formfield.h"
6 
7 #include <vector>
8 
9 #include "constants/form_fields.h"
10 #include "constants/form_flags.h"
11 #include "core/fpdfapi/page/cpdf_pagemodule.h"
12 #include "core/fpdfapi/parser/cpdf_array.h"
13 #include "core/fpdfapi/parser/cpdf_dictionary.h"
14 #include "core/fpdfapi/parser/cpdf_document.h"
15 #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h"
16 #include "core/fpdfapi/parser/cpdf_name.h"
17 #include "core/fpdfapi/parser/cpdf_number.h"
18 #include "core/fpdfapi/parser/cpdf_reference.h"
19 #include "core/fpdfapi/parser/cpdf_string.h"
20 #include "core/fpdfapi/parser/cpdf_test_document.h"
21 #include "core/fpdfdoc/cpdf_interactiveform.h"
22 #include "core/fxcrt/containers/contains.h"
23 #include "core/fxcrt/fx_memory.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 
26 namespace {
27 
28 // Create and destroys the page module that is necessary when instantiating a
29 // CPDF_Document.
30 class ScopedCPDF_PageModule {
31  public:
32   FX_STACK_ALLOCATED();
33 
ScopedCPDF_PageModule()34   ScopedCPDF_PageModule() { CPDF_PageModule::Create(); }
~ScopedCPDF_PageModule()35   ~ScopedCPDF_PageModule() { CPDF_PageModule::Destroy(); }
36 };
37 
TestMultiselectFieldDict(RetainPtr<CPDF_Array> opt_array,RetainPtr<CPDF_Object> values,RetainPtr<CPDF_Object> selected_indices,bool expected_use_indices,const std::vector<int> & expected_indices,const std::vector<int> & excluded_indices)38 void TestMultiselectFieldDict(RetainPtr<CPDF_Array> opt_array,
39                               RetainPtr<CPDF_Object> values,
40                               RetainPtr<CPDF_Object> selected_indices,
41                               bool expected_use_indices,
42                               const std::vector<int>& expected_indices,
43                               const std::vector<int>& excluded_indices) {
44   auto form_dict = pdfium::MakeRetain<CPDF_Dictionary>();
45   form_dict->SetNewFor<CPDF_Name>("Type", "Annot");
46   form_dict->SetNewFor<CPDF_Name>("Subtype", "Widget");
47   form_dict->SetNewFor<CPDF_Name>(pdfium::form_fields::kFT,
48                                   pdfium::form_fields::kCh);
49   constexpr int kMuliSelectFlag = pdfium::form_flags::kChoiceMultiSelect;
50   form_dict->SetNewFor<CPDF_Number>(pdfium::form_fields::kFf, kMuliSelectFlag);
51   form_dict->SetFor("Opt", opt_array);
52   form_dict->SetFor(pdfium::form_fields::kV, values);
53   form_dict->SetFor("I", selected_indices);
54 
55   CPDF_TestDocument doc;
56   CPDF_InteractiveForm form(&doc);
57   CPDF_FormField form_field(&form, std::move(form_dict));
58   EXPECT_EQ(expected_use_indices, form_field.UseSelectedIndicesObject());
59   for (int i = 0; i < form_field.CountOptions(); i++) {
60     const bool expected_selected = pdfium::Contains(expected_indices, i);
61     EXPECT_EQ(expected_selected, form_field.IsItemSelected(i));
62   }
63   for (int i : excluded_indices) {
64     EXPECT_FALSE(form_field.IsItemSelected(i));
65   }
66 }
67 
68 }  // namespace
69 
TEST(CPDFFormFieldTest,GetFullNameForDict)70 TEST(CPDFFormFieldTest, GetFullNameForDict) {
71   WideString name = CPDF_FormField::GetFullNameForDict(nullptr);
72   EXPECT_TRUE(name.IsEmpty());
73 
74   CPDF_IndirectObjectHolder obj_holder;
75   auto root = obj_holder.NewIndirect<CPDF_Dictionary>();
76   root->SetNewFor<CPDF_Name>("T", "foo");
77   name = CPDF_FormField::GetFullNameForDict(root.Get());
78   EXPECT_EQ("foo", name.ToUTF8());
79 
80   auto dict1 = obj_holder.NewIndirect<CPDF_Dictionary>();
81   root->SetNewFor<CPDF_Reference>("Parent", &obj_holder, dict1->GetObjNum());
82   dict1->SetNewFor<CPDF_Name>("T", "bar");
83   name = CPDF_FormField::GetFullNameForDict(root.Get());
84   EXPECT_EQ("bar.foo", name.ToUTF8());
85 
86   auto dict2 = dict1->SetNewFor<CPDF_Dictionary>("Parent");
87   name = CPDF_FormField::GetFullNameForDict(root.Get());
88   EXPECT_EQ("bar.foo", name.ToUTF8());
89 
90   auto dict3 = obj_holder.NewIndirect<CPDF_Dictionary>();
91   dict2->SetNewFor<CPDF_Reference>("Parent", &obj_holder, dict3->GetObjNum());
92 
93   dict3->SetNewFor<CPDF_Name>("T", "qux");
94   name = CPDF_FormField::GetFullNameForDict(root.Get());
95   EXPECT_EQ("qux.bar.foo", name.ToUTF8());
96 
97   dict3->SetNewFor<CPDF_Reference>("Parent", &obj_holder, root->GetObjNum());
98   name = CPDF_FormField::GetFullNameForDict(root.Get());
99   EXPECT_EQ("qux.bar.foo", name.ToUTF8());
100   name = CPDF_FormField::GetFullNameForDict(dict1.Get());
101   EXPECT_EQ("foo.qux.bar", name.ToUTF8());
102   name = CPDF_FormField::GetFullNameForDict(dict2.Get());
103   EXPECT_EQ("bar.foo.qux", name.ToUTF8());
104   name = CPDF_FormField::GetFullNameForDict(dict3.Get());
105   EXPECT_EQ("bar.foo.qux", name.ToUTF8());
106 }
107 
TEST(CPDFFormFieldTest,IsItemSelected)108 TEST(CPDFFormFieldTest, IsItemSelected) {
109   ScopedCPDF_PageModule page_module;
110 
111   auto opt_array = pdfium::MakeRetain<CPDF_Array>();
112   opt_array->AppendNew<CPDF_String>(L"Alpha");
113   opt_array->AppendNew<CPDF_String>(L"Beta");
114   opt_array->AppendNew<CPDF_String>(L"Gamma");
115   opt_array->AppendNew<CPDF_String>(L"Delta");
116   opt_array->AppendNew<CPDF_String>(L"Epsilon");
117 
118   {
119     // No Values (/V) or Selected Indices (/I) objects.
120     std::vector<int> expected_indices;
121     std::vector<int> excluded_indices{-1, 5};
122     TestMultiselectFieldDict(opt_array, /*values=*/nullptr,
123                              /*selected_indices=*/nullptr,
124                              /*expected_use_indices=*/false, expected_indices,
125                              excluded_indices);
126   }
127   {
128     // Values (/V) object is just a string.
129     auto values = pdfium::MakeRetain<CPDF_String>(/*pPool=*/nullptr, L"Gamma");
130     std::vector<int> expected_indices{2};
131     std::vector<int> excluded_indices{-1, 5};
132     TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr,
133                              /*expected_use_indices=*/false, expected_indices,
134                              excluded_indices);
135   }
136   {
137     // Values (/V) object is just an invalid string.
138     auto values = pdfium::MakeRetain<CPDF_String>(/*pPool=*/nullptr, L"Omega");
139     std::vector<int> expected_indices;
140     std::vector<int> excluded_indices{-1, 5};
141     TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr,
142                              /*expected_use_indices=*/false, expected_indices,
143                              excluded_indices);
144   }
145   {
146     // Values (/V) object is an array with one object.
147     auto values = pdfium::MakeRetain<CPDF_Array>();
148     values->AppendNew<CPDF_String>(L"Beta");
149     std::vector<int> expected_indices{1};
150     std::vector<int> excluded_indices{-1, 5};
151     TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr,
152                              /*expected_use_indices=*/false, expected_indices,
153                              excluded_indices);
154   }
155   {
156     // Values (/V) object is an array with one invalid object.
157     auto values = pdfium::MakeRetain<CPDF_Array>();
158     values->AppendNew<CPDF_String>(L"Omega");
159     std::vector<int> expected_indices;
160     std::vector<int> excluded_indices{-1, 5};
161     TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr,
162                              /*expected_use_indices=*/false, expected_indices,
163                              excluded_indices);
164   }
165   {
166     // Values (/V) object is an array with multiple objects.
167     auto values = pdfium::MakeRetain<CPDF_Array>();
168     values->AppendNew<CPDF_String>(L"Beta");
169     values->AppendNew<CPDF_String>(L"Epsilon");
170     std::vector<int> expected_indices{1, 4};
171     std::vector<int> excluded_indices{-1, 5};
172     TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr,
173                              /*expected_use_indices=*/false, expected_indices,
174                              excluded_indices);
175   }
176   {
177     // Values (/V) object is an array with multiple objects with one invalid.
178     auto values = pdfium::MakeRetain<CPDF_Array>();
179     values->AppendNew<CPDF_String>(L"Beta");
180     values->AppendNew<CPDF_String>(L"Epsilon");
181     values->AppendNew<CPDF_String>(L"Omega");
182     std::vector<int> expected_indices{1, 4};
183     std::vector<int> excluded_indices{-1, 5};
184     TestMultiselectFieldDict(opt_array, values, /*selected_indices=*/nullptr,
185                              /*expected_use_indices=*/false, expected_indices,
186                              excluded_indices);
187   }
188   {
189     // Selected indices (/I) object is just a number.
190     auto selected_indices = pdfium::MakeRetain<CPDF_Number>(3);
191     std::vector<int> expected_indices{3};
192     std::vector<int> excluded_indices{-1, 5};
193     TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices,
194                              /*expected_use_indices=*/true, expected_indices,
195                              excluded_indices);
196   }
197   {
198     // Selected indices (/I) object is just an invalid number.
199     auto selected_indices = pdfium::MakeRetain<CPDF_Number>(26);
200     std::vector<int> expected_indices;
201     std::vector<int> excluded_indices{-1, 5, 26};
202     TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices,
203                              /*expected_use_indices=*/true, expected_indices,
204                              excluded_indices);
205   }
206   {
207     // Selected indices (/I) object is an array with one object.
208     auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
209     selected_indices->AppendNew<CPDF_Number>(0);
210     std::vector<int> expected_indices{0};
211     std::vector<int> excluded_indices{-1, 5};
212     TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices,
213                              /*expected_use_indices=*/true, expected_indices,
214                              excluded_indices);
215   }
216   {
217     // Selected indices (/I) object is an array with multiple objects.
218     auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
219     selected_indices->AppendNew<CPDF_Number>(0);
220     selected_indices->AppendNew<CPDF_Number>(2);
221     selected_indices->AppendNew<CPDF_Number>(3);
222     std::vector<int> expected_indices{0, 2, 3};
223     std::vector<int> excluded_indices{-1, 5};
224     TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices,
225                              /*expected_use_indices=*/true, expected_indices,
226                              excluded_indices);
227   }
228   {
229     // Selected indices (/I) object is an array with multiple objects and some
230     // are invalid.
231     auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
232     selected_indices->AppendNew<CPDF_Number>(0);
233     selected_indices->AppendNew<CPDF_Number>(2);
234     selected_indices->AppendNew<CPDF_Number>(3);
235     selected_indices->AppendNew<CPDF_Number>(-5);
236     selected_indices->AppendNew<CPDF_Number>(12);
237     selected_indices->AppendNew<CPDF_Number>(42);
238     std::vector<int> expected_indices{0, 2, 3};
239     std::vector<int> excluded_indices{-5, -1, 5, 12, 42};
240     TestMultiselectFieldDict(opt_array, /*values=*/nullptr, selected_indices,
241                              /*expected_use_indices=*/true, expected_indices,
242                              excluded_indices);
243   }
244   {
245     // Values (/V) or Selected Indices (/I) objects conflict with different
246     // lengths.
247     auto values = pdfium::MakeRetain<CPDF_Array>();
248     values->AppendNew<CPDF_String>(L"Beta");
249     values->AppendNew<CPDF_String>(L"Epsilon");
250     auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
251     selected_indices->AppendNew<CPDF_Number>(0);
252     selected_indices->AppendNew<CPDF_Number>(2);
253     selected_indices->AppendNew<CPDF_Number>(3);
254     std::vector<int> expected_indices{1, 4};
255     std::vector<int> excluded_indices{-1, 5};
256     TestMultiselectFieldDict(opt_array, values, selected_indices,
257                              /*expected_use_indices=*/false, expected_indices,
258                              excluded_indices);
259   }
260   {
261     // Values (/V) or Selected Indices (/I) objects conflict with same lengths.
262     auto values = pdfium::MakeRetain<CPDF_Array>();
263     values->AppendNew<CPDF_String>(L"Alpha");
264     values->AppendNew<CPDF_String>(L"Epsilon");
265     auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
266     selected_indices->AppendNew<CPDF_Number>(2);
267     selected_indices->AppendNew<CPDF_Number>(3);
268     std::vector<int> expected_indices{0, 4};
269     std::vector<int> excluded_indices{-1, 5};
270     TestMultiselectFieldDict(opt_array, values, selected_indices,
271                              /*expected_use_indices=*/false, expected_indices,
272                              excluded_indices);
273   }
274   {
275     // Values (/V) or Selected Indices (/I) objects conflict with values being
276     // invalid.
277     auto values = pdfium::MakeRetain<CPDF_Array>();
278     values->AppendNew<CPDF_String>(L"Beta");
279     values->AppendNew<CPDF_String>(L"Epsilon");
280     values->AppendNew<CPDF_String>(L"Omega");
281     auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
282     selected_indices->AppendNew<CPDF_Number>(1);
283     selected_indices->AppendNew<CPDF_Number>(4);
284     std::vector<int> expected_indices{1, 4};
285     std::vector<int> excluded_indices{-1, 5};
286     TestMultiselectFieldDict(opt_array, values, selected_indices,
287                              /*expected_use_indices=*/false, expected_indices,
288                              excluded_indices);
289   }
290   {
291     // Values (/V) or Selected Indices (/I) objects conflict with selected
292     // indices being invalid.
293     auto values = pdfium::MakeRetain<CPDF_Array>();
294     values->AppendNew<CPDF_String>(L"Beta");
295     values->AppendNew<CPDF_String>(L"Epsilon");
296     auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
297     selected_indices->AppendNew<CPDF_Number>(1);
298     selected_indices->AppendNew<CPDF_Number>(4);
299     selected_indices->AppendNew<CPDF_Number>(26);
300     std::vector<int> expected_indices{1, 4};
301     std::vector<int> excluded_indices{-1, 5, 26};
302     TestMultiselectFieldDict(opt_array, values, selected_indices,
303                              /*expected_use_indices=*/false, expected_indices,
304                              excluded_indices);
305   }
306   {
307     // Values (/V) or Selected Indices (/I) objects conflict with both being
308     // invalid.
309     auto values = pdfium::MakeRetain<CPDF_Array>();
310     values->AppendNew<CPDF_String>(L"Beta");
311     values->AppendNew<CPDF_String>(L"Epsilon");
312     values->AppendNew<CPDF_String>(L"Omega");
313     auto selected_indices = pdfium::MakeRetain<CPDF_Array>();
314     selected_indices->AppendNew<CPDF_Number>(0);
315     selected_indices->AppendNew<CPDF_Number>(2);
316     selected_indices->AppendNew<CPDF_Number>(3);
317     selected_indices->AppendNew<CPDF_Number>(26);
318     std::vector<int> expected_indices{1, 4};
319     std::vector<int> excluded_indices{-1, 5, 26};
320     TestMultiselectFieldDict(opt_array, values, selected_indices,
321                              /*expected_use_indices=*/false, expected_indices,
322                              excluded_indices);
323   }
324   {
325     // Values (/V) or Selected Indices (/I) objects conflict with each not being
326     // an array.
327     auto values = pdfium::MakeRetain<CPDF_String>(/*pPool=*/nullptr, L"Gamma");
328     auto selected_indices = pdfium::MakeRetain<CPDF_Number>(4);
329     std::vector<int> expected_indices{2};
330     std::vector<int> excluded_indices{-1, 5};
331     TestMultiselectFieldDict(opt_array, values, selected_indices,
332                              /*expected_use_indices=*/false, expected_indices,
333                              excluded_indices);
334   }
335 }
336