Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
cpdf_document_unittest.cpp
Go to the documentation of this file.
1// Copyright 2016 The PDFium Authors
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/test_with_page_module.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/fpdfapi/parser/cpdf_test_document.h"
21#include "testing/gtest/include/gtest/gtest.h"
22#include "third_party/base/check.h"
23
24namespace {
25
26const int kNumTestPages = 7;
27
28RetainPtr<CPDF_Dictionary> CreatePageTreeNode(RetainPtr<CPDF_Array> kids,
29 CPDF_Document* pDoc,
30 int count) {
31 uint32_t new_objnum = pDoc->AddIndirectObject(kids);
32 auto pageNode = pDoc->NewIndirect<CPDF_Dictionary>();
33 pageNode->SetNewFor<CPDF_Name>("Type", "Pages");
34 pageNode->SetNewFor<CPDF_Reference>("Kids", pDoc, new_objnum);
35 pageNode->SetNewFor<CPDF_Number>("Count", count);
36 for (size_t i = 0; i < kids->size(); i++) {
37 kids->GetMutableDictAt(i)->SetNewFor<CPDF_Reference>("Parent", pDoc,
38 pageNode->GetObjNum());
39 }
40 return pageNode;
41}
42
43RetainPtr<CPDF_Dictionary> CreateNumberedPage(size_t number) {
44 auto page = pdfium::MakeRetain<CPDF_Dictionary>();
45 page->SetNewFor<CPDF_Name>("Type", "Page");
46 page->SetNewFor<CPDF_Number>("PageNumbering", static_cast<int>(number));
47 return page;
48}
49
50class CPDF_TestDocumentForPages final : public CPDF_TestDocument {
51 public:
52 CPDF_TestDocumentForPages() {
53 // Set up test
54 auto zeroToTwo = pdfium::MakeRetain<CPDF_Array>();
55 zeroToTwo->AppendNew<CPDF_Reference>(
56 this, AddIndirectObject(CreateNumberedPage(0)));
57 zeroToTwo->AppendNew<CPDF_Reference>(
58 this, AddIndirectObject(CreateNumberedPage(1)));
59 zeroToTwo->AppendNew<CPDF_Reference>(
60 this, AddIndirectObject(CreateNumberedPage(2)));
61 RetainPtr<CPDF_Dictionary> branch1 =
62 CreatePageTreeNode(std::move(zeroToTwo), this, 3);
63
64 auto zeroToThree = pdfium::MakeRetain<CPDF_Array>();
65 zeroToThree->AppendNew<CPDF_Reference>(this, branch1->GetObjNum());
66 zeroToThree->AppendNew<CPDF_Reference>(
67 this, AddIndirectObject(CreateNumberedPage(3)));
68 RetainPtr<CPDF_Dictionary> branch2 =
69 CreatePageTreeNode(std::move(zeroToThree), this, 4);
70
71 auto fourFive = pdfium::MakeRetain<CPDF_Array>();
72 fourFive->AppendNew<CPDF_Reference>(
73 this, AddIndirectObject(CreateNumberedPage(4)));
74 fourFive->AppendNew<CPDF_Reference>(
75 this, AddIndirectObject(CreateNumberedPage(5)));
76 RetainPtr<CPDF_Dictionary> branch3 =
77 CreatePageTreeNode(std::move(fourFive), this, 2);
78
79 auto justSix = pdfium::MakeRetain<CPDF_Array>();
80 justSix->AppendNew<CPDF_Reference>(
81 this, AddIndirectObject(CreateNumberedPage(6)));
82 RetainPtr<CPDF_Dictionary> branch4 =
83 CreatePageTreeNode(std::move(justSix), this, 1);
84
85 auto allPages = pdfium::MakeRetain<CPDF_Array>();
86 allPages->AppendNew<CPDF_Reference>(this, branch2->GetObjNum());
87 allPages->AppendNew<CPDF_Reference>(this, branch3->GetObjNum());
88 allPages->AppendNew<CPDF_Reference>(this, branch4->GetObjNum());
89 RetainPtr<CPDF_Dictionary> pagesDict =
90 CreatePageTreeNode(std::move(allPages), this, kNumTestPages);
91
92 SetRootForTesting(NewIndirect<CPDF_Dictionary>());
93 GetMutableRoot()->SetNewFor<CPDF_Reference>("Pages", this,
94 pagesDict->GetObjNum());
95 ResizePageListForTesting(kNumTestPages);
96 }
97
98 void SetTreeSize(int size) {
99 GetMutableRoot()->SetNewFor<CPDF_Number>("Count", size);
100 ResizePageListForTesting(size);
101 }
102};
103
104class CPDF_TestDocumentWithPageWithoutPageNum final : public CPDF_TestDocument {
105 public:
106 CPDF_TestDocumentWithPageWithoutPageNum() {
107 // Set up test
108 auto allPages = pdfium::MakeRetain<CPDF_Array>();
109 allPages->AppendNew<CPDF_Reference>(
110 this, AddIndirectObject(CreateNumberedPage(0)));
111 allPages->AppendNew<CPDF_Reference>(
112 this, AddIndirectObject(CreateNumberedPage(1)));
113 // Page without pageNum.
114 inlined_page_ = CreateNumberedPage(2);
115 allPages->Append(inlined_page_);
116 RetainPtr<CPDF_Dictionary> pagesDict =
117 CreatePageTreeNode(std::move(allPages), this, 3);
118 SetRootForTesting(NewIndirect<CPDF_Dictionary>());
119 GetMutableRoot()->SetNewFor<CPDF_Reference>("Pages", this,
120 pagesDict->GetObjNum());
121 ResizePageListForTesting(3);
122 }
123
124 const CPDF_Object* inlined_page() const { return inlined_page_.Get(); }
125
126 private:
127 RetainPtr<CPDF_Object> inlined_page_;
128};
129
130class TestLinearized final : public CPDF_LinearizedHeader {
131 public:
132 explicit TestLinearized(CPDF_Dictionary* dict)
133 : CPDF_LinearizedHeader(dict, 0) {}
134};
135
136class CPDF_TestDocPagesWithoutKids final : public CPDF_TestDocument {
137 public:
138 CPDF_TestDocPagesWithoutKids() {
139 auto pagesDict = NewIndirect<CPDF_Dictionary>();
140 pagesDict->SetNewFor<CPDF_Name>("Type", "Pages");
141 pagesDict->SetNewFor<CPDF_Number>("Count", 3);
142 ResizePageListForTesting(10);
143 SetRootForTesting(NewIndirect<CPDF_Dictionary>());
144 GetMutableRoot()->SetNewFor<CPDF_Reference>("Pages", this,
145 pagesDict->GetObjNum());
146 }
147};
148
149class CPDF_TestDocumentAllowSetParser final : public CPDF_TestDocument {
150 public:
151 CPDF_TestDocumentAllowSetParser() = default;
152
153 using CPDF_Document::SetParser;
154};
155
156} // namespace
157
158using DocumentTest = TestWithPageModule;
159
160TEST_F(DocumentTest, GetPages) {
161 std::unique_ptr<CPDF_TestDocumentForPages> document =
162 std::make_unique<CPDF_TestDocumentForPages>();
163 for (int i = 0; i < kNumTestPages; i++) {
164 RetainPtr<const CPDF_Dictionary> page = document->GetPageDictionary(i);
165 ASSERT_TRUE(page);
166 ASSERT_TRUE(page->KeyExist("PageNumbering"));
167 EXPECT_EQ(i, page->GetIntegerFor("PageNumbering"));
168 }
169 RetainPtr<const CPDF_Dictionary> page =
170 document->GetPageDictionary(kNumTestPages);
171 EXPECT_FALSE(page);
172}
173
175 auto document = std::make_unique<CPDF_TestDocumentWithPageWithoutPageNum>();
176 RetainPtr<const CPDF_Dictionary> page = document->GetPageDictionary(2);
177 ASSERT_TRUE(page);
178 ASSERT_EQ(document->inlined_page(), page);
179
180 RetainPtr<const CPDF_Dictionary> second_call_page =
181 document->GetPageDictionary(2);
182 EXPECT_TRUE(second_call_page);
183 EXPECT_EQ(page, second_call_page);
184}
185
187 std::unique_ptr<CPDF_TestDocumentForPages> document =
188 std::make_unique<CPDF_TestDocumentForPages>();
189 for (int i = 6; i >= 0; i--) {
190 RetainPtr<const CPDF_Dictionary> page = document->GetPageDictionary(i);
191 ASSERT_TRUE(page);
192 ASSERT_TRUE(page->KeyExist("PageNumbering"));
193 EXPECT_EQ(i, page->GetIntegerFor("PageNumbering"));
194 }
195 RetainPtr<const CPDF_Dictionary> page =
196 document->GetPageDictionary(kNumTestPages);
197 EXPECT_FALSE(page);
198}
199
201 std::unique_ptr<CPDF_TestDocumentForPages> document =
202 std::make_unique<CPDF_TestDocumentForPages>();
203
204 RetainPtr<const CPDF_Dictionary> page = document->GetPageDictionary(1);
205 ASSERT_TRUE(page);
206 ASSERT_TRUE(page->KeyExist("PageNumbering"));
207 EXPECT_EQ(1, page->GetIntegerFor("PageNumbering"));
208
209 page = document->GetPageDictionary(3);
210 ASSERT_TRUE(page);
211 ASSERT_TRUE(page->KeyExist("PageNumbering"));
212 EXPECT_EQ(3, page->GetIntegerFor("PageNumbering"));
213
214 page = document->GetPageDictionary(kNumTestPages);
215 EXPECT_FALSE(page);
216
217 page = document->GetPageDictionary(6);
218 ASSERT_TRUE(page);
219 ASSERT_TRUE(page->KeyExist("PageNumbering"));
220 EXPECT_EQ(6, page->GetIntegerFor("PageNumbering"));
221}
222
223TEST_F(DocumentTest, IsValidPageObject) {
224 CPDF_TestDocumentForPages document;
225
226 auto dict_type_name_page = pdfium::MakeRetain<CPDF_Dictionary>();
227 dict_type_name_page->SetNewFor<CPDF_Name>("Type", "Page");
228 document.AddIndirectObject(dict_type_name_page);
229 EXPECT_TRUE(CPDF_Document::IsValidPageObject(dict_type_name_page.Get()));
230
231 auto dict_type_string_page = pdfium::MakeRetain<CPDF_Dictionary>();
232 dict_type_string_page->SetNewFor<CPDF_String>("Type", "Page", false);
233 document.AddIndirectObject(dict_type_string_page);
234 EXPECT_FALSE(CPDF_Document::IsValidPageObject(dict_type_string_page.Get()));
235
236 auto dict_type_name_font = pdfium::MakeRetain<CPDF_Dictionary>();
237 dict_type_name_font->SetNewFor<CPDF_Name>("Type", "Font");
238 document.AddIndirectObject(dict_type_name_font);
239 EXPECT_FALSE(CPDF_Document::IsValidPageObject(dict_type_name_font.Get()));
240
241 auto obj_no_type = document.NewIndirect<CPDF_Dictionary>();
242 EXPECT_FALSE(CPDF_Document::IsValidPageObject(obj_no_type.Get()));
243}
244
246 // ObjNum can be added in CPDF_DataAvail::IsPageAvail(), and PagesDict may not
247 // exist in this case, e.g. when hint table is used to page check in
248 // CPDF_DataAvail.
249 constexpr int kPageCount = 100;
250 constexpr int kTestPageNum = 33;
251
252 auto linearization_dict = pdfium::MakeRetain<CPDF_Dictionary>();
253 CPDF_TestDocumentAllowSetParser document;
254
255 {
256 auto first_page = CreateNumberedPage(0);
257 ASSERT_TRUE(first_page);
258
259 int first_page_obj_num = document.AddIndirectObject(first_page);
260 ASSERT_NE(kTestPageNum, first_page_obj_num);
261
262 linearization_dict->SetNewFor<CPDF_Boolean>("Linearized", true);
263 linearization_dict->SetNewFor<CPDF_Number>("N", kPageCount);
264 linearization_dict->SetNewFor<CPDF_Number>("O", first_page_obj_num);
265
266 auto parser = std::make_unique<CPDF_Parser>();
267 parser->SetLinearizedHeaderForTesting(
268 std::make_unique<TestLinearized>(linearization_dict.Get()));
269 document.SetParser(std::move(parser));
270 }
271
272 document.LoadPages();
273
274 ASSERT_EQ(kPageCount, document.GetPageCount());
275 auto page_stub = document.NewIndirect<CPDF_Dictionary>();
276 const uint32_t obj_num = page_stub->GetObjNum();
277
278 EXPECT_FALSE(document.IsPageLoaded(kTestPageNum));
279 EXPECT_FALSE(document.GetPageDictionary(kTestPageNum));
280
281 document.SetPageObjNum(kTestPageNum, obj_num);
282 EXPECT_TRUE(document.IsPageLoaded(kTestPageNum));
283 EXPECT_EQ(page_stub, document.GetPageDictionary(kTestPageNum));
284}
285
287 std::unique_ptr<CPDF_TestDocumentForPages> document =
288 std::make_unique<CPDF_TestDocumentForPages>();
289 document->SetTreeSize(kNumTestPages + 3);
290 for (int i = 0; i < kNumTestPages; i++)
291 EXPECT_TRUE(document->GetPageDictionary(i));
292 for (int i = kNumTestPages; i < kNumTestPages + 4; i++)
293 EXPECT_FALSE(document->GetPageDictionary(i));
294 EXPECT_TRUE(document->GetPageDictionary(kNumTestPages - 1));
295}
296
297TEST_F(DocumentTest, PagesWithoutKids) {
298 // Set up a document with Pages dict without kids, and Count = 3
299 auto pDoc = std::make_unique<CPDF_TestDocPagesWithoutKids>();
300 EXPECT_TRUE(pDoc->GetPageDictionary(0));
301 // Test GetPage does not fetch pages out of range
302 for (int i = 1; i < 5; i++)
303 EXPECT_FALSE(pDoc->GetPageDictionary(i));
304
305 EXPECT_TRUE(pDoc->GetPageDictionary(0));
306}
bool IsPageLoaded(int iPage) const
RetainPtr< const CPDF_Dictionary > GetPageDictionary(int iPage)
int GetPageCount() const
void SetRootForTesting(RetainPtr< CPDF_Dictionary > root)
static bool IsValidPageObject(const CPDF_Object *obj)
void SetPageObjNum(int iPage, uint32_t objNum)
uint32_t AddIndirectObject(RetainPtr< CPDF_Object > pObj)
CPDF_LinearizedHeader(const CPDF_Dictionary *pDict, FX_FILESIZE szLastXRefOffset)
TEST_F(CPDF_CIDFontTest, BUG_920636)