// Copyright 2017 PDFium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "core/fpdfapi/parser/cpdf_object_walker.h" #include #include #include #include "core/fpdfapi/parser/cpdf_array.h" #include "core/fpdfapi/parser/cpdf_boolean.h" #include "core/fpdfapi/parser/cpdf_dictionary.h" #include "core/fpdfapi/parser/cpdf_name.h" #include "core/fpdfapi/parser/cpdf_null.h" #include "core/fpdfapi/parser/cpdf_number.h" #include "core/fpdfapi/parser/cpdf_reference.h" #include "core/fpdfapi/parser/cpdf_stream.h" #include "core/fpdfapi/parser/cpdf_string.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/base/ptr_util.h" namespace { std::string Walk(CPDF_Object* object) { std::ostringstream result; CPDF_ObjectWalker walker(object); while (const CPDF_Object* obj = walker.GetNext()) { if (obj->IsDictionary()) result << " Dict"; else if (obj->IsArray()) result << " Arr"; else if (obj->IsString()) result << " Str"; else if (obj->IsBoolean()) result << " Bool"; else if (obj->IsStream()) result << " Stream"; else if (obj->IsReference()) result << " Ref"; else if (obj->IsNull()) result << " Null"; else result << " Unknown"; } std::string result_str = result.str(); if (!result_str.empty()) { result_str.erase(result_str.begin()); // remove start space } return result_str; } } // namespace TEST(CPDF_ObjectWalkerTest, Simple) { EXPECT_EQ(Walk(pdfium::MakeUnique().get()), "Null"); EXPECT_EQ(Walk(pdfium::MakeUnique().get()), "Dict"); EXPECT_EQ(Walk(pdfium::MakeUnique().get()), "Arr"); EXPECT_EQ(Walk(pdfium::MakeUnique().get()), "Str"); EXPECT_EQ(Walk(pdfium::MakeUnique().get()), "Bool"); EXPECT_EQ(Walk(pdfium::MakeUnique().get()), "Stream"); EXPECT_EQ(Walk(pdfium::MakeUnique(nullptr, 0).get()), "Ref"); } TEST(CPDF_ObjectWalkerTest, CombinedObject) { auto dict = pdfium::MakeUnique(); dict->SetFor("1", pdfium::MakeUnique()); dict->SetFor("2", pdfium::MakeUnique()); auto array = pdfium::MakeUnique(); array->Add(pdfium::MakeUnique(nullptr, 0)); array->Add(pdfium::MakeUnique()); array->Add(pdfium::MakeUnique( nullptr, 0, pdfium::MakeUnique())); dict->SetFor("3", std::move(array)); EXPECT_EQ(Walk(dict.get()), "Dict Str Bool Arr Ref Null Stream Dict"); } TEST(CPDF_ObjectWalkerTest, GetParent) { auto level_4 = pdfium::MakeUnique(); auto level_3 = pdfium::MakeUnique(); level_3->SetFor("AnyObj", std::move(level_4)); auto level_2 = pdfium::MakeUnique(nullptr, 0, std::move(level_3)); auto level_1 = pdfium::MakeUnique(); level_1->Add(std::move(level_2)); auto level_0 = pdfium::MakeUnique(); level_0->SetFor("Array", std::move(level_1)); // We have <>) ]>> // In this case each step will increase depth. // And on each step the prev object should be parent for current. const CPDF_Object* cur_parent = nullptr; CPDF_ObjectWalker walker(level_0.get()); while (const CPDF_Object* obj = walker.GetNext()) { EXPECT_EQ(cur_parent, walker.GetParent()); cur_parent = obj; } } TEST(CPDF_ObjectWalkerTest, SkipWalkIntoCurrentObject) { auto root_array = pdfium::MakeUnique(); // Add 2 null objects into |root_array|. [ null1, null2 ] root_array->AddNew(); root_array->AddNew(); // |root_array| will contain 4 null objects after this. // [ null1, null2, [ null3, null4 ] ] root_array->Add(root_array->Clone()); int non_array_objects = 0; CPDF_ObjectWalker walker(root_array.get()); while (const CPDF_Object* obj = walker.GetNext()) { if (obj != root_array.get() && obj->IsArray()) { // skip other array except root. walker.SkipWalkIntoCurrentObject(); } if (!obj->IsArray()) ++non_array_objects; } // 2 objects from child array should be skipped. EXPECT_EQ(2, non_array_objects); } TEST(CPDF_ObjectWalkerTest, DictionaryKey) { auto dict = pdfium::MakeUnique(); dict->SetFor("1", pdfium::MakeUnique()); dict->SetFor("2", pdfium::MakeUnique()); dict->SetFor("3", pdfium::MakeUnique()); dict->SetFor("4", pdfium::MakeUnique()); dict->SetFor("5", pdfium::MakeUnique()); CPDF_ObjectWalker walker(dict.get()); while (const CPDF_Object* obj = walker.GetNext()) { if (obj == dict.get()) { // Ignore root dictinary object continue; } // Test that, dictionary key is correct. EXPECT_EQ(walker.GetParent()->AsDictionary()->GetObjectFor( walker.dictionary_key()), obj); } }