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