blob: eb04de69711c9b6be9da8eb3a11c35d0be425838 [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
Dan Sinclairaa403d32016-03-15 14:57:22 -040010#include "core/fpdfapi/fpdf_parser/include/cpdf_document.h"
11#include "core/fpdfapi/fpdf_parser/include/cpdf_name.h"
12#include "core/fpdfapi/fpdf_parser/include/cpdf_number.h"
13#include "core/fpdfapi/fpdf_parser/include/cpdf_reference.h"
14#include "core/fpdfapi/fpdf_parser/include/cpdf_string.h"
15#include "core/fpdfapi/include/cpdf_modulemgr.h"
dsinclaire530fb72016-04-06 12:09:37 -070016#include "core/fpdfdoc/include/fpdf_doc.h"
Wei Li5227e572016-03-04 15:49:17 -080017#include "testing/gtest/include/gtest/gtest.h"
18#include "testing/test_support.h"
19
20#ifdef PDF_ENABLE_XFA
dsinclair89bdd082016-04-06 10:47:54 -070021#include "fpdfsdk/fpdfxfa/include/fpdfxfa_app.h"
22#include "fpdfsdk/fpdfxfa/include/fpdfxfa_doc.h"
Wei Li5227e572016-03-04 15:49:17 -080023#endif // PDF_ENABLE_XFA
24
25class CPDF_TestDocument : public CPDF_Document {
26 public:
thestig931bf372016-04-26 22:24:30 -070027 CPDF_TestDocument() : CPDF_Document(nullptr) {}
28
Wei Li5227e572016-03-04 15:49:17 -080029 void SetRoot(CPDF_Dictionary* root) { m_pRootDict = root; }
30 CPDF_IndirectObjectHolder* GetHolder() { return this; }
31};
32
33#ifdef PDF_ENABLE_XFA
34class CPDF_TestXFADocument : public CPDFXFA_Document {
35 public:
36 CPDF_TestXFADocument()
37 : CPDFXFA_Document(new CPDF_TestDocument(), CPDFXFA_App::GetInstance()) {}
38
39 void SetRoot(CPDF_Dictionary* root) {
40 reinterpret_cast<CPDF_TestDocument*>(GetPDFDoc())->SetRoot(root);
41 }
42
43 CPDF_IndirectObjectHolder* GetHolder() { return GetPDFDoc(); }
44};
45using CPDF_TestPdfDocument = CPDF_TestXFADocument;
46#else // PDF_ENABLE_XFA
47using CPDF_TestPdfDocument = CPDF_TestDocument;
48#endif // PDF_ENABLE_XFA
49
50class PDFDocTest : public testing::Test {
51 public:
52 struct DictObjInfo {
tsepezc3255f52016-03-25 14:52:27 -070053 uint32_t num;
Wei Li5227e572016-03-04 15:49:17 -080054 CPDF_Dictionary* obj;
55 };
56
57 void SetUp() override {
58 // We don't need page module or render module, but
59 // initialize them to keep the code sane.
60 CPDF_ModuleMgr::Create();
61 CPDF_ModuleMgr* module_mgr = CPDF_ModuleMgr::Get();
62 module_mgr->InitPageModule();
Wei Li5227e572016-03-04 15:49:17 -080063
64 m_pDoc.reset(new CPDF_TestPdfDocument());
65 m_pIndirectObjs = m_pDoc->GetHolder();
66 // Setup the root directory.
67 m_pRootObj.reset(new CPDF_Dictionary());
68 m_pDoc->SetRoot(m_pRootObj.get());
69 }
70
71 std::vector<DictObjInfo> CreateDictObjs(int num) {
72 std::vector<DictObjInfo> info;
73 for (int i = 0; i < num; ++i) {
74 // Objects created will be released by the document.
75 CPDF_Dictionary* obj(new CPDF_Dictionary());
76 m_pIndirectObjs->AddIndirectObject(obj);
77 info.push_back({obj->GetObjNum(), obj});
78 }
79 return info;
80 }
81
82 protected:
83 std::unique_ptr<CPDF_TestPdfDocument> m_pDoc;
84 CPDF_IndirectObjectHolder* m_pIndirectObjs;
85 std::unique_ptr<CPDF_Dictionary, ReleaseDeleter<CPDF_Dictionary>> m_pRootObj;
86};
87
88TEST_F(PDFDocTest, FindBookmark) {
89 {
90 // No bookmark information.
91 std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
92 GetFPDFWideString(L"");
93 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
94
95 title = GetFPDFWideString(L"Preface");
96 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
97 }
98 {
99 // Empty bookmark tree.
100 m_pRootObj->SetAt("Outlines", new CPDF_Dictionary());
101 std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
102 GetFPDFWideString(L"");
103 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
104
105 title = GetFPDFWideString(L"Preface");
106 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
107 }
108 {
109 // Check on a regular bookmark tree.
110 auto bookmarks = CreateDictObjs(3);
111
112 bookmarks[1].obj->SetAt("Title", new CPDF_String(L"Chapter 1"));
113 bookmarks[1].obj->SetAt(
114 "Parent", new CPDF_Reference(m_pIndirectObjs, bookmarks[0].num));
115 bookmarks[1].obj->SetAt(
116 "Next", new CPDF_Reference(m_pIndirectObjs, bookmarks[2].num));
117
118 bookmarks[2].obj->SetAt("Title", new CPDF_String(L"Chapter 2"));
119 bookmarks[2].obj->SetAt(
120 "Parent", new CPDF_Reference(m_pIndirectObjs, bookmarks[0].num));
121 bookmarks[2].obj->SetAt(
122 "Prev", new CPDF_Reference(m_pIndirectObjs, bookmarks[1].num));
123
124 bookmarks[0].obj->SetAt("Type", new CPDF_Name("Outlines"));
125 bookmarks[0].obj->SetAt("Count", new CPDF_Number(2));
126 bookmarks[0].obj->SetAt(
127 "First", new CPDF_Reference(m_pIndirectObjs, bookmarks[1].num));
128 bookmarks[0].obj->SetAt(
129 "Last", new CPDF_Reference(m_pIndirectObjs, bookmarks[2].num));
130
131 m_pRootObj->SetAt("Outlines",
132 new CPDF_Reference(m_pIndirectObjs, bookmarks[0].num));
133
134 // Title with no match.
135 std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
136 GetFPDFWideString(L"Chapter 3");
137 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
138
139 // Title with partial match only.
140 title = GetFPDFWideString(L"Chapter");
141 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
142
143 // Title with a match.
144 title = GetFPDFWideString(L"Chapter 2");
145 EXPECT_EQ(bookmarks[2].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
146
147 // Title match is case insensitive.
148 title = GetFPDFWideString(L"cHaPter 2");
149 EXPECT_EQ(bookmarks[2].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
150 }
151 {
152 // Circular bookmarks in depth.
153 auto bookmarks = CreateDictObjs(3);
154
155 bookmarks[1].obj->SetAt("Title", new CPDF_String(L"Chapter 1"));
156 bookmarks[1].obj->SetAt(
157 "Parent", new CPDF_Reference(m_pIndirectObjs, bookmarks[0].num));
158 bookmarks[1].obj->SetAt(
159 "First", new CPDF_Reference(m_pIndirectObjs, bookmarks[2].num));
160
161 bookmarks[2].obj->SetAt("Title", new CPDF_String(L"Chapter 2"));
162 bookmarks[2].obj->SetAt(
163 "Parent", new CPDF_Reference(m_pIndirectObjs, bookmarks[1].num));
164 bookmarks[2].obj->SetAt(
165 "First", new CPDF_Reference(m_pIndirectObjs, bookmarks[1].num));
166
167 bookmarks[0].obj->SetAt("Type", new CPDF_Name("Outlines"));
168 bookmarks[0].obj->SetAt("Count", new CPDF_Number(2));
169 bookmarks[0].obj->SetAt(
170 "First", new CPDF_Reference(m_pIndirectObjs, bookmarks[1].num));
171 bookmarks[0].obj->SetAt(
172 "Last", new CPDF_Reference(m_pIndirectObjs, bookmarks[2].num));
173
174 m_pRootObj->SetAt("Outlines",
175 new CPDF_Reference(m_pIndirectObjs, bookmarks[0].num));
176
177 // Title with no match.
178 std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
179 GetFPDFWideString(L"Chapter 3");
180 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
181
182 // Title with a match.
183 title = GetFPDFWideString(L"Chapter 2");
184 EXPECT_EQ(bookmarks[2].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
185 }
186 {
187 // Circular bookmarks in breadth.
188 auto bookmarks = CreateDictObjs(4);
189
190 bookmarks[1].obj->SetAt("Title", new CPDF_String(L"Chapter 1"));
191 bookmarks[1].obj->SetAt(
192 "Parent", new CPDF_Reference(m_pIndirectObjs, bookmarks[0].num));
193 bookmarks[1].obj->SetAt(
194 "Next", new CPDF_Reference(m_pIndirectObjs, bookmarks[2].num));
195
196 bookmarks[2].obj->SetAt("Title", new CPDF_String(L"Chapter 2"));
197 bookmarks[2].obj->SetAt(
198 "Parent", new CPDF_Reference(m_pIndirectObjs, bookmarks[0].num));
199 bookmarks[2].obj->SetAt(
200 "Next", new CPDF_Reference(m_pIndirectObjs, bookmarks[3].num));
201
202 bookmarks[3].obj->SetAt("Title", new CPDF_String(L"Chapter 3"));
203 bookmarks[3].obj->SetAt(
204 "Parent", new CPDF_Reference(m_pIndirectObjs, bookmarks[0].num));
205 bookmarks[3].obj->SetAt(
206 "Next", new CPDF_Reference(m_pIndirectObjs, bookmarks[1].num));
207
208 bookmarks[0].obj->SetAt("Type", new CPDF_Name("Outlines"));
209 bookmarks[0].obj->SetAt("Count", new CPDF_Number(2));
210 bookmarks[0].obj->SetAt(
211 "First", new CPDF_Reference(m_pIndirectObjs, bookmarks[1].num));
212 bookmarks[0].obj->SetAt(
213 "Last", new CPDF_Reference(m_pIndirectObjs, bookmarks[2].num));
214
215 m_pRootObj->SetAt("Outlines",
216 new CPDF_Reference(m_pIndirectObjs, bookmarks[0].num));
217
218 // Title with no match.
219 std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
220 GetFPDFWideString(L"Chapter 8");
221 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
222
223 // Title with a match.
224 title = GetFPDFWideString(L"Chapter 3");
225 EXPECT_EQ(bookmarks[3].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
226 }
227}