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