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