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