blob: 664ce393813b9e57ebca1ce0e9a64d367bac3abc [file] [log] [blame]
Wei Li5227e572016-03-04 15:49:17 -08001// 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
dsinclair39c62fd2016-09-29 12:49:17 -070010#include "core/fpdfapi/cpdf_modulemgr.h"
dsinclairc59fa882016-11-08 06:55:40 -080011#include "core/fpdfapi/parser/cpdf_array.h"
dsinclair488b7ad2016-10-04 11:55:50 -070012#include "core/fpdfapi/parser/cpdf_document.h"
13#include "core/fpdfapi/parser/cpdf_name.h"
dsinclairc59fa882016-11-08 06:55:40 -080014#include "core/fpdfapi/parser/cpdf_null.h"
dsinclair488b7ad2016-10-04 11:55:50 -070015#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"
dsinclairc59fa882016-11-08 06:55:40 -080019#include "core/fpdfdoc/cpdf_dest.h"
Wei Li5227e572016-03-04 15:49:17 -080020#include "testing/gtest/include/gtest/gtest.h"
21#include "testing/test_support.h"
tsepez36eb4bd2016-10-03 15:24:27 -070022#include "third_party/base/ptr_util.h"
Wei Li5227e572016-03-04 15:49:17 -080023
24#ifdef PDF_ENABLE_XFA
dsinclair521b7502016-11-02 13:02:28 -070025#include "fpdfsdk/fpdfxfa/cpdfxfa_context.h"
Wei Li5227e572016-03-04 15:49:17 -080026#endif // PDF_ENABLE_XFA
27
28class CPDF_TestDocument : public CPDF_Document {
29 public:
tsepeze5cb0b12016-10-26 15:06:11 -070030 CPDF_TestDocument() : CPDF_Document(nullptr) {}
thestig931bf372016-04-26 22:24:30 -070031
Wei Li5227e572016-03-04 15:49:17 -080032 void SetRoot(CPDF_Dictionary* root) { m_pRootDict = root; }
33 CPDF_IndirectObjectHolder* GetHolder() { return this; }
34};
35
36#ifdef PDF_ENABLE_XFA
dsinclair521b7502016-11-02 13:02:28 -070037class CPDF_TestXFAContext : public CPDFXFA_Context {
Wei Li5227e572016-03-04 15:49:17 -080038 public:
dsinclair521b7502016-11-02 13:02:28 -070039 CPDF_TestXFAContext()
40 : CPDFXFA_Context(pdfium::MakeUnique<CPDF_TestDocument>()) {}
Wei Li5227e572016-03-04 15:49:17 -080041
42 void SetRoot(CPDF_Dictionary* root) {
43 reinterpret_cast<CPDF_TestDocument*>(GetPDFDoc())->SetRoot(root);
44 }
45
46 CPDF_IndirectObjectHolder* GetHolder() { return GetPDFDoc(); }
47};
dsinclair521b7502016-11-02 13:02:28 -070048using CPDF_TestPdfDocument = CPDF_TestXFAContext;
Wei Li5227e572016-03-04 15:49:17 -080049#else // PDF_ENABLE_XFA
50using CPDF_TestPdfDocument = CPDF_TestDocument;
51#endif // PDF_ENABLE_XFA
52
53class PDFDocTest : public testing::Test {
54 public:
55 struct DictObjInfo {
tsepezc3255f52016-03-25 14:52:27 -070056 uint32_t num;
Wei Li5227e572016-03-04 15:49:17 -080057 CPDF_Dictionary* obj;
58 };
59
60 void SetUp() override {
61 // We don't need page module or render module, but
62 // initialize them to keep the code sane.
Wei Li5227e572016-03-04 15:49:17 -080063 CPDF_ModuleMgr* module_mgr = CPDF_ModuleMgr::Get();
64 module_mgr->InitPageModule();
Wei Li5227e572016-03-04 15:49:17 -080065
tsepez36eb4bd2016-10-03 15:24:27 -070066 m_pDoc = pdfium::MakeUnique<CPDF_TestPdfDocument>();
Wei Li5227e572016-03-04 15:49:17 -080067 m_pIndirectObjs = m_pDoc->GetHolder();
68 // Setup the root directory.
tsepezcd5bca42016-09-30 10:45:06 -070069 m_pRootObj.reset(new CPDF_Dictionary());
Wei Li5227e572016-03-04 15:49:17 -080070 m_pDoc->SetRoot(m_pRootObj.get());
71 }
72
thestigfd36b8f2016-07-11 10:43:48 -070073 void TearDown() override {
74 m_pRootObj.reset();
75 m_pIndirectObjs = nullptr;
76 m_pDoc.reset();
77 CPDF_ModuleMgr::Destroy();
78 }
79
Wei Li5227e572016-03-04 15:49:17 -080080 std::vector<DictObjInfo> CreateDictObjs(int num) {
81 std::vector<DictObjInfo> info;
82 for (int i = 0; i < num; ++i) {
83 // Objects created will be released by the document.
tsepez70c4afd2016-11-15 11:33:44 -080084 CPDF_Dictionary* obj = m_pIndirectObjs->NewIndirect<CPDF_Dictionary>();
85 info.push_back({obj->GetObjNum(), obj});
Wei Li5227e572016-03-04 15:49:17 -080086 }
87 return info;
88 }
89
90 protected:
91 std::unique_ptr<CPDF_TestPdfDocument> m_pDoc;
92 CPDF_IndirectObjectHolder* m_pIndirectObjs;
tsepez33fdebc2016-11-04 11:38:40 -070093 std::unique_ptr<CPDF_Dictionary> m_pRootObj;
Wei Li5227e572016-03-04 15:49:17 -080094};
95
96TEST_F(PDFDocTest, FindBookmark) {
97 {
98 // No bookmark information.
99 std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
100 GetFPDFWideString(L"");
101 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
102
103 title = GetFPDFWideString(L"Preface");
104 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
105 }
106 {
107 // Empty bookmark tree.
tsepez0e606b52016-11-18 16:22:41 -0800108 m_pRootObj->SetNewFor<CPDF_Dictionary>("Outlines");
Wei Li5227e572016-03-04 15:49:17 -0800109 std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
110 GetFPDFWideString(L"");
111 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
112
113 title = GetFPDFWideString(L"Preface");
114 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
115 }
116 {
117 // Check on a regular bookmark tree.
118 auto bookmarks = CreateDictObjs(3);
119
tsepez0e606b52016-11-18 16:22:41 -0800120 bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
121 bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
122 bookmarks[0].num);
123 bookmarks[1].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs,
124 bookmarks[2].num);
Wei Li5227e572016-03-04 15:49:17 -0800125
tsepez0e606b52016-11-18 16:22:41 -0800126 bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
127 bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
128 bookmarks[0].num);
129 bookmarks[2].obj->SetNewFor<CPDF_Reference>("Prev", m_pIndirectObjs,
130 bookmarks[1].num);
Wei Li5227e572016-03-04 15:49:17 -0800131
tsepez0e606b52016-11-18 16:22:41 -0800132 bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
133 bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
134 bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs,
135 bookmarks[1].num);
136 bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs,
137 bookmarks[2].num);
Wei Li5227e572016-03-04 15:49:17 -0800138
tsepez0e606b52016-11-18 16:22:41 -0800139 m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs,
140 bookmarks[0].num);
Wei Li5227e572016-03-04 15:49:17 -0800141
142 // Title with no match.
143 std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
144 GetFPDFWideString(L"Chapter 3");
145 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
146
147 // Title with partial match only.
148 title = GetFPDFWideString(L"Chapter");
149 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
150
151 // Title with a match.
152 title = GetFPDFWideString(L"Chapter 2");
153 EXPECT_EQ(bookmarks[2].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
154
155 // Title match is case insensitive.
156 title = GetFPDFWideString(L"cHaPter 2");
157 EXPECT_EQ(bookmarks[2].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
158 }
159 {
160 // Circular bookmarks in depth.
161 auto bookmarks = CreateDictObjs(3);
162
tsepez0e606b52016-11-18 16:22:41 -0800163 bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
164 bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
165 bookmarks[0].num);
166 bookmarks[1].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs,
167 bookmarks[2].num);
Wei Li5227e572016-03-04 15:49:17 -0800168
tsepez0e606b52016-11-18 16:22:41 -0800169 bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
170 bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
171 bookmarks[1].num);
172 bookmarks[2].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs,
173 bookmarks[1].num);
Wei Li5227e572016-03-04 15:49:17 -0800174
tsepez0e606b52016-11-18 16:22:41 -0800175 bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
176 bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
177 bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs,
178 bookmarks[1].num);
179 bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs,
180 bookmarks[2].num);
Wei Li5227e572016-03-04 15:49:17 -0800181
tsepez0e606b52016-11-18 16:22:41 -0800182 m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs,
183 bookmarks[0].num);
Wei Li5227e572016-03-04 15:49:17 -0800184
185 // Title with no match.
186 std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
187 GetFPDFWideString(L"Chapter 3");
188 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
189
190 // Title with a match.
191 title = GetFPDFWideString(L"Chapter 2");
192 EXPECT_EQ(bookmarks[2].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
193 }
194 {
195 // Circular bookmarks in breadth.
196 auto bookmarks = CreateDictObjs(4);
197
tsepez0e606b52016-11-18 16:22:41 -0800198 bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
199 bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
200 bookmarks[0].num);
201 bookmarks[1].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs,
202 bookmarks[2].num);
Wei Li5227e572016-03-04 15:49:17 -0800203
tsepez0e606b52016-11-18 16:22:41 -0800204 bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
205 bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
206 bookmarks[0].num);
207 bookmarks[2].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs,
208 bookmarks[3].num);
Wei Li5227e572016-03-04 15:49:17 -0800209
tsepez0e606b52016-11-18 16:22:41 -0800210 bookmarks[3].obj->SetNewFor<CPDF_String>("Title", L"Chapter 3");
211 bookmarks[3].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
212 bookmarks[0].num);
213 bookmarks[3].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs,
214 bookmarks[1].num);
Wei Li5227e572016-03-04 15:49:17 -0800215
tsepez0e606b52016-11-18 16:22:41 -0800216 bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
217 bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
218 bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs,
219 bookmarks[1].num);
220 bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs,
221 bookmarks[2].num);
Wei Li5227e572016-03-04 15:49:17 -0800222
tsepez0e606b52016-11-18 16:22:41 -0800223 m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs,
224 bookmarks[0].num);
Wei Li5227e572016-03-04 15:49:17 -0800225
226 // Title with no match.
227 std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
228 GetFPDFWideString(L"Chapter 8");
229 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
230
231 // Title with a match.
232 title = GetFPDFWideString(L"Chapter 3");
233 EXPECT_EQ(bookmarks[3].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
234 }
235}
dsinclairc59fa882016-11-08 06:55:40 -0800236
237TEST_F(PDFDocTest, GetLocationInPage) {
238 auto array = pdfium::MakeUnique<CPDF_Array>();
tsepez8a3aa452016-11-16 12:26:06 -0800239 array->AddNew<CPDF_Number>(0); // Page Index.
240 array->AddNew<CPDF_Name>("XYZ");
241 array->AddNew<CPDF_Number>(4); // X
242 array->AddNew<CPDF_Number>(5); // Y
243 array->AddNew<CPDF_Number>(6); // Zoom.
dsinclairc59fa882016-11-08 06:55:40 -0800244
245 FPDF_BOOL hasX;
246 FPDF_BOOL hasY;
247 FPDF_BOOL hasZoom;
248 FS_FLOAT x;
249 FS_FLOAT y;
250 FS_FLOAT zoom;
251
252 EXPECT_TRUE(FPDFDest_GetLocationInPage(array.get(), &hasX, &hasY, &hasZoom,
253 &x, &y, &zoom));
254 EXPECT_TRUE(hasX);
255 EXPECT_TRUE(hasY);
256 EXPECT_TRUE(hasZoom);
257 EXPECT_EQ(4, x);
258 EXPECT_EQ(5, y);
259 EXPECT_EQ(6, zoom);
260
tsepez8a3aa452016-11-16 12:26:06 -0800261 array->SetNewAt<CPDF_Null>(2);
262 array->SetNewAt<CPDF_Null>(3);
263 array->SetNewAt<CPDF_Null>(4);
dsinclairc59fa882016-11-08 06:55:40 -0800264 EXPECT_TRUE(FPDFDest_GetLocationInPage(array.get(), &hasX, &hasY, &hasZoom,
265 &x, &y, &zoom));
266 EXPECT_FALSE(hasX);
267 EXPECT_FALSE(hasY);
268 EXPECT_FALSE(hasZoom);
269
270 array = pdfium::MakeUnique<CPDF_Array>();
271 EXPECT_FALSE(FPDFDest_GetLocationInPage(array.get(), &hasX, &hasY, &hasZoom,
272 &x, &y, &zoom));
273}