Home | History | Annotate | Download | only in parser
      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 
     22 namespace {
     23 
     24 void 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 
     44 class 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 
    186 TEST_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 
    202 TEST_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 
    218 TEST_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 
    231 TEST_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 
    243 TEST_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 
    264 TEST_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 
    277 TEST_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 
    291 TEST_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 
    301 TEST_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 
    311 TEST_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 
    323 TEST_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 
    392 TEST(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 
    413 TEST(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 
    431 TEST(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 
    676 TEST(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 
    688 TEST(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 
    699 TEST(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 
    717 TEST(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 
    753 TEST(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 
    773 TEST(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 
    787 TEST(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 
    810 TEST(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 
    832 TEST(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 
    856 TEST(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 
    876 TEST(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 
    941 TEST(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 
    955 TEST(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