• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Walk(CPDF_Object * object)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 
TEST(CPDF_ObjectWalkerTest,Simple)57 TEST(CPDF_ObjectWalkerTest, Simple) {
58   EXPECT_EQ(Walk(pdfium::MakeRetain<CPDF_Null>().Get()), "Null");
59   EXPECT_EQ(Walk(pdfium::MakeRetain<CPDF_Dictionary>().Get()), "Dict");
60   EXPECT_EQ(Walk(pdfium::MakeRetain<CPDF_Array>().Get()), "Arr");
61   EXPECT_EQ(Walk(pdfium::MakeRetain<CPDF_String>().Get()), "Str");
62   EXPECT_EQ(Walk(pdfium::MakeRetain<CPDF_Boolean>().Get()), "Bool");
63   EXPECT_EQ(Walk(pdfium::MakeRetain<CPDF_Stream>().Get()), "Stream");
64   EXPECT_EQ(Walk(pdfium::MakeRetain<CPDF_Reference>(nullptr, 0).Get()), "Ref");
65 }
66 
TEST(CPDF_ObjectWalkerTest,CombinedObject)67 TEST(CPDF_ObjectWalkerTest, CombinedObject) {
68   auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
69   dict->SetFor("1", pdfium::MakeRetain<CPDF_String>());
70   dict->SetFor("2", pdfium::MakeRetain<CPDF_Boolean>());
71   auto array = pdfium::MakeRetain<CPDF_Array>();
72   array->Add(pdfium::MakeRetain<CPDF_Reference>(nullptr, 0));
73   array->Add(pdfium::MakeRetain<CPDF_Null>());
74   array->Add(pdfium::MakeRetain<CPDF_Stream>(
75       nullptr, 0, pdfium::MakeRetain<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 
TEST(CPDF_ObjectWalkerTest,GetParent)81 TEST(CPDF_ObjectWalkerTest, GetParent) {
82   auto level_4 = pdfium::MakeRetain<CPDF_Number>(0);
83   auto level_3 = pdfium::MakeRetain<CPDF_Dictionary>();
84   level_3->SetFor("Length", std::move(level_4));
85   auto level_2 =
86       pdfium::MakeRetain<CPDF_Stream>(nullptr, 0, std::move(level_3));
87   auto level_1 = pdfium::MakeRetain<CPDF_Array>();
88   level_1->Add(std::move(level_2));
89   auto level_0 = pdfium::MakeRetain<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 
TEST(CPDF_ObjectWalkerTest,SkipWalkIntoCurrentObject)103 TEST(CPDF_ObjectWalkerTest, SkipWalkIntoCurrentObject) {
104   auto root_array = pdfium::MakeRetain<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 && 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 
TEST(CPDF_ObjectWalkerTest,DictionaryKey)126 TEST(CPDF_ObjectWalkerTest, DictionaryKey) {
127   auto dict = pdfium::MakeRetain<CPDF_Dictionary>();
128   dict->SetFor("1", pdfium::MakeRetain<CPDF_Null>());
129   dict->SetFor("2", pdfium::MakeRetain<CPDF_Null>());
130   dict->SetFor("3", pdfium::MakeRetain<CPDF_Null>());
131   dict->SetFor("4", pdfium::MakeRetain<CPDF_Null>());
132   dict->SetFor("5", pdfium::MakeRetain<CPDF_Null>());
133 
134   CPDF_ObjectWalker walker(dict.Get());
135   while (const CPDF_Object* obj = walker.GetNext()) {
136     if (dict == obj) {
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