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