• 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 "public/fpdf_doc.h"
6 
7 #include <memory>
8 #include <vector>
9 
10 #include "core/fpdfapi/cpdf_modulemgr.h"
11 #include "core/fpdfapi/parser/cpdf_array.h"
12 #include "core/fpdfapi/parser/cpdf_document.h"
13 #include "core/fpdfapi/parser/cpdf_name.h"
14 #include "core/fpdfapi/parser/cpdf_null.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/fpdfdoc/cpdf_dest.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 #include "testing/test_support.h"
22 #include "third_party/base/ptr_util.h"
23 
24 #ifdef PDF_ENABLE_XFA
25 #include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
26 #endif  // PDF_ENABLE_XFA
27 
28 class CPDF_TestDocument : public CPDF_Document {
29  public:
CPDF_TestDocument()30   CPDF_TestDocument() : CPDF_Document(nullptr) {}
31 
SetRoot(CPDF_Dictionary * root)32   void SetRoot(CPDF_Dictionary* root) { m_pRootDict = root; }
GetHolder()33   CPDF_IndirectObjectHolder* GetHolder() { return this; }
34 };
35 
36 #ifdef PDF_ENABLE_XFA
37 class CPDF_TestXFAContext : public CPDFXFA_Context {
38  public:
CPDF_TestXFAContext()39   CPDF_TestXFAContext()
40       : CPDFXFA_Context(pdfium::MakeUnique<CPDF_TestDocument>()) {}
41 
SetRoot(CPDF_Dictionary * root)42   void SetRoot(CPDF_Dictionary* root) {
43     reinterpret_cast<CPDF_TestDocument*>(GetPDFDoc())->SetRoot(root);
44   }
45 
GetHolder()46   CPDF_IndirectObjectHolder* GetHolder() { return GetPDFDoc(); }
47 };
48 using CPDF_TestPdfDocument = CPDF_TestXFAContext;
49 #else   // PDF_ENABLE_XFA
50 using CPDF_TestPdfDocument = CPDF_TestDocument;
51 #endif  // PDF_ENABLE_XFA
52 
53 class PDFDocTest : public testing::Test {
54  public:
55   struct DictObjInfo {
56     uint32_t num;
57     CPDF_Dictionary* obj;
58   };
59 
SetUp()60   void SetUp() override {
61     CPDF_ModuleMgr::Get()->Init();
62 
63     m_pDoc = pdfium::MakeUnique<CPDF_TestPdfDocument>();
64     m_pIndirectObjs = m_pDoc->GetHolder();
65 
66     // Setup the root directory.
67     m_pRootObj = pdfium::MakeUnique<CPDF_Dictionary>();
68     m_pDoc->SetRoot(m_pRootObj.get());
69   }
70 
TearDown()71   void TearDown() override {
72     m_pRootObj.reset();
73     m_pIndirectObjs = nullptr;
74     m_pDoc.reset();
75     CPDF_ModuleMgr::Destroy();
76   }
77 
CreateDictObjs(int num)78   std::vector<DictObjInfo> CreateDictObjs(int num) {
79     std::vector<DictObjInfo> info;
80     for (int i = 0; i < num; ++i) {
81       // Objects created will be released by the document.
82       CPDF_Dictionary* obj = m_pIndirectObjs->NewIndirect<CPDF_Dictionary>();
83       info.push_back({obj->GetObjNum(), obj});
84     }
85     return info;
86   }
87 
88  protected:
89   std::unique_ptr<CPDF_TestPdfDocument> m_pDoc;
90   UnownedPtr<CPDF_IndirectObjectHolder> m_pIndirectObjs;
91   std::unique_ptr<CPDF_Dictionary> m_pRootObj;
92 };
93 
TEST_F(PDFDocTest,FindBookmark)94 TEST_F(PDFDocTest, FindBookmark) {
95   {
96     // No bookmark information.
97     std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
98         GetFPDFWideString(L"");
99     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
100 
101     title = GetFPDFWideString(L"Preface");
102     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
103   }
104   {
105     // Empty bookmark tree.
106     m_pRootObj->SetNewFor<CPDF_Dictionary>("Outlines");
107     std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
108         GetFPDFWideString(L"");
109     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
110 
111     title = GetFPDFWideString(L"Preface");
112     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
113   }
114   {
115     // Check on a regular bookmark tree.
116     auto bookmarks = CreateDictObjs(3);
117 
118     bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
119     bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
120                                                 bookmarks[0].num);
121     bookmarks[1].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs.Get(),
122                                                 bookmarks[2].num);
123 
124     bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
125     bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
126                                                 bookmarks[0].num);
127     bookmarks[2].obj->SetNewFor<CPDF_Reference>("Prev", m_pIndirectObjs.Get(),
128                                                 bookmarks[1].num);
129 
130     bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
131     bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
132     bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
133                                                 bookmarks[1].num);
134     bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs.Get(),
135                                                 bookmarks[2].num);
136 
137     m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs.Get(),
138                                           bookmarks[0].num);
139 
140     // Title with no match.
141     std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
142         GetFPDFWideString(L"Chapter 3");
143     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
144 
145     // Title with partial match only.
146     title = GetFPDFWideString(L"Chapter");
147     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
148 
149     // Title with a match.
150     title = GetFPDFWideString(L"Chapter 2");
151     EXPECT_EQ(bookmarks[2].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
152 
153     // Title match is case insensitive.
154     title = GetFPDFWideString(L"cHaPter 2");
155     EXPECT_EQ(bookmarks[2].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
156   }
157   {
158     // Circular bookmarks in depth.
159     auto bookmarks = CreateDictObjs(3);
160 
161     bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
162     bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
163                                                 bookmarks[0].num);
164     bookmarks[1].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
165                                                 bookmarks[2].num);
166 
167     bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
168     bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
169                                                 bookmarks[1].num);
170     bookmarks[2].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
171                                                 bookmarks[1].num);
172 
173     bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
174     bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
175     bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
176                                                 bookmarks[1].num);
177     bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs.Get(),
178                                                 bookmarks[2].num);
179 
180     m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs.Get(),
181                                           bookmarks[0].num);
182 
183     // Title with no match.
184     std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
185         GetFPDFWideString(L"Chapter 3");
186     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
187 
188     // Title with a match.
189     title = GetFPDFWideString(L"Chapter 2");
190     EXPECT_EQ(bookmarks[2].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
191   }
192   {
193     // Circular bookmarks in breadth.
194     auto bookmarks = CreateDictObjs(4);
195 
196     bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
197     bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
198                                                 bookmarks[0].num);
199     bookmarks[1].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs.Get(),
200                                                 bookmarks[2].num);
201 
202     bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
203     bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
204                                                 bookmarks[0].num);
205     bookmarks[2].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs.Get(),
206                                                 bookmarks[3].num);
207 
208     bookmarks[3].obj->SetNewFor<CPDF_String>("Title", L"Chapter 3");
209     bookmarks[3].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs.Get(),
210                                                 bookmarks[0].num);
211     bookmarks[3].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs.Get(),
212                                                 bookmarks[1].num);
213 
214     bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
215     bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
216     bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs.Get(),
217                                                 bookmarks[1].num);
218     bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs.Get(),
219                                                 bookmarks[2].num);
220 
221     m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs.Get(),
222                                           bookmarks[0].num);
223 
224     // Title with no match.
225     std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
226         GetFPDFWideString(L"Chapter 8");
227     EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
228 
229     // Title with a match.
230     title = GetFPDFWideString(L"Chapter 3");
231     EXPECT_EQ(bookmarks[3].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
232   }
233 }
234 
TEST_F(PDFDocTest,GetLocationInPage)235 TEST_F(PDFDocTest, GetLocationInPage) {
236   auto array = pdfium::MakeUnique<CPDF_Array>();
237   array->AddNew<CPDF_Number>(0);  // Page Index.
238   array->AddNew<CPDF_Name>("XYZ");
239   array->AddNew<CPDF_Number>(4);  // X
240   array->AddNew<CPDF_Number>(5);  // Y
241   array->AddNew<CPDF_Number>(6);  // Zoom.
242 
243   FPDF_BOOL hasX;
244   FPDF_BOOL hasY;
245   FPDF_BOOL hasZoom;
246   FS_FLOAT x;
247   FS_FLOAT y;
248   FS_FLOAT zoom;
249 
250   EXPECT_TRUE(FPDFDest_GetLocationInPage(array.get(), &hasX, &hasY, &hasZoom,
251                                          &x, &y, &zoom));
252   EXPECT_TRUE(hasX);
253   EXPECT_TRUE(hasY);
254   EXPECT_TRUE(hasZoom);
255   EXPECT_EQ(4, x);
256   EXPECT_EQ(5, y);
257   EXPECT_EQ(6, zoom);
258 
259   array->SetNewAt<CPDF_Null>(2);
260   array->SetNewAt<CPDF_Null>(3);
261   array->SetNewAt<CPDF_Null>(4);
262   EXPECT_TRUE(FPDFDest_GetLocationInPage(array.get(), &hasX, &hasY, &hasZoom,
263                                          &x, &y, &zoom));
264   EXPECT_FALSE(hasX);
265   EXPECT_FALSE(hasY);
266   EXPECT_FALSE(hasZoom);
267 
268   array = pdfium::MakeUnique<CPDF_Array>();
269   EXPECT_FALSE(FPDFDest_GetLocationInPage(array.get(), &hasX, &hasY, &hasZoom,
270                                           &x, &y, &zoom));
271 }
272