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