1 // Copyright 2017 PDFium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "core/fpdfapi/parser/cpdf_object_walker.h" 6 7 #include <sstream> 8 #include <string> 9 #include <utility> 10 11 #include "core/fpdfapi/parser/cpdf_array.h" 12 #include "core/fpdfapi/parser/cpdf_boolean.h" 13 #include "core/fpdfapi/parser/cpdf_dictionary.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 #include "third_party/base/ptr_util.h" 22 23 namespace { 24 25 std::string Walk(CPDF_Object* object) { 26 std::ostringstream result; 27 CPDF_ObjectWalker walker(object); 28 while (const CPDF_Object* obj = walker.GetNext()) { 29 if (obj->IsDictionary()) 30 result << " Dict"; 31 else if (obj->IsArray()) 32 result << " Arr"; 33 else if (obj->IsString()) 34 result << " Str"; 35 else if (obj->IsBoolean()) 36 result << " Bool"; 37 else if (obj->IsStream()) 38 result << " Stream"; 39 else if (obj->IsReference()) 40 result << " Ref"; 41 else if (obj->IsNumber()) 42 result << " Num"; 43 else if (obj->IsNull()) 44 result << " Null"; 45 else 46 result << " Unknown"; 47 } 48 std::string result_str = result.str(); 49 if (!result_str.empty()) { 50 result_str.erase(result_str.begin()); // remove start space 51 } 52 return result_str; 53 } 54 55 } // namespace 56 57 TEST(CPDF_ObjectWalkerTest, Simple) { 58 EXPECT_EQ(Walk(pdfium::MakeUnique<CPDF_Null>().get()), "Null"); 59 EXPECT_EQ(Walk(pdfium::MakeUnique<CPDF_Dictionary>().get()), "Dict"); 60 EXPECT_EQ(Walk(pdfium::MakeUnique<CPDF_Array>().get()), "Arr"); 61 EXPECT_EQ(Walk(pdfium::MakeUnique<CPDF_String>().get()), "Str"); 62 EXPECT_EQ(Walk(pdfium::MakeUnique<CPDF_Boolean>().get()), "Bool"); 63 EXPECT_EQ(Walk(pdfium::MakeUnique<CPDF_Stream>().get()), "Stream"); 64 EXPECT_EQ(Walk(pdfium::MakeUnique<CPDF_Reference>(nullptr, 0).get()), "Ref"); 65 } 66 67 TEST(CPDF_ObjectWalkerTest, CombinedObject) { 68 auto dict = pdfium::MakeUnique<CPDF_Dictionary>(); 69 dict->SetFor("1", pdfium::MakeUnique<CPDF_String>()); 70 dict->SetFor("2", pdfium::MakeUnique<CPDF_Boolean>()); 71 auto array = pdfium::MakeUnique<CPDF_Array>(); 72 array->Add(pdfium::MakeUnique<CPDF_Reference>(nullptr, 0)); 73 array->Add(pdfium::MakeUnique<CPDF_Null>()); 74 array->Add(pdfium::MakeUnique<CPDF_Stream>( 75 nullptr, 0, pdfium::MakeUnique<CPDF_Dictionary>())); 76 dict->SetFor("3", std::move(array)); 77 // The last number for stream length. 78 EXPECT_EQ(Walk(dict.get()), "Dict Str Bool Arr Ref Null Stream Dict Num"); 79 } 80 81 TEST(CPDF_ObjectWalkerTest, GetParent) { 82 auto level_4 = pdfium::MakeUnique<CPDF_Number>(0); 83 auto level_3 = pdfium::MakeUnique<CPDF_Dictionary>(); 84 level_3->SetFor("Length", std::move(level_4)); 85 auto level_2 = 86 pdfium::MakeUnique<CPDF_Stream>(nullptr, 0, std::move(level_3)); 87 auto level_1 = pdfium::MakeUnique<CPDF_Array>(); 88 level_1->Add(std::move(level_2)); 89 auto level_0 = pdfium::MakeUnique<CPDF_Dictionary>(); 90 level_0->SetFor("Array", std::move(level_1)); 91 92 // We have <</Array [ stream( << /Length 0 >>) ]>> 93 // In this case each step will increase depth. 94 // And on each step the prev object should be parent for current. 95 const CPDF_Object* cur_parent = nullptr; 96 CPDF_ObjectWalker walker(level_0.get()); 97 while (const CPDF_Object* obj = walker.GetNext()) { 98 EXPECT_EQ(cur_parent, walker.GetParent()); 99 cur_parent = obj; 100 } 101 } 102 103 TEST(CPDF_ObjectWalkerTest, SkipWalkIntoCurrentObject) { 104 auto root_array = pdfium::MakeUnique<CPDF_Array>(); 105 // Add 2 null objects into |root_array|. [ null1, null2 ] 106 root_array->AddNew<CPDF_Null>(); 107 root_array->AddNew<CPDF_Null>(); 108 // |root_array| will contain 4 null objects after this. 109 // [ null1, null2, [ null3, null4 ] ] 110 root_array->Add(root_array->Clone()); 111 112 int non_array_objects = 0; 113 CPDF_ObjectWalker walker(root_array.get()); 114 while (const CPDF_Object* obj = walker.GetNext()) { 115 if (obj != root_array.get() && obj->IsArray()) { 116 // skip other array except root. 117 walker.SkipWalkIntoCurrentObject(); 118 } 119 if (!obj->IsArray()) 120 ++non_array_objects; 121 } 122 // 2 objects from child array should be skipped. 123 EXPECT_EQ(2, non_array_objects); 124 } 125 126 TEST(CPDF_ObjectWalkerTest, DictionaryKey) { 127 auto dict = pdfium::MakeUnique<CPDF_Dictionary>(); 128 dict->SetFor("1", pdfium::MakeUnique<CPDF_Null>()); 129 dict->SetFor("2", pdfium::MakeUnique<CPDF_Null>()); 130 dict->SetFor("3", pdfium::MakeUnique<CPDF_Null>()); 131 dict->SetFor("4", pdfium::MakeUnique<CPDF_Null>()); 132 dict->SetFor("5", pdfium::MakeUnique<CPDF_Null>()); 133 134 CPDF_ObjectWalker walker(dict.get()); 135 while (const CPDF_Object* obj = walker.GetNext()) { 136 if (obj == dict.get()) { 137 // Ignore root dictinary object 138 continue; 139 } 140 // Test that, dictionary key is correct. 141 EXPECT_EQ(walker.GetParent()->AsDictionary()->GetObjectFor( 142 walker.dictionary_key()), 143 obj); 144 } 145 } 146