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