Home | History | Annotate | Download | only in parser
      1 // Copyright 2016 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_document.h"
      6 
      7 #include <memory>
      8 #include <utility>
      9 
     10 #include "core/fpdfapi/cpdf_modulemgr.h"
     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_linearized_header.h"
     15 #include "core/fpdfapi/parser/cpdf_name.h"
     16 #include "core/fpdfapi/parser/cpdf_number.h"
     17 #include "core/fpdfapi/parser/cpdf_parser.h"
     18 #include "core/fpdfapi/parser/cpdf_reference.h"
     19 #include "core/fpdfapi/parser/cpdf_string.h"
     20 #include "core/fxcrt/fx_memory.h"
     21 #include "testing/gtest/include/gtest/gtest.h"
     22 #include "third_party/base/ptr_util.h"
     23 
     24 namespace {
     25 
     26 const int kNumTestPages = 7;
     27 
     28 CPDF_Dictionary* CreatePageTreeNode(std::unique_ptr<CPDF_Array> kids,
     29                                     CPDF_Document* pDoc,
     30                                     int count) {
     31   CPDF_Array* pUnowned = pDoc->AddIndirectObject(std::move(kids))->AsArray();
     32   CPDF_Dictionary* pageNode = pDoc->NewIndirect<CPDF_Dictionary>();
     33   pageNode->SetNewFor<CPDF_String>("Type", "Pages", false);
     34   pageNode->SetNewFor<CPDF_Reference>("Kids", pDoc, pUnowned->GetObjNum());
     35   pageNode->SetNewFor<CPDF_Number>("Count", count);
     36   for (size_t i = 0; i < pUnowned->GetCount(); i++) {
     37     pUnowned->GetDictAt(i)->SetNewFor<CPDF_Reference>("Parent", pDoc,
     38                                                       pageNode->GetObjNum());
     39   }
     40   return pageNode;
     41 }
     42 
     43 std::unique_ptr<CPDF_Dictionary> CreateNumberedPage(size_t number) {
     44   auto page = pdfium::MakeUnique<CPDF_Dictionary>();
     45   page->SetNewFor<CPDF_String>("Type", "Page", false);
     46   page->SetNewFor<CPDF_Number>("PageNumbering", static_cast<int>(number));
     47   return page;
     48 }
     49 
     50 class CPDF_TestDocumentForPages : public CPDF_Document {
     51  public:
     52   CPDF_TestDocumentForPages() : CPDF_Document(nullptr) {
     53     // Set up test
     54     auto zeroToTwo = pdfium::MakeUnique<CPDF_Array>();
     55     zeroToTwo->AddNew<CPDF_Reference>(
     56         this, AddIndirectObject(CreateNumberedPage(0))->GetObjNum());
     57     zeroToTwo->AddNew<CPDF_Reference>(
     58         this, AddIndirectObject(CreateNumberedPage(1))->GetObjNum());
     59     zeroToTwo->AddNew<CPDF_Reference>(
     60         this, AddIndirectObject(CreateNumberedPage(2))->GetObjNum());
     61     CPDF_Dictionary* branch1 =
     62         CreatePageTreeNode(std::move(zeroToTwo), this, 3);
     63 
     64     auto zeroToThree = pdfium::MakeUnique<CPDF_Array>();
     65     zeroToThree->AddNew<CPDF_Reference>(this, branch1->GetObjNum());
     66     zeroToThree->AddNew<CPDF_Reference>(
     67         this, AddIndirectObject(CreateNumberedPage(3))->GetObjNum());
     68     CPDF_Dictionary* branch2 =
     69         CreatePageTreeNode(std::move(zeroToThree), this, 4);
     70 
     71     auto fourFive = pdfium::MakeUnique<CPDF_Array>();
     72     fourFive->AddNew<CPDF_Reference>(
     73         this, AddIndirectObject(CreateNumberedPage(4))->GetObjNum());
     74     fourFive->AddNew<CPDF_Reference>(
     75         this, AddIndirectObject(CreateNumberedPage(5))->GetObjNum());
     76     CPDF_Dictionary* branch3 = CreatePageTreeNode(std::move(fourFive), this, 2);
     77 
     78     auto justSix = pdfium::MakeUnique<CPDF_Array>();
     79     justSix->AddNew<CPDF_Reference>(
     80         this, AddIndirectObject(CreateNumberedPage(6))->GetObjNum());
     81     CPDF_Dictionary* branch4 = CreatePageTreeNode(std::move(justSix), this, 1);
     82 
     83     auto allPages = pdfium::MakeUnique<CPDF_Array>();
     84     allPages->AddNew<CPDF_Reference>(this, branch2->GetObjNum());
     85     allPages->AddNew<CPDF_Reference>(this, branch3->GetObjNum());
     86     allPages->AddNew<CPDF_Reference>(this, branch4->GetObjNum());
     87     CPDF_Dictionary* pagesDict =
     88         CreatePageTreeNode(std::move(allPages), this, kNumTestPages);
     89 
     90     m_pOwnedRootDict = pdfium::MakeUnique<CPDF_Dictionary>();
     91     m_pOwnedRootDict->SetNewFor<CPDF_Reference>("Pages", this,
     92                                                 pagesDict->GetObjNum());
     93     m_pRootDict = m_pOwnedRootDict.get();
     94     m_PageList.resize(kNumTestPages);
     95   }
     96 
     97   void SetTreeSize(int size) {
     98     m_pOwnedRootDict->SetNewFor<CPDF_Number>("Count", size);
     99     m_PageList.resize(size);
    100   }
    101 
    102  private:
    103   std::unique_ptr<CPDF_Dictionary> m_pOwnedRootDict;
    104 };
    105 
    106 class CPDF_TestDocumentWithPageWithoutPageNum : public CPDF_Document {
    107  public:
    108   CPDF_TestDocumentWithPageWithoutPageNum() : CPDF_Document(nullptr) {
    109     // Set up test
    110     auto allPages = pdfium::MakeUnique<CPDF_Array>();
    111     allPages->AddNew<CPDF_Reference>(
    112         this, AddIndirectObject(CreateNumberedPage(0))->GetObjNum());
    113     allPages->AddNew<CPDF_Reference>(
    114         this, AddIndirectObject(CreateNumberedPage(1))->GetObjNum());
    115     // Page without pageNum.
    116     inlined_page_ = allPages->Add(CreateNumberedPage(2));
    117     CPDF_Dictionary* pagesDict =
    118         CreatePageTreeNode(std::move(allPages), this, 3);
    119     m_pOwnedRootDict = pdfium::MakeUnique<CPDF_Dictionary>();
    120     m_pOwnedRootDict->SetNewFor<CPDF_Reference>("Pages", this,
    121                                                 pagesDict->GetObjNum());
    122     m_pRootDict = m_pOwnedRootDict.get();
    123     m_PageList.resize(3);
    124   }
    125 
    126   const CPDF_Object* inlined_page() const { return inlined_page_; }
    127 
    128  private:
    129   std::unique_ptr<CPDF_Dictionary> m_pOwnedRootDict;
    130   const CPDF_Object* inlined_page_;
    131 };
    132 
    133 class TestLinearized : public CPDF_LinearizedHeader {
    134  public:
    135   explicit TestLinearized(CPDF_Dictionary* dict)
    136       : CPDF_LinearizedHeader(dict, 0) {}
    137 };
    138 
    139 class CPDF_TestDocPagesWithoutKids : public CPDF_Document {
    140  public:
    141   CPDF_TestDocPagesWithoutKids() : CPDF_Document(nullptr) {
    142     CPDF_Dictionary* pagesDict = NewIndirect<CPDF_Dictionary>();
    143     pagesDict->SetNewFor<CPDF_Name>("Type", "Pages");
    144     pagesDict->SetNewFor<CPDF_Number>("Count", 3);
    145     m_PageList.resize(10);
    146     m_pOwnedRootDict = pdfium::MakeUnique<CPDF_Dictionary>();
    147     m_pOwnedRootDict->SetNewFor<CPDF_Reference>("Pages", this,
    148                                                 pagesDict->GetObjNum());
    149     m_pRootDict = m_pOwnedRootDict.get();
    150   }
    151 
    152  private:
    153   std::unique_ptr<CPDF_Dictionary> m_pOwnedRootDict;
    154 };
    155 }  // namespace
    156 
    157 class cpdf_document_test : public testing::Test {
    158  public:
    159   void SetUp() override { CPDF_ModuleMgr::Get()->Init(); }
    160   void TearDown() override { CPDF_ModuleMgr::Destroy(); }
    161 };
    162 
    163 TEST_F(cpdf_document_test, GetPages) {
    164   std::unique_ptr<CPDF_TestDocumentForPages> document =
    165       pdfium::MakeUnique<CPDF_TestDocumentForPages>();
    166   for (int i = 0; i < kNumTestPages; i++) {
    167     CPDF_Dictionary* page = document->GetPage(i);
    168     ASSERT_TRUE(page);
    169     ASSERT_TRUE(page->KeyExist("PageNumbering"));
    170     EXPECT_EQ(i, page->GetIntegerFor("PageNumbering"));
    171   }
    172   CPDF_Dictionary* page = document->GetPage(kNumTestPages);
    173   EXPECT_FALSE(page);
    174 }
    175 
    176 TEST_F(cpdf_document_test, GetPageWithoutObjNumTwice) {
    177   auto document = pdfium::MakeUnique<CPDF_TestDocumentWithPageWithoutPageNum>();
    178   const CPDF_Dictionary* page = document->GetPage(2);
    179   ASSERT_TRUE(page);
    180   ASSERT_EQ(document->inlined_page(), page);
    181 
    182   const CPDF_Dictionary* second_call_page = document->GetPage(2);
    183   EXPECT_TRUE(second_call_page);
    184   EXPECT_EQ(page, second_call_page);
    185 }
    186 
    187 TEST_F(cpdf_document_test, GetPagesReverseOrder) {
    188   std::unique_ptr<CPDF_TestDocumentForPages> document =
    189       pdfium::MakeUnique<CPDF_TestDocumentForPages>();
    190   for (int i = 6; i >= 0; i--) {
    191     CPDF_Dictionary* page = document->GetPage(i);
    192     ASSERT_TRUE(page);
    193     ASSERT_TRUE(page->KeyExist("PageNumbering"));
    194     EXPECT_EQ(i, page->GetIntegerFor("PageNumbering"));
    195   }
    196   CPDF_Dictionary* page = document->GetPage(kNumTestPages);
    197   EXPECT_FALSE(page);
    198 }
    199 
    200 TEST_F(cpdf_document_test, GetPagesInDisorder) {
    201   std::unique_ptr<CPDF_TestDocumentForPages> document =
    202       pdfium::MakeUnique<CPDF_TestDocumentForPages>();
    203 
    204   CPDF_Dictionary* page = document->GetPage(1);
    205   ASSERT_TRUE(page);
    206   ASSERT_TRUE(page->KeyExist("PageNumbering"));
    207   EXPECT_EQ(1, page->GetIntegerFor("PageNumbering"));
    208 
    209   page = document->GetPage(3);
    210   ASSERT_TRUE(page);
    211   ASSERT_TRUE(page->KeyExist("PageNumbering"));
    212   EXPECT_EQ(3, page->GetIntegerFor("PageNumbering"));
    213 
    214   page = document->GetPage(kNumTestPages);
    215   EXPECT_FALSE(page);
    216 
    217   page = document->GetPage(6);
    218   ASSERT_TRUE(page);
    219   ASSERT_TRUE(page->KeyExist("PageNumbering"));
    220   EXPECT_EQ(6, page->GetIntegerFor("PageNumbering"));
    221 }
    222 
    223 TEST_F(cpdf_document_test, UseCachedPageObjNumIfHaveNotPagesDict) {
    224   // ObjNum can be added in CPDF_DataAvail::IsPageAvail, and PagesDict
    225   // can be not exists in this case.
    226   // (case, when hint table is used to page check in CPDF_DataAvail).
    227   CPDF_Document document(pdfium::MakeUnique<CPDF_Parser>());
    228   auto dict = pdfium::MakeUnique<CPDF_Dictionary>();
    229   dict->SetNewFor<CPDF_Boolean>("Linearized", true);
    230   const int page_count = 100;
    231   dict->SetNewFor<CPDF_Number>("N", page_count);
    232   TestLinearized linearized(dict.get());
    233   document.LoadLinearizedDoc(&linearized);
    234   ASSERT_EQ(page_count, document.GetPageCount());
    235   CPDF_Object* page_stub = document.NewIndirect<CPDF_Dictionary>();
    236   const uint32_t obj_num = page_stub->GetObjNum();
    237   const int test_page_num = 33;
    238 
    239   EXPECT_FALSE(document.IsPageLoaded(test_page_num));
    240   EXPECT_EQ(nullptr, document.GetPage(test_page_num));
    241 
    242   document.SetPageObjNum(test_page_num, obj_num);
    243   EXPECT_TRUE(document.IsPageLoaded(test_page_num));
    244   EXPECT_EQ(page_stub, document.GetPage(test_page_num));
    245 }
    246 
    247 TEST_F(cpdf_document_test, CountGreaterThanPageTree) {
    248   std::unique_ptr<CPDF_TestDocumentForPages> document =
    249       pdfium::MakeUnique<CPDF_TestDocumentForPages>();
    250   document->SetTreeSize(kNumTestPages + 3);
    251   for (int i = 0; i < kNumTestPages; i++)
    252     EXPECT_TRUE(document->GetPage(i));
    253   for (int i = kNumTestPages; i < kNumTestPages + 4; i++)
    254     EXPECT_FALSE(document->GetPage(i));
    255   EXPECT_TRUE(document->GetPage(kNumTestPages - 1));
    256 }
    257 
    258 TEST_F(cpdf_document_test, PagesWithoutKids) {
    259   // Set up a document with Pages dict without kids, and Count = 3
    260   auto pDoc = pdfium::MakeUnique<CPDF_TestDocPagesWithoutKids>();
    261   EXPECT_TRUE(pDoc->GetPage(0));
    262   // Test GetPage does not fetch pages out of range
    263   for (int i = 1; i < 5; i++)
    264     EXPECT_FALSE(pDoc->GetPage(i));
    265 
    266   EXPECT_TRUE(pDoc->GetPage(0));
    267 }
    268