Home | History | Annotate | Download | only in parser
      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