1 // Copyright 2016 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 #include "core/fpdfapi/parser/cpdf_object.h"
6
7 #include <memory>
8 #include <string>
9 #include <utility>
10 #include <vector>
11
12 #include "constants/stream_dict_common.h"
13 #include "core/fpdfapi/parser/cpdf_array.h"
14 #include "core/fpdfapi/parser/cpdf_boolean.h"
15 #include "core/fpdfapi/parser/cpdf_dictionary.h"
16 #include "core/fpdfapi/parser/cpdf_indirect_object_holder.h"
17 #include "core/fpdfapi/parser/cpdf_name.h"
18 #include "core/fpdfapi/parser/cpdf_null.h"
19 #include "core/fpdfapi/parser/cpdf_number.h"
20 #include "core/fpdfapi/parser/cpdf_reference.h"
21 #include "core/fpdfapi/parser/cpdf_stream.h"
22 #include "core/fpdfapi/parser/cpdf_stream_acc.h"
23 #include "core/fpdfapi/parser/cpdf_string.h"
24 #include "core/fxcrt/fx_memory_wrappers.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "third_party/base/stl_util.h"
27
28 namespace {
29
TestArrayAccessors(const CPDF_Array * arr,size_t index,const char * str_val,const char * const_str_val,int int_val,float float_val,CPDF_Array * arr_val,CPDF_Dictionary * dict_val,CPDF_Stream * stream_val)30 void TestArrayAccessors(const CPDF_Array* arr,
31 size_t index,
32 const char* str_val,
33 const char* const_str_val,
34 int int_val,
35 float float_val,
36 CPDF_Array* arr_val,
37 CPDF_Dictionary* dict_val,
38 CPDF_Stream* stream_val) {
39 EXPECT_STREQ(str_val, arr->GetStringAt(index).c_str());
40 EXPECT_EQ(int_val, arr->GetIntegerAt(index));
41 EXPECT_EQ(float_val, arr->GetNumberAt(index));
42 EXPECT_EQ(arr_val, arr->GetArrayAt(index));
43 EXPECT_EQ(dict_val, arr->GetDictAt(index));
44 EXPECT_EQ(stream_val, arr->GetStreamAt(index));
45 }
46
47 } // namespace
48
49 class PDFObjectsTest : public testing::Test {
50 public:
SetUp()51 void SetUp() override {
52 // Initialize different kinds of objects.
53 // Boolean objects.
54 auto boolean_false_obj = pdfium::MakeRetain<CPDF_Boolean>(false);
55 auto boolean_true_obj = pdfium::MakeRetain<CPDF_Boolean>(true);
56 // Number objects.
57 auto number_int_obj = pdfium::MakeRetain<CPDF_Number>(1245);
58 auto number_float_obj = pdfium::MakeRetain<CPDF_Number>(9.00345f);
59 // String objects.
60 auto str_reg_obj =
61 pdfium::MakeRetain<CPDF_String>(nullptr, L"A simple test");
62 auto str_spec_obj = pdfium::MakeRetain<CPDF_String>(nullptr, L"\t\n");
63 // Name object.
64 auto name_obj = pdfium::MakeRetain<CPDF_Name>(nullptr, "space");
65 // Array object.
66 m_ArrayObj = pdfium::MakeRetain<CPDF_Array>();
67 m_ArrayObj->InsertNewAt<CPDF_Number>(0, 8902);
68 m_ArrayObj->InsertNewAt<CPDF_Name>(1, "address");
69 // Dictionary object.
70 m_DictObj = pdfium::MakeRetain<CPDF_Dictionary>();
71 m_DictObj->SetNewFor<CPDF_Boolean>("bool", false);
72 m_DictObj->SetNewFor<CPDF_Number>("num", 0.23f);
73 // Stream object.
74 const char content[] = "abcdefghijklmnopqrstuvwxyz";
75 size_t buf_len = pdfium::size(content);
76 std::unique_ptr<uint8_t, FxFreeDeleter> buf(
77 FX_AllocUninit(uint8_t, buf_len));
78 memcpy(buf.get(), content, buf_len);
79 auto pNewDict = pdfium::MakeRetain<CPDF_Dictionary>();
80 m_StreamDictObj = pNewDict;
81 m_StreamDictObj->SetNewFor<CPDF_String>("key1", L" test dict");
82 m_StreamDictObj->SetNewFor<CPDF_Number>("key2", -1);
83 auto stream_obj = pdfium::MakeRetain<CPDF_Stream>(std::move(buf), buf_len,
84 std::move(pNewDict));
85 // Null Object.
86 auto null_obj = pdfium::MakeRetain<CPDF_Null>();
87 // All direct objects.
88 CPDF_Object* objs[] = {
89 boolean_false_obj.Get(), boolean_true_obj.Get(), number_int_obj.Get(),
90 number_float_obj.Get(), str_reg_obj.Get(), str_spec_obj.Get(),
91 name_obj.Get(), m_ArrayObj.Get(), m_DictObj.Get(),
92 stream_obj.Get(), null_obj.Get()};
93 m_DirectObjTypes = {
94 CPDF_Object::kBoolean, CPDF_Object::kBoolean, CPDF_Object::kNumber,
95 CPDF_Object::kNumber, CPDF_Object::kString, CPDF_Object::kString,
96 CPDF_Object::kName, CPDF_Object::kArray, CPDF_Object::kDictionary,
97 CPDF_Object::kStream, CPDF_Object::kNullobj};
98 for (size_t i = 0; i < pdfium::size(objs); ++i)
99 m_DirectObjs.emplace_back(objs[i]);
100
101 // Indirect references to indirect objects.
102 m_ObjHolder = std::make_unique<CPDF_IndirectObjectHolder>();
103 m_IndirectObjs = {m_ObjHolder->AddIndirectObject(boolean_true_obj->Clone()),
104 m_ObjHolder->AddIndirectObject(number_int_obj->Clone()),
105 m_ObjHolder->AddIndirectObject(str_spec_obj->Clone()),
106 m_ObjHolder->AddIndirectObject(name_obj->Clone()),
107 m_ObjHolder->AddIndirectObject(m_ArrayObj->Clone()),
108 m_ObjHolder->AddIndirectObject(m_DictObj->Clone()),
109 m_ObjHolder->AddIndirectObject(stream_obj->Clone())};
110 for (CPDF_Object* pObj : m_IndirectObjs) {
111 m_RefObjs.emplace_back(pdfium::MakeRetain<CPDF_Reference>(
112 m_ObjHolder.get(), pObj->GetObjNum()));
113 }
114 }
115
Equal(const CPDF_Object * obj1,const CPDF_Object * obj2)116 bool Equal(const CPDF_Object* obj1, const CPDF_Object* obj2) {
117 if (obj1 == obj2)
118 return true;
119 if (!obj1 || !obj2 || obj1->GetType() != obj2->GetType())
120 return false;
121 switch (obj1->GetType()) {
122 case CPDF_Object::kBoolean:
123 return obj1->GetInteger() == obj2->GetInteger();
124 case CPDF_Object::kNumber:
125 return obj1->AsNumber()->IsInteger() == obj2->AsNumber()->IsInteger() &&
126 obj1->GetInteger() == obj2->GetInteger();
127 case CPDF_Object::kString:
128 case CPDF_Object::kName:
129 return obj1->GetString() == obj2->GetString();
130 case CPDF_Object::kArray: {
131 const CPDF_Array* array1 = obj1->AsArray();
132 const CPDF_Array* array2 = obj2->AsArray();
133 if (array1->size() != array2->size())
134 return false;
135 for (size_t i = 0; i < array1->size(); ++i) {
136 if (!Equal(array1->GetObjectAt(i), array2->GetObjectAt(i)))
137 return false;
138 }
139 return true;
140 }
141 case CPDF_Object::kDictionary: {
142 const CPDF_Dictionary* dict1 = obj1->AsDictionary();
143 const CPDF_Dictionary* dict2 = obj2->AsDictionary();
144 if (dict1->size() != dict2->size())
145 return false;
146 CPDF_DictionaryLocker locker1(dict1);
147 for (const auto& item : locker1) {
148 if (!Equal(item.second.Get(), dict2->GetObjectFor(item.first)))
149 return false;
150 }
151 return true;
152 }
153 case CPDF_Object::kNullobj:
154 return true;
155 case CPDF_Object::kStream: {
156 const CPDF_Stream* stream1 = obj1->AsStream();
157 const CPDF_Stream* stream2 = obj2->AsStream();
158 if (!stream1->GetDict() && !stream2->GetDict())
159 return true;
160 // Compare dictionaries.
161 if (!Equal(stream1->GetDict(), stream2->GetDict()))
162 return false;
163
164 auto streamAcc1 = pdfium::MakeRetain<CPDF_StreamAcc>(stream1);
165 streamAcc1->LoadAllDataRaw();
166 auto streamAcc2 = pdfium::MakeRetain<CPDF_StreamAcc>(stream2);
167 streamAcc2->LoadAllDataRaw();
168
169 // Compare sizes.
170 if (streamAcc1->GetSize() != streamAcc2->GetSize())
171 return false;
172
173 return memcmp(streamAcc1->GetData(), streamAcc2->GetData(),
174 streamAcc2->GetSize()) == 0;
175 }
176 case CPDF_Object::kReference:
177 return obj1->AsReference()->GetRefObjNum() ==
178 obj2->AsReference()->GetRefObjNum();
179 }
180 return false;
181 }
182
183 protected:
184 // m_ObjHolder needs to be declared first and destructed last since it also
185 // refers to some objects in m_DirectObjs.
186 std::unique_ptr<CPDF_IndirectObjectHolder> m_ObjHolder;
187 std::vector<RetainPtr<CPDF_Object>> m_DirectObjs;
188 std::vector<int> m_DirectObjTypes;
189 std::vector<RetainPtr<CPDF_Object>> m_RefObjs;
190 RetainPtr<CPDF_Dictionary> m_DictObj;
191 RetainPtr<CPDF_Dictionary> m_StreamDictObj;
192 RetainPtr<CPDF_Array> m_ArrayObj;
193 std::vector<CPDF_Object*> m_IndirectObjs;
194 };
195
TEST_F(PDFObjectsTest,GetString)196 TEST_F(PDFObjectsTest, GetString) {
197 const char* const direct_obj_results[] = {
198 "false", "true", "1245", "9.00345", "A simple test", "\t\n", "space",
199 "", "", "", ""};
200 // Check for direct objects.
201 for (size_t i = 0; i < m_DirectObjs.size(); ++i)
202 EXPECT_STREQ(direct_obj_results[i], m_DirectObjs[i]->GetString().c_str());
203
204 // Check indirect references.
205 const char* const indirect_obj_results[] = {"true", "1245", "\t\n", "space",
206 "", "", ""};
207 for (size_t i = 0; i < m_RefObjs.size(); ++i) {
208 EXPECT_STREQ(indirect_obj_results[i], m_RefObjs[i]->GetString().c_str());
209 }
210 }
211
TEST_F(PDFObjectsTest,GetUnicodeText)212 TEST_F(PDFObjectsTest, GetUnicodeText) {
213 const wchar_t* const direct_obj_results[] = {
214 L"", L"", L"", L"", L"A simple test",
215 L"\t\n", L"space", L"", L"", L"abcdefghijklmnopqrstuvwxyz",
216 L""};
217 // Check for direct objects.
218 for (size_t i = 0; i < m_DirectObjs.size(); ++i) {
219 EXPECT_STREQ(direct_obj_results[i],
220 m_DirectObjs[i]->GetUnicodeText().c_str());
221 }
222
223 // Check indirect references.
224 for (const auto& it : m_RefObjs)
225 EXPECT_STREQ(L"", it->GetUnicodeText().c_str());
226 }
227
TEST_F(PDFObjectsTest,GetNumber)228 TEST_F(PDFObjectsTest, GetNumber) {
229 const float direct_obj_results[] = {0, 0, 1245, 9.00345f, 0, 0,
230 0, 0, 0, 0, 0};
231 // Check for direct objects.
232 for (size_t i = 0; i < m_DirectObjs.size(); ++i)
233 EXPECT_EQ(direct_obj_results[i], m_DirectObjs[i]->GetNumber());
234
235 // Check indirect references.
236 const float indirect_obj_results[] = {0, 1245, 0, 0, 0, 0, 0};
237 for (size_t i = 0; i < m_RefObjs.size(); ++i)
238 EXPECT_EQ(indirect_obj_results[i], m_RefObjs[i]->GetNumber());
239 }
240
TEST_F(PDFObjectsTest,GetInteger)241 TEST_F(PDFObjectsTest, GetInteger) {
242 const int direct_obj_results[] = {0, 1, 1245, 9, 0, 0, 0, 0, 0, 0, 0};
243 // Check for direct objects.
244 for (size_t i = 0; i < m_DirectObjs.size(); ++i)
245 EXPECT_EQ(direct_obj_results[i], m_DirectObjs[i]->GetInteger());
246
247 // Check indirect references.
248 const int indirect_obj_results[] = {1, 1245, 0, 0, 0, 0, 0};
249 for (size_t i = 0; i < m_RefObjs.size(); ++i)
250 EXPECT_EQ(indirect_obj_results[i], m_RefObjs[i]->GetInteger());
251 }
252
TEST_F(PDFObjectsTest,GetDict)253 TEST_F(PDFObjectsTest, GetDict) {
254 const CPDF_Dictionary* const direct_obj_results[] = {
255 nullptr, nullptr, nullptr, nullptr, nullptr,
256 nullptr, nullptr, nullptr, m_DictObj.Get(), m_StreamDictObj.Get(),
257 nullptr};
258 // Check for direct objects.
259 for (size_t i = 0; i < m_DirectObjs.size(); ++i)
260 EXPECT_EQ(direct_obj_results[i], m_DirectObjs[i]->GetDict());
261
262 // Check indirect references.
263 const CPDF_Dictionary* const indirect_obj_results[] = {nullptr,
264 nullptr,
265 nullptr,
266 nullptr,
267 nullptr,
268 m_DictObj.Get(),
269 m_StreamDictObj.Get()};
270 for (size_t i = 0; i < m_RefObjs.size(); ++i)
271 EXPECT_TRUE(Equal(indirect_obj_results[i], m_RefObjs[i]->GetDict()));
272 }
273
TEST_F(PDFObjectsTest,GetNameFor)274 TEST_F(PDFObjectsTest, GetNameFor) {
275 m_DictObj->SetNewFor<CPDF_String>("string", "ium", false);
276 m_DictObj->SetNewFor<CPDF_Name>("name", "Pdf");
277
278 EXPECT_STREQ("", m_DictObj->GetNameFor("invalid").c_str());
279 EXPECT_STREQ("", m_DictObj->GetNameFor("bool").c_str());
280 EXPECT_STREQ("", m_DictObj->GetNameFor("num").c_str());
281 EXPECT_STREQ("", m_DictObj->GetNameFor("string").c_str());
282 EXPECT_STREQ("Pdf", m_DictObj->GetNameFor("name").c_str());
283
284 EXPECT_STREQ("", m_DictObj->GetStringFor("invalid").c_str());
285 EXPECT_STREQ("false", m_DictObj->GetStringFor("bool").c_str());
286 EXPECT_STREQ("0.23", m_DictObj->GetStringFor("num").c_str());
287 EXPECT_STREQ("ium", m_DictObj->GetStringFor("string").c_str());
288 EXPECT_STREQ("Pdf", m_DictObj->GetStringFor("name").c_str());
289 }
290
TEST_F(PDFObjectsTest,GetArray)291 TEST_F(PDFObjectsTest, GetArray) {
292 const CPDF_Array* const direct_obj_results[] = {
293 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
294 nullptr, m_ArrayObj.Get(), nullptr, nullptr, nullptr};
295 // Check for direct objects.
296 for (size_t i = 0; i < m_DirectObjs.size(); ++i)
297 EXPECT_EQ(direct_obj_results[i], m_DirectObjs[i]->AsArray());
298
299 // Check indirect references.
300 for (const auto& it : m_RefObjs)
301 EXPECT_EQ(nullptr, it->AsArray());
302 }
303
TEST_F(PDFObjectsTest,Clone)304 TEST_F(PDFObjectsTest, Clone) {
305 // Check for direct objects.
306 for (size_t i = 0; i < m_DirectObjs.size(); ++i) {
307 RetainPtr<CPDF_Object> obj = m_DirectObjs[i]->Clone();
308 EXPECT_TRUE(Equal(m_DirectObjs[i].Get(), obj.Get()));
309 }
310
311 // Check indirect references.
312 for (const auto& it : m_RefObjs) {
313 RetainPtr<CPDF_Object> obj = it->Clone();
314 EXPECT_TRUE(Equal(it.Get(), obj.Get()));
315 }
316 }
317
TEST_F(PDFObjectsTest,GetType)318 TEST_F(PDFObjectsTest, GetType) {
319 // Check for direct objects.
320 for (size_t i = 0; i < m_DirectObjs.size(); ++i)
321 EXPECT_EQ(m_DirectObjTypes[i], m_DirectObjs[i]->GetType());
322
323 // Check indirect references.
324 for (const auto& it : m_RefObjs)
325 EXPECT_EQ(CPDF_Object::kReference, it->GetType());
326 }
327
TEST_F(PDFObjectsTest,GetDirect)328 TEST_F(PDFObjectsTest, GetDirect) {
329 // Check for direct objects.
330 for (size_t i = 0; i < m_DirectObjs.size(); ++i)
331 EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->GetDirect());
332
333 // Check indirect references.
334 for (size_t i = 0; i < m_RefObjs.size(); ++i)
335 EXPECT_EQ(m_IndirectObjs[i], m_RefObjs[i]->GetDirect());
336 }
337
TEST_F(PDFObjectsTest,SetString)338 TEST_F(PDFObjectsTest, SetString) {
339 // Check for direct objects.
340 const char* const set_values[] = {"true", "fake", "3.125f", "097",
341 "changed", "", "NewName"};
342 const char* const expected[] = {"true", "false", "3.125", "97",
343 "changed", "", "NewName"};
344 for (size_t i = 0; i < pdfium::size(set_values); ++i) {
345 m_DirectObjs[i]->SetString(set_values[i]);
346 EXPECT_STREQ(expected[i], m_DirectObjs[i]->GetString().c_str());
347 }
348 }
349
TEST_F(PDFObjectsTest,IsTypeAndAsType)350 TEST_F(PDFObjectsTest, IsTypeAndAsType) {
351 // Check for direct objects.
352 for (size_t i = 0; i < m_DirectObjs.size(); ++i) {
353 if (m_DirectObjTypes[i] == CPDF_Object::kArray) {
354 EXPECT_TRUE(m_DirectObjs[i]->IsArray());
355 EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsArray());
356 } else {
357 EXPECT_FALSE(m_DirectObjs[i]->IsArray());
358 EXPECT_EQ(nullptr, m_DirectObjs[i]->AsArray());
359 }
360
361 if (m_DirectObjTypes[i] == CPDF_Object::kBoolean) {
362 EXPECT_TRUE(m_DirectObjs[i]->IsBoolean());
363 EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsBoolean());
364 } else {
365 EXPECT_FALSE(m_DirectObjs[i]->IsBoolean());
366 EXPECT_EQ(nullptr, m_DirectObjs[i]->AsBoolean());
367 }
368
369 if (m_DirectObjTypes[i] == CPDF_Object::kName) {
370 EXPECT_TRUE(m_DirectObjs[i]->IsName());
371 EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsName());
372 } else {
373 EXPECT_FALSE(m_DirectObjs[i]->IsName());
374 EXPECT_EQ(nullptr, m_DirectObjs[i]->AsName());
375 }
376
377 if (m_DirectObjTypes[i] == CPDF_Object::kNumber) {
378 EXPECT_TRUE(m_DirectObjs[i]->IsNumber());
379 EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsNumber());
380 } else {
381 EXPECT_FALSE(m_DirectObjs[i]->IsNumber());
382 EXPECT_EQ(nullptr, m_DirectObjs[i]->AsNumber());
383 }
384
385 if (m_DirectObjTypes[i] == CPDF_Object::kString) {
386 EXPECT_TRUE(m_DirectObjs[i]->IsString());
387 EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsString());
388 } else {
389 EXPECT_FALSE(m_DirectObjs[i]->IsString());
390 EXPECT_EQ(nullptr, m_DirectObjs[i]->AsString());
391 }
392
393 if (m_DirectObjTypes[i] == CPDF_Object::kDictionary) {
394 EXPECT_TRUE(m_DirectObjs[i]->IsDictionary());
395 EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsDictionary());
396 } else {
397 EXPECT_FALSE(m_DirectObjs[i]->IsDictionary());
398 EXPECT_EQ(nullptr, m_DirectObjs[i]->AsDictionary());
399 }
400
401 if (m_DirectObjTypes[i] == CPDF_Object::kStream) {
402 EXPECT_TRUE(m_DirectObjs[i]->IsStream());
403 EXPECT_EQ(m_DirectObjs[i].Get(), m_DirectObjs[i]->AsStream());
404 } else {
405 EXPECT_FALSE(m_DirectObjs[i]->IsStream());
406 EXPECT_EQ(nullptr, m_DirectObjs[i]->AsStream());
407 }
408
409 EXPECT_FALSE(m_DirectObjs[i]->IsReference());
410 EXPECT_EQ(nullptr, m_DirectObjs[i]->AsReference());
411 }
412 // Check indirect references.
413 for (size_t i = 0; i < m_RefObjs.size(); ++i) {
414 EXPECT_TRUE(m_RefObjs[i]->IsReference());
415 EXPECT_EQ(m_RefObjs[i].Get(), m_RefObjs[i]->AsReference());
416 }
417 }
418
TEST_F(PDFObjectsTest,MakeReferenceGeneric)419 TEST_F(PDFObjectsTest, MakeReferenceGeneric) {
420 auto original_obj = pdfium::MakeRetain<CPDF_Null>();
421 original_obj->SetObjNum(42);
422 ASSERT_FALSE(original_obj->IsInline());
423
424 auto ref_obj = original_obj->MakeReference(m_ObjHolder.get());
425
426 ASSERT_TRUE(ref_obj->IsReference());
427 EXPECT_EQ(original_obj->GetObjNum(),
428 ToReference(ref_obj.Get())->GetRefObjNum());
429 }
430
TEST(PDFArrayTest,GetMatrix)431 TEST(PDFArrayTest, GetMatrix) {
432 float elems[][6] = {{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
433 {1, 2, 3, 4, 5, 6},
434 {2.3f, 4.05f, 3, -2, -3, 0.0f},
435 {0.05f, 0.1f, 0.56f, 0.67f, 1.34f, 99.9f}};
436 for (size_t i = 0; i < pdfium::size(elems); ++i) {
437 auto arr = pdfium::MakeRetain<CPDF_Array>();
438 CFX_Matrix matrix(elems[i][0], elems[i][1], elems[i][2], elems[i][3],
439 elems[i][4], elems[i][5]);
440 for (size_t j = 0; j < 6; ++j)
441 arr->AppendNew<CPDF_Number>(elems[i][j]);
442 CFX_Matrix arr_matrix = arr->GetMatrix();
443 EXPECT_EQ(matrix.a, arr_matrix.a);
444 EXPECT_EQ(matrix.b, arr_matrix.b);
445 EXPECT_EQ(matrix.c, arr_matrix.c);
446 EXPECT_EQ(matrix.d, arr_matrix.d);
447 EXPECT_EQ(matrix.e, arr_matrix.e);
448 EXPECT_EQ(matrix.f, arr_matrix.f);
449 }
450 }
451
TEST(PDFArrayTest,GetRect)452 TEST(PDFArrayTest, GetRect) {
453 float elems[][4] = {{0.0f, 0.0f, 0.0f, 0.0f},
454 {1, 2, 5, 6},
455 {2.3f, 4.05f, -3, 0.0f},
456 {0.05f, 0.1f, 1.34f, 99.9f}};
457 for (size_t i = 0; i < pdfium::size(elems); ++i) {
458 auto arr = pdfium::MakeRetain<CPDF_Array>();
459 CFX_FloatRect rect(elems[i]);
460 for (size_t j = 0; j < 4; ++j)
461 arr->AppendNew<CPDF_Number>(elems[i][j]);
462 CFX_FloatRect arr_rect = arr->GetRect();
463 EXPECT_EQ(rect.left, arr_rect.left);
464 EXPECT_EQ(rect.right, arr_rect.right);
465 EXPECT_EQ(rect.bottom, arr_rect.bottom);
466 EXPECT_EQ(rect.top, arr_rect.top);
467 }
468 }
469
TEST(PDFArrayTest,GetTypeAt)470 TEST(PDFArrayTest, GetTypeAt) {
471 {
472 // Boolean array.
473 const bool vals[] = {true, false, false, true, true};
474 auto arr = pdfium::MakeRetain<CPDF_Array>();
475 for (size_t i = 0; i < pdfium::size(vals); ++i)
476 arr->InsertNewAt<CPDF_Boolean>(i, vals[i]);
477 for (size_t i = 0; i < pdfium::size(vals); ++i) {
478 TestArrayAccessors(arr.Get(), i, // Array and index.
479 vals[i] ? "true" : "false", // String value.
480 nullptr, // Const string value.
481 vals[i] ? 1 : 0, // Integer value.
482 0, // Float value.
483 nullptr, // Array value.
484 nullptr, // Dictionary value.
485 nullptr); // Stream value.
486 }
487 }
488 {
489 // Integer array.
490 const int vals[] = {10, 0, -345, 2089345456, -1000000000, 567, 93658767};
491 auto arr = pdfium::MakeRetain<CPDF_Array>();
492 for (size_t i = 0; i < pdfium::size(vals); ++i)
493 arr->InsertNewAt<CPDF_Number>(i, vals[i]);
494 for (size_t i = 0; i < pdfium::size(vals); ++i) {
495 char buf[33];
496 TestArrayAccessors(arr.Get(), i, // Array and index.
497 FXSYS_itoa(vals[i], buf, 10), // String value.
498 nullptr, // Const string value.
499 vals[i], // Integer value.
500 vals[i], // Float value.
501 nullptr, // Array value.
502 nullptr, // Dictionary value.
503 nullptr); // Stream value.
504 }
505 }
506 {
507 // Float array.
508 const float vals[] = {0.0f, 0, 10, 10.0f, 0.0345f,
509 897.34f, -2.5f, -1.0f, -345.0f, -0.0f};
510 const char* const expected_str[] = {
511 "0", "0", "10", "10", "0.0345", "897.34", "-2.5", "-1", "-345", "0"};
512 auto arr = pdfium::MakeRetain<CPDF_Array>();
513 for (size_t i = 0; i < pdfium::size(vals); ++i)
514 arr->InsertNewAt<CPDF_Number>(i, vals[i]);
515 for (size_t i = 0; i < pdfium::size(vals); ++i) {
516 TestArrayAccessors(arr.Get(), i, // Array and index.
517 expected_str[i], // String value.
518 nullptr, // Const string value.
519 vals[i], // Integer value.
520 vals[i], // Float value.
521 nullptr, // Array value.
522 nullptr, // Dictionary value.
523 nullptr); // Stream value.
524 }
525 }
526 {
527 // String and name array
528 const char* const vals[] = {"this", "adsde$%^", "\r\t", "\"012",
529 ".", "EYREW", "It is a joke :)"};
530 auto string_array = pdfium::MakeRetain<CPDF_Array>();
531 auto name_array = pdfium::MakeRetain<CPDF_Array>();
532 for (size_t i = 0; i < pdfium::size(vals); ++i) {
533 string_array->InsertNewAt<CPDF_String>(i, vals[i], false);
534 name_array->InsertNewAt<CPDF_Name>(i, vals[i]);
535 }
536 for (size_t i = 0; i < pdfium::size(vals); ++i) {
537 TestArrayAccessors(string_array.Get(), i, // Array and index.
538 vals[i], // String value.
539 vals[i], // Const string value.
540 0, // Integer value.
541 0, // Float value.
542 nullptr, // Array value.
543 nullptr, // Dictionary value.
544 nullptr); // Stream value.
545 TestArrayAccessors(name_array.Get(), i, // Array and index.
546 vals[i], // String value.
547 vals[i], // Const string value.
548 0, // Integer value.
549 0, // Float value.
550 nullptr, // Array value.
551 nullptr, // Dictionary value.
552 nullptr); // Stream value.
553 }
554 }
555 {
556 // Null element array.
557 auto arr = pdfium::MakeRetain<CPDF_Array>();
558 for (size_t i = 0; i < 3; ++i)
559 arr->InsertNewAt<CPDF_Null>(i);
560 for (size_t i = 0; i < 3; ++i) {
561 TestArrayAccessors(arr.Get(), i, // Array and index.
562 "", // String value.
563 nullptr, // Const string value.
564 0, // Integer value.
565 0, // Float value.
566 nullptr, // Array value.
567 nullptr, // Dictionary value.
568 nullptr); // Stream value.
569 }
570 }
571 {
572 // Array of array.
573 CPDF_Array* vals[3];
574 auto arr = pdfium::MakeRetain<CPDF_Array>();
575 for (size_t i = 0; i < 3; ++i) {
576 vals[i] = arr->AppendNew<CPDF_Array>();
577 for (size_t j = 0; j < 3; ++j) {
578 int value = j + 100;
579 vals[i]->InsertNewAt<CPDF_Number>(i, value);
580 }
581 }
582 for (size_t i = 0; i < 3; ++i) {
583 TestArrayAccessors(arr.Get(), i, // Array and index.
584 "", // String value.
585 nullptr, // Const string value.
586 0, // Integer value.
587 0, // Float value.
588 vals[i], // Array value.
589 nullptr, // Dictionary value.
590 nullptr); // Stream value.
591 }
592 }
593 {
594 // Dictionary array.
595 CPDF_Dictionary* vals[3];
596 auto arr = pdfium::MakeRetain<CPDF_Array>();
597 for (size_t i = 0; i < 3; ++i) {
598 vals[i] = arr->AppendNew<CPDF_Dictionary>();
599 for (size_t j = 0; j < 3; ++j) {
600 std::string key("key");
601 char buf[33];
602 key.append(FXSYS_itoa(j, buf, 10));
603 int value = j + 200;
604 vals[i]->SetNewFor<CPDF_Number>(key.c_str(), value);
605 }
606 }
607 for (size_t i = 0; i < 3; ++i) {
608 TestArrayAccessors(arr.Get(), i, // Array and index.
609 "", // String value.
610 nullptr, // Const string value.
611 0, // Integer value.
612 0, // Float value.
613 nullptr, // Array value.
614 vals[i], // Dictionary value.
615 nullptr); // Stream value.
616 }
617 }
618 {
619 // Stream array.
620 RetainPtr<CPDF_Dictionary> vals[3];
621 CPDF_Stream* stream_vals[3];
622 auto arr = pdfium::MakeRetain<CPDF_Array>();
623 for (size_t i = 0; i < 3; ++i) {
624 vals[i] = pdfium::MakeRetain<CPDF_Dictionary>();
625 for (size_t j = 0; j < 3; ++j) {
626 std::string key("key");
627 char buf[33];
628 key.append(FXSYS_itoa(j, buf, 10));
629 int value = j + 200;
630 vals[i]->SetNewFor<CPDF_Number>(key.c_str(), value);
631 }
632 uint8_t content[] = "content: this is a stream";
633 size_t data_size = pdfium::size(content);
634 std::unique_ptr<uint8_t, FxFreeDeleter> data(
635 FX_AllocUninit(uint8_t, data_size));
636 memcpy(data.get(), content, data_size);
637 stream_vals[i] =
638 arr->AppendNew<CPDF_Stream>(std::move(data), data_size, vals[i]);
639 }
640 for (size_t i = 0; i < 3; ++i) {
641 TestArrayAccessors(arr.Get(), i, // Array and index.
642 "", // String value.
643 nullptr, // Const string value.
644 0, // Integer value.
645 0, // Float value.
646 nullptr, // Array value.
647 vals[i].Get(), // Dictionary value.
648 stream_vals[i]); // Stream value.
649 }
650 }
651 {
652 // Mixed array.
653 auto arr = pdfium::MakeRetain<CPDF_Array>();
654 arr->InsertNewAt<CPDF_Boolean>(0, true);
655 arr->InsertNewAt<CPDF_Boolean>(1, false);
656 arr->InsertNewAt<CPDF_Number>(2, 0);
657 arr->InsertNewAt<CPDF_Number>(3, -1234);
658 arr->InsertNewAt<CPDF_Number>(4, 2345.0f);
659 arr->InsertNewAt<CPDF_Number>(5, 0.05f);
660 arr->InsertNewAt<CPDF_String>(6, "", false);
661 arr->InsertNewAt<CPDF_String>(7, "It is a test!", false);
662 arr->InsertNewAt<CPDF_Name>(8, "NAME");
663 arr->InsertNewAt<CPDF_Name>(9, "test");
664 arr->InsertNewAt<CPDF_Null>(10);
665
666 CPDF_Array* arr_val = arr->InsertNewAt<CPDF_Array>(11);
667 arr_val->AppendNew<CPDF_Number>(1);
668 arr_val->AppendNew<CPDF_Number>(2);
669
670 CPDF_Dictionary* dict_val = arr->InsertNewAt<CPDF_Dictionary>(12);
671 dict_val->SetNewFor<CPDF_String>("key1", "Linda", false);
672 dict_val->SetNewFor<CPDF_String>("key2", "Zoe", false);
673
674 auto stream_dict = pdfium::MakeRetain<CPDF_Dictionary>();
675 stream_dict->SetNewFor<CPDF_String>("key1", "John", false);
676 stream_dict->SetNewFor<CPDF_String>("key2", "King", false);
677 uint8_t data[] = "A stream for test";
678 // The data buffer will be owned by stream object, so it needs to be
679 // dynamically allocated.
680 size_t buf_size = sizeof(data);
681 std::unique_ptr<uint8_t, FxFreeDeleter> buf(
682 FX_AllocUninit(uint8_t, buf_size));
683 memcpy(buf.get(), data, buf_size);
684 CPDF_Stream* stream_val = arr->InsertNewAt<CPDF_Stream>(
685 13, std::move(buf), buf_size, stream_dict);
686 const char* const expected_str[] = {
687 "true", "false", "0", "-1234", "2345", "0.05", "",
688 "It is a test!", "NAME", "test", "", "", "", ""};
689 const int expected_int[] = {1, 0, 0, -1234, 2345, 0, 0,
690 0, 0, 0, 0, 0, 0, 0};
691 const float expected_float[] = {0, 0, 0, -1234, 2345, 0.05f, 0,
692 0, 0, 0, 0, 0, 0, 0};
693 for (size_t i = 0; i < arr->size(); ++i) {
694 EXPECT_STREQ(expected_str[i], arr->GetStringAt(i).c_str());
695 EXPECT_EQ(expected_int[i], arr->GetIntegerAt(i));
696 EXPECT_EQ(expected_float[i], arr->GetNumberAt(i));
697 if (i == 11)
698 EXPECT_EQ(arr_val, arr->GetArrayAt(i));
699 else
700 EXPECT_EQ(nullptr, arr->GetArrayAt(i));
701 if (i == 13) {
702 EXPECT_EQ(stream_dict, arr->GetDictAt(i));
703 EXPECT_EQ(stream_val, arr->GetStreamAt(i));
704 } else {
705 EXPECT_EQ(nullptr, arr->GetStreamAt(i));
706 if (i == 12)
707 EXPECT_EQ(dict_val, arr->GetDictAt(i));
708 else
709 EXPECT_EQ(nullptr, arr->GetDictAt(i));
710 }
711 }
712 }
713 }
714
TEST(PDFArrayTest,AddNumber)715 TEST(PDFArrayTest, AddNumber) {
716 float vals[] = {1.0f, -1.0f, 0, 0.456734f,
717 12345.54321f, 0.5f, 1000, 0.000045f};
718 auto arr = pdfium::MakeRetain<CPDF_Array>();
719 for (size_t i = 0; i < pdfium::size(vals); ++i)
720 arr->AppendNew<CPDF_Number>(vals[i]);
721 for (size_t i = 0; i < pdfium::size(vals); ++i) {
722 EXPECT_EQ(CPDF_Object::kNumber, arr->GetObjectAt(i)->GetType());
723 EXPECT_EQ(vals[i], arr->GetObjectAt(i)->GetNumber());
724 }
725 }
726
TEST(PDFArrayTest,AddInteger)727 TEST(PDFArrayTest, AddInteger) {
728 int vals[] = {0, 1, 934435456, 876, 10000, -1, -24354656, -100};
729 auto arr = pdfium::MakeRetain<CPDF_Array>();
730 for (size_t i = 0; i < pdfium::size(vals); ++i)
731 arr->AppendNew<CPDF_Number>(vals[i]);
732 for (size_t i = 0; i < pdfium::size(vals); ++i) {
733 EXPECT_EQ(CPDF_Object::kNumber, arr->GetObjectAt(i)->GetType());
734 EXPECT_EQ(vals[i], arr->GetObjectAt(i)->GetNumber());
735 }
736 }
737
TEST(PDFArrayTest,AddStringAndName)738 TEST(PDFArrayTest, AddStringAndName) {
739 static constexpr const char* kVals[] = {
740 "", "a", "ehjhRIOYTTFdfcdnv", "122323",
741 "$#%^&**", " ", "This is a test.\r\n"};
742 auto string_array = pdfium::MakeRetain<CPDF_Array>();
743 auto name_array = pdfium::MakeRetain<CPDF_Array>();
744 for (const char* val : kVals) {
745 string_array->AppendNew<CPDF_String>(val, false);
746 name_array->AppendNew<CPDF_Name>(val);
747 }
748 for (size_t i = 0; i < pdfium::size(kVals); ++i) {
749 EXPECT_EQ(CPDF_Object::kString, string_array->GetObjectAt(i)->GetType());
750 EXPECT_STREQ(kVals[i], string_array->GetObjectAt(i)->GetString().c_str());
751 EXPECT_EQ(CPDF_Object::kName, name_array->GetObjectAt(i)->GetType());
752 EXPECT_STREQ(kVals[i], name_array->GetObjectAt(i)->GetString().c_str());
753 }
754 }
755
TEST(PDFArrayTest,AddReferenceAndGetObjectAt)756 TEST(PDFArrayTest, AddReferenceAndGetObjectAt) {
757 auto holder = std::make_unique<CPDF_IndirectObjectHolder>();
758 auto boolean_obj = pdfium::MakeRetain<CPDF_Boolean>(true);
759 auto int_obj = pdfium::MakeRetain<CPDF_Number>(-1234);
760 auto float_obj = pdfium::MakeRetain<CPDF_Number>(2345.089f);
761 auto str_obj =
762 pdfium::MakeRetain<CPDF_String>(nullptr, "Adsfdsf 343434 %&&*\n", false);
763 auto name_obj = pdfium::MakeRetain<CPDF_Name>(nullptr, "Title:");
764 auto null_obj = pdfium::MakeRetain<CPDF_Null>();
765 RetainPtr<CPDF_Object> indirect_objs[] = {boolean_obj, int_obj, float_obj,
766 str_obj, name_obj, null_obj};
767 unsigned int obj_nums[] = {2, 4, 7, 2345, 799887, 1};
768 auto arr = pdfium::MakeRetain<CPDF_Array>();
769 auto arr1 = pdfium::MakeRetain<CPDF_Array>();
770 // Create two arrays of references by different AddReference() APIs.
771 for (size_t i = 0; i < pdfium::size(indirect_objs); ++i) {
772 holder->ReplaceIndirectObjectIfHigherGeneration(obj_nums[i],
773 indirect_objs[i]);
774 arr->AppendNew<CPDF_Reference>(holder.get(), obj_nums[i]);
775 arr1->AppendNew<CPDF_Reference>(holder.get(),
776 indirect_objs[i]->GetObjNum());
777 }
778 // Check indirect objects.
779 for (size_t i = 0; i < pdfium::size(obj_nums); ++i)
780 EXPECT_EQ(indirect_objs[i], holder->GetOrParseIndirectObject(obj_nums[i]));
781 // Check arrays.
782 EXPECT_EQ(arr->size(), arr1->size());
783 for (size_t i = 0; i < arr->size(); ++i) {
784 EXPECT_EQ(CPDF_Object::kReference, arr->GetObjectAt(i)->GetType());
785 EXPECT_EQ(indirect_objs[i], arr->GetObjectAt(i)->GetDirect());
786 EXPECT_EQ(indirect_objs[i], arr->GetDirectObjectAt(i));
787 EXPECT_EQ(CPDF_Object::kReference, arr1->GetObjectAt(i)->GetType());
788 EXPECT_EQ(indirect_objs[i], arr1->GetObjectAt(i)->GetDirect());
789 EXPECT_EQ(indirect_objs[i], arr1->GetDirectObjectAt(i));
790 }
791 }
792
TEST(PDFArrayTest,CloneDirectObject)793 TEST(PDFArrayTest, CloneDirectObject) {
794 CPDF_IndirectObjectHolder objects_holder;
795 auto array = pdfium::MakeRetain<CPDF_Array>();
796 array->AppendNew<CPDF_Reference>(&objects_holder, 1234);
797 ASSERT_EQ(1U, array->size());
798 CPDF_Object* obj = array->GetObjectAt(0);
799 ASSERT_TRUE(obj);
800 EXPECT_TRUE(obj->IsReference());
801
802 RetainPtr<CPDF_Object> cloned_array_object = array->CloneDirectObject();
803 ASSERT_TRUE(cloned_array_object);
804 ASSERT_TRUE(cloned_array_object->IsArray());
805
806 RetainPtr<CPDF_Array> cloned_array = ToArray(std::move(cloned_array_object));
807 ASSERT_EQ(0U, cloned_array->size());
808 CPDF_Object* cloned_obj = cloned_array->GetObjectAt(0);
809 EXPECT_FALSE(cloned_obj);
810 }
811
TEST(PDFArrayTest,ConvertIndirect)812 TEST(PDFArrayTest, ConvertIndirect) {
813 CPDF_IndirectObjectHolder objects_holder;
814 auto array = pdfium::MakeRetain<CPDF_Array>();
815 CPDF_Object* pObj = array->AppendNew<CPDF_Number>(42);
816 array->ConvertToIndirectObjectAt(0, &objects_holder);
817 CPDF_Object* pRef = array->GetObjectAt(0);
818 CPDF_Object* pNum = array->GetDirectObjectAt(0);
819 EXPECT_TRUE(pRef->IsReference());
820 EXPECT_TRUE(pNum->IsNumber());
821 EXPECT_NE(pObj, pRef);
822 EXPECT_EQ(pObj, pNum);
823 EXPECT_EQ(42, array->GetIntegerAt(0));
824 }
825
TEST(PDFStreamTest,SetData)826 TEST(PDFStreamTest, SetData) {
827 std::vector<uint8_t, FxAllocAllocator<uint8_t>> data(100);
828 auto stream = pdfium::MakeRetain<CPDF_Stream>();
829 stream->InitStream(data, pdfium::MakeRetain<CPDF_Dictionary>());
830 EXPECT_EQ(static_cast<int>(data.size()),
831 stream->GetDict()->GetIntegerFor(pdfium::stream::kLength));
832
833 stream->GetDict()->SetNewFor<CPDF_String>(pdfium::stream::kFilter,
834 L"SomeFilter");
835 stream->GetDict()->SetNewFor<CPDF_String>(pdfium::stream::kDecodeParms,
836 L"SomeParams");
837
838 std::vector<uint8_t, FxAllocAllocator<uint8_t>> new_data(data.size() * 2);
839 stream->SetData(new_data);
840
841 // The "Length" field should be updated for new data size.
842 EXPECT_EQ(static_cast<int>(new_data.size()),
843 stream->GetDict()->GetIntegerFor(pdfium::stream::kLength));
844
845 // The "Filter" and "DecodeParms" fields should not be changed.
846 EXPECT_EQ(stream->GetDict()->GetUnicodeTextFor(pdfium::stream::kFilter),
847 L"SomeFilter");
848 EXPECT_EQ(stream->GetDict()->GetUnicodeTextFor(pdfium::stream::kDecodeParms),
849 L"SomeParams");
850 }
851
TEST(PDFStreamTest,SetDataAndRemoveFilter)852 TEST(PDFStreamTest, SetDataAndRemoveFilter) {
853 std::vector<uint8_t, FxAllocAllocator<uint8_t>> data(100);
854 auto stream = pdfium::MakeRetain<CPDF_Stream>();
855 stream->InitStream(data, pdfium::MakeRetain<CPDF_Dictionary>());
856 EXPECT_EQ(static_cast<int>(data.size()),
857 stream->GetDict()->GetIntegerFor(pdfium::stream::kLength));
858
859 stream->GetDict()->SetNewFor<CPDF_String>(pdfium::stream::kFilter,
860 L"SomeFilter");
861 stream->GetDict()->SetNewFor<CPDF_String>(pdfium::stream::kDecodeParms,
862 L"SomeParams");
863
864 std::vector<uint8_t, FxAllocAllocator<uint8_t>> new_data(data.size() * 2);
865 stream->SetDataAndRemoveFilter(new_data);
866 // The "Length" field should be updated for new data size.
867 EXPECT_EQ(static_cast<int>(new_data.size()),
868 stream->GetDict()->GetIntegerFor(pdfium::stream::kLength));
869
870 // The "Filter" and "DecodeParms" should be removed.
871 EXPECT_FALSE(stream->GetDict()->KeyExist(pdfium::stream::kFilter));
872 EXPECT_FALSE(stream->GetDict()->KeyExist(pdfium::stream::kDecodeParms));
873 }
874
TEST(PDFStreamTest,LengthInDictionaryOnCreate)875 TEST(PDFStreamTest, LengthInDictionaryOnCreate) {
876 static constexpr uint32_t kBufSize = 100;
877 // The length field should be created on stream create.
878 {
879 std::unique_ptr<uint8_t, FxFreeDeleter> data;
880 data.reset(FX_Alloc(uint8_t, kBufSize));
881 auto stream = pdfium::MakeRetain<CPDF_Stream>(
882 std::move(data), kBufSize, pdfium::MakeRetain<CPDF_Dictionary>());
883 EXPECT_EQ(static_cast<int>(kBufSize),
884 stream->GetDict()->GetIntegerFor(pdfium::stream::kLength));
885 }
886 // The length field should be corrected on stream create.
887 {
888 std::unique_ptr<uint8_t, FxFreeDeleter> data;
889 data.reset(FX_Alloc(uint8_t, kBufSize));
890 auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
891 dict->SetNewFor<CPDF_Number>(pdfium::stream::kLength, 30000);
892 auto stream = pdfium::MakeRetain<CPDF_Stream>(std::move(data), kBufSize,
893 std::move(dict));
894 EXPECT_EQ(static_cast<int>(kBufSize),
895 stream->GetDict()->GetIntegerFor(pdfium::stream::kLength));
896 }
897 }
898
TEST(PDFDictionaryTest,CloneDirectObject)899 TEST(PDFDictionaryTest, CloneDirectObject) {
900 CPDF_IndirectObjectHolder objects_holder;
901 auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
902 dict->SetNewFor<CPDF_Reference>("foo", &objects_holder, 1234);
903 ASSERT_EQ(1U, dict->size());
904 CPDF_Object* obj = dict->GetObjectFor("foo");
905 ASSERT_TRUE(obj);
906 EXPECT_TRUE(obj->IsReference());
907
908 RetainPtr<CPDF_Object> cloned_dict_object = dict->CloneDirectObject();
909 ASSERT_TRUE(cloned_dict_object);
910 ASSERT_TRUE(cloned_dict_object->IsDictionary());
911
912 RetainPtr<CPDF_Dictionary> cloned_dict =
913 ToDictionary(std::move(cloned_dict_object));
914 ASSERT_EQ(0U, cloned_dict->size());
915 CPDF_Object* cloned_obj = cloned_dict->GetObjectFor("foo");
916 EXPECT_FALSE(cloned_obj);
917 }
918
TEST(PDFObjectTest,CloneCheckLoop)919 TEST(PDFObjectTest, CloneCheckLoop) {
920 {
921 // Create a dictionary/array pair with a reference loop.
922 auto arr_obj = pdfium::MakeRetain<CPDF_Array>();
923 CPDF_Dictionary* dict_obj = arr_obj->InsertNewAt<CPDF_Dictionary>(0);
924 dict_obj->SetFor("arr", arr_obj);
925 // Clone this object to see whether stack overflow will be triggered.
926 RetainPtr<CPDF_Array> cloned_array = ToArray(arr_obj->Clone());
927 // Cloned object should be the same as the original.
928 ASSERT_TRUE(cloned_array);
929 EXPECT_EQ(1u, cloned_array->size());
930 CPDF_Object* cloned_dict = cloned_array->GetObjectAt(0);
931 ASSERT_TRUE(cloned_dict);
932 ASSERT_TRUE(cloned_dict->IsDictionary());
933 // Recursively referenced object is not cloned.
934 EXPECT_EQ(nullptr, cloned_dict->AsDictionary()->GetObjectFor("arr"));
935 dict_obj->RemoveFor("arr"); // Break deliberate cycle for cleanup.
936 }
937 {
938 // Create a dictionary/stream pair with a reference loop.
939 auto dict_obj = pdfium::MakeRetain<CPDF_Dictionary>();
940 CPDF_Stream* stream_obj =
941 dict_obj->SetNewFor<CPDF_Stream>("stream", nullptr, 0, dict_obj);
942 // Clone this object to see whether stack overflow will be triggered.
943 RetainPtr<CPDF_Stream> cloned_stream = ToStream(stream_obj->Clone());
944 // Cloned object should be the same as the original.
945 ASSERT_TRUE(cloned_stream);
946 CPDF_Object* cloned_dict = cloned_stream->GetDict();
947 ASSERT_TRUE(cloned_dict);
948 ASSERT_TRUE(cloned_dict->IsDictionary());
949 // Recursively referenced object is not cloned.
950 EXPECT_EQ(nullptr, cloned_dict->AsDictionary()->GetObjectFor("stream"));
951 dict_obj->RemoveFor("stream"); // Break deliberate cycle for cleanup.
952 }
953 {
954 CPDF_IndirectObjectHolder objects_holder;
955 // Create an object with a reference loop.
956 CPDF_Dictionary* dict_obj = objects_holder.NewIndirect<CPDF_Dictionary>();
957 RetainPtr<CPDF_Array> arr_obj = pdfium::MakeRetain<CPDF_Array>();
958 arr_obj->InsertNewAt<CPDF_Reference>(0, &objects_holder,
959 dict_obj->GetObjNum());
960 CPDF_Object* elem0 = arr_obj->GetObjectAt(0);
961 dict_obj->SetFor("arr", std::move(arr_obj));
962 EXPECT_EQ(1u, dict_obj->GetObjNum());
963 ASSERT_TRUE(elem0);
964 ASSERT_TRUE(elem0->IsReference());
965 EXPECT_EQ(1u, elem0->AsReference()->GetRefObjNum());
966 EXPECT_EQ(dict_obj, elem0->AsReference()->GetDirect());
967
968 // Clone this object to see whether stack overflow will be triggered.
969 RetainPtr<CPDF_Dictionary> cloned_dict =
970 ToDictionary(dict_obj->CloneDirectObject());
971 // Cloned object should be the same as the original.
972 ASSERT_TRUE(cloned_dict);
973 CPDF_Object* cloned_arr = cloned_dict->GetObjectFor("arr");
974 ASSERT_TRUE(cloned_arr);
975 ASSERT_TRUE(cloned_arr->IsArray());
976 EXPECT_EQ(0U, cloned_arr->AsArray()->size());
977 // Recursively referenced object is not cloned.
978 EXPECT_EQ(nullptr, cloned_arr->AsArray()->GetObjectAt(0));
979 dict_obj->RemoveFor("arr"); // Break deliberate cycle for cleanup.
980 }
981 }
982
TEST(PDFDictionaryTest,ConvertIndirect)983 TEST(PDFDictionaryTest, ConvertIndirect) {
984 CPDF_IndirectObjectHolder objects_holder;
985 auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
986 CPDF_Object* pObj = dict->SetNewFor<CPDF_Number>("clams", 42);
987 dict->ConvertToIndirectObjectFor("clams", &objects_holder);
988 CPDF_Object* pRef = dict->GetObjectFor("clams");
989 CPDF_Object* pNum = dict->GetDirectObjectFor("clams");
990 EXPECT_TRUE(pRef->IsReference());
991 EXPECT_TRUE(pNum->IsNumber());
992 EXPECT_NE(pObj, pRef);
993 EXPECT_EQ(pObj, pNum);
994 EXPECT_EQ(42, dict->GetIntegerFor("clams"));
995 }
996
TEST(PDFDictionaryTest,ExtractObjectOnRemove)997 TEST(PDFDictionaryTest, ExtractObjectOnRemove) {
998 auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
999 CPDF_Object* pObj = dict->SetNewFor<CPDF_Number>("child", 42);
1000 auto extracted_object = dict->RemoveFor("child");
1001 EXPECT_EQ(pObj, extracted_object.Get());
1002
1003 extracted_object = dict->RemoveFor("non_exists_object");
1004 EXPECT_FALSE(extracted_object);
1005 }
1006
TEST(PDFRefernceTest,MakeReferenceToReference)1007 TEST(PDFRefernceTest, MakeReferenceToReference) {
1008 auto obj_holder = std::make_unique<CPDF_IndirectObjectHolder>();
1009 auto original_ref = pdfium::MakeRetain<CPDF_Reference>(obj_holder.get(), 42);
1010 original_ref->SetObjNum(1952);
1011 ASSERT_FALSE(original_ref->IsInline());
1012
1013 auto ref_obj = original_ref->MakeReference(obj_holder.get());
1014
1015 ASSERT_TRUE(ref_obj->IsReference());
1016 // We do not allow reference to reference.
1017 // New reference should have same RefObjNum.
1018 EXPECT_EQ(original_ref->GetRefObjNum(),
1019 ToReference(ref_obj.Get())->GetRefObjNum());
1020 }
1021