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