blob: 7ac8ce62d500d0956528edbdf0204be200dc7197 [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"
dsinclair488b7ad2016-10-04 11:55:50 -070011#include "core/fpdfapi/parser/cpdf_document.h"
12#include "core/fpdfapi/parser/cpdf_name.h"
13#include "core/fpdfapi/parser/cpdf_number.h"
14#include "core/fpdfapi/parser/cpdf_parser.h"
15#include "core/fpdfapi/parser/cpdf_reference.h"
16#include "core/fpdfapi/parser/cpdf_string.h"
Wei Li5227e572016-03-04 15:49:17 -080017#include "testing/gtest/include/gtest/gtest.h"
18#include "testing/test_support.h"
tsepez36eb4bd2016-10-03 15:24:27 -070019#include "third_party/base/ptr_util.h"
Wei Li5227e572016-03-04 15:49:17 -080020
21#ifdef PDF_ENABLE_XFA
dsinclair4d29e782016-10-04 14:02:47 -070022#include "fpdfsdk/fpdfxfa/cpdfxfa_document.h"
Wei Li5227e572016-03-04 15:49:17 -080023#endif // PDF_ENABLE_XFA
24
25class CPDF_TestDocument : public CPDF_Document {
26 public:
tsepeze5cb0b12016-10-26 15:06:11 -070027 CPDF_TestDocument() : CPDF_Document(nullptr) {}
thestig931bf372016-04-26 22:24:30 -070028
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()
dsinclair8837c912016-11-01 11:22:37 -070037 : CPDFXFA_Document(pdfium::MakeUnique<CPDF_TestDocument>()) {}
Wei Li5227e572016-03-04 15:49:17 -080038
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.
Wei Li5227e572016-03-04 15:49:17 -080060 CPDF_ModuleMgr* module_mgr = CPDF_ModuleMgr::Get();
61 module_mgr->InitPageModule();
Wei Li5227e572016-03-04 15:49:17 -080062
tsepez36eb4bd2016-10-03 15:24:27 -070063 m_pDoc = pdfium::MakeUnique<CPDF_TestPdfDocument>();
Wei Li5227e572016-03-04 15:49:17 -080064 m_pIndirectObjs = m_pDoc->GetHolder();
65 // Setup the root directory.
tsepezcd5bca42016-09-30 10:45:06 -070066 m_pRootObj.reset(new CPDF_Dictionary());
Wei Li5227e572016-03-04 15:49:17 -080067 m_pDoc->SetRoot(m_pRootObj.get());
68 }
69
thestigfd36b8f2016-07-11 10:43:48 -070070 void TearDown() override {
71 m_pRootObj.reset();
72 m_pIndirectObjs = nullptr;
73 m_pDoc.reset();
74 CPDF_ModuleMgr::Destroy();
75 }
76
Wei Li5227e572016-03-04 15:49:17 -080077 std::vector<DictObjInfo> CreateDictObjs(int num) {
78 std::vector<DictObjInfo> info;
79 for (int i = 0; i < num; ++i) {
80 // Objects created will be released by the document.
Tom Sepezc25a4212016-10-14 17:45:56 -070081 CPDF_Dictionary* obj = new CPDF_Dictionary();
82 info.push_back({m_pIndirectObjs->AddIndirectObject(obj), obj});
Wei Li5227e572016-03-04 15:49:17 -080083 }
84 return info;
85 }
86
87 protected:
88 std::unique_ptr<CPDF_TestPdfDocument> m_pDoc;
89 CPDF_IndirectObjectHolder* m_pIndirectObjs;
90 std::unique_ptr<CPDF_Dictionary, ReleaseDeleter<CPDF_Dictionary>> m_pRootObj;
91};
92
93TEST_F(PDFDocTest, FindBookmark) {
94 {
95 // No bookmark information.
96 std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
97 GetFPDFWideString(L"");
98 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
99
100 title = GetFPDFWideString(L"Preface");
101 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
102 }
103 {
104 // Empty bookmark tree.
tsepezcd5bca42016-09-30 10:45:06 -0700105 m_pRootObj->SetFor("Outlines", new CPDF_Dictionary());
Wei Li5227e572016-03-04 15:49:17 -0800106 std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
107 GetFPDFWideString(L"");
108 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
109
110 title = GetFPDFWideString(L"Preface");
111 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
112 }
113 {
114 // Check on a regular bookmark tree.
115 auto bookmarks = CreateDictObjs(3);
116
dsinclair38fd8442016-09-15 10:15:32 -0700117 bookmarks[1].obj->SetFor("Title", new CPDF_String(L"Chapter 1"));
118 bookmarks[1].obj->SetFor(
Wei Li5227e572016-03-04 15:49:17 -0800119 "Parent", new CPDF_Reference(m_pIndirectObjs, bookmarks[0].num));
dsinclair38fd8442016-09-15 10:15:32 -0700120 bookmarks[1].obj->SetFor(
Wei Li5227e572016-03-04 15:49:17 -0800121 "Next", new CPDF_Reference(m_pIndirectObjs, bookmarks[2].num));
122
dsinclair38fd8442016-09-15 10:15:32 -0700123 bookmarks[2].obj->SetFor("Title", new CPDF_String(L"Chapter 2"));
124 bookmarks[2].obj->SetFor(
Wei Li5227e572016-03-04 15:49:17 -0800125 "Parent", new CPDF_Reference(m_pIndirectObjs, bookmarks[0].num));
dsinclair38fd8442016-09-15 10:15:32 -0700126 bookmarks[2].obj->SetFor(
Wei Li5227e572016-03-04 15:49:17 -0800127 "Prev", new CPDF_Reference(m_pIndirectObjs, bookmarks[1].num));
128
dsinclair38fd8442016-09-15 10:15:32 -0700129 bookmarks[0].obj->SetFor("Type", new CPDF_Name("Outlines"));
130 bookmarks[0].obj->SetFor("Count", new CPDF_Number(2));
131 bookmarks[0].obj->SetFor(
Wei Li5227e572016-03-04 15:49:17 -0800132 "First", new CPDF_Reference(m_pIndirectObjs, bookmarks[1].num));
dsinclair38fd8442016-09-15 10:15:32 -0700133 bookmarks[0].obj->SetFor(
Wei Li5227e572016-03-04 15:49:17 -0800134 "Last", new CPDF_Reference(m_pIndirectObjs, bookmarks[2].num));
135
dsinclair38fd8442016-09-15 10:15:32 -0700136 m_pRootObj->SetFor("Outlines",
137 new CPDF_Reference(m_pIndirectObjs, bookmarks[0].num));
Wei Li5227e572016-03-04 15:49:17 -0800138
139 // Title with no match.
140 std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
141 GetFPDFWideString(L"Chapter 3");
142 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
143
144 // Title with partial match only.
145 title = GetFPDFWideString(L"Chapter");
146 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
147
148 // Title with a match.
149 title = GetFPDFWideString(L"Chapter 2");
150 EXPECT_EQ(bookmarks[2].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
151
152 // Title match is case insensitive.
153 title = GetFPDFWideString(L"cHaPter 2");
154 EXPECT_EQ(bookmarks[2].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
155 }
156 {
157 // Circular bookmarks in depth.
158 auto bookmarks = CreateDictObjs(3);
159
dsinclair38fd8442016-09-15 10:15:32 -0700160 bookmarks[1].obj->SetFor("Title", new CPDF_String(L"Chapter 1"));
161 bookmarks[1].obj->SetFor(
Wei Li5227e572016-03-04 15:49:17 -0800162 "Parent", new CPDF_Reference(m_pIndirectObjs, bookmarks[0].num));
dsinclair38fd8442016-09-15 10:15:32 -0700163 bookmarks[1].obj->SetFor(
Wei Li5227e572016-03-04 15:49:17 -0800164 "First", new CPDF_Reference(m_pIndirectObjs, bookmarks[2].num));
165
dsinclair38fd8442016-09-15 10:15:32 -0700166 bookmarks[2].obj->SetFor("Title", new CPDF_String(L"Chapter 2"));
167 bookmarks[2].obj->SetFor(
Wei Li5227e572016-03-04 15:49:17 -0800168 "Parent", new CPDF_Reference(m_pIndirectObjs, bookmarks[1].num));
dsinclair38fd8442016-09-15 10:15:32 -0700169 bookmarks[2].obj->SetFor(
Wei Li5227e572016-03-04 15:49:17 -0800170 "First", new CPDF_Reference(m_pIndirectObjs, bookmarks[1].num));
171
dsinclair38fd8442016-09-15 10:15:32 -0700172 bookmarks[0].obj->SetFor("Type", new CPDF_Name("Outlines"));
173 bookmarks[0].obj->SetFor("Count", new CPDF_Number(2));
174 bookmarks[0].obj->SetFor(
Wei Li5227e572016-03-04 15:49:17 -0800175 "First", new CPDF_Reference(m_pIndirectObjs, bookmarks[1].num));
dsinclair38fd8442016-09-15 10:15:32 -0700176 bookmarks[0].obj->SetFor(
Wei Li5227e572016-03-04 15:49:17 -0800177 "Last", new CPDF_Reference(m_pIndirectObjs, bookmarks[2].num));
178
dsinclair38fd8442016-09-15 10:15:32 -0700179 m_pRootObj->SetFor("Outlines",
180 new CPDF_Reference(m_pIndirectObjs, bookmarks[0].num));
Wei Li5227e572016-03-04 15:49:17 -0800181
182 // Title with no match.
183 std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
184 GetFPDFWideString(L"Chapter 3");
185 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
186
187 // Title with a match.
188 title = GetFPDFWideString(L"Chapter 2");
189 EXPECT_EQ(bookmarks[2].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
190 }
191 {
192 // Circular bookmarks in breadth.
193 auto bookmarks = CreateDictObjs(4);
194
dsinclair38fd8442016-09-15 10:15:32 -0700195 bookmarks[1].obj->SetFor("Title", new CPDF_String(L"Chapter 1"));
196 bookmarks[1].obj->SetFor(
Wei Li5227e572016-03-04 15:49:17 -0800197 "Parent", new CPDF_Reference(m_pIndirectObjs, bookmarks[0].num));
dsinclair38fd8442016-09-15 10:15:32 -0700198 bookmarks[1].obj->SetFor(
Wei Li5227e572016-03-04 15:49:17 -0800199 "Next", new CPDF_Reference(m_pIndirectObjs, bookmarks[2].num));
200
dsinclair38fd8442016-09-15 10:15:32 -0700201 bookmarks[2].obj->SetFor("Title", new CPDF_String(L"Chapter 2"));
202 bookmarks[2].obj->SetFor(
Wei Li5227e572016-03-04 15:49:17 -0800203 "Parent", new CPDF_Reference(m_pIndirectObjs, bookmarks[0].num));
dsinclair38fd8442016-09-15 10:15:32 -0700204 bookmarks[2].obj->SetFor(
Wei Li5227e572016-03-04 15:49:17 -0800205 "Next", new CPDF_Reference(m_pIndirectObjs, bookmarks[3].num));
206
dsinclair38fd8442016-09-15 10:15:32 -0700207 bookmarks[3].obj->SetFor("Title", new CPDF_String(L"Chapter 3"));
208 bookmarks[3].obj->SetFor(
Wei Li5227e572016-03-04 15:49:17 -0800209 "Parent", new CPDF_Reference(m_pIndirectObjs, bookmarks[0].num));
dsinclair38fd8442016-09-15 10:15:32 -0700210 bookmarks[3].obj->SetFor(
Wei Li5227e572016-03-04 15:49:17 -0800211 "Next", new CPDF_Reference(m_pIndirectObjs, bookmarks[1].num));
212
dsinclair38fd8442016-09-15 10:15:32 -0700213 bookmarks[0].obj->SetFor("Type", new CPDF_Name("Outlines"));
214 bookmarks[0].obj->SetFor("Count", new CPDF_Number(2));
215 bookmarks[0].obj->SetFor(
Wei Li5227e572016-03-04 15:49:17 -0800216 "First", new CPDF_Reference(m_pIndirectObjs, bookmarks[1].num));
dsinclair38fd8442016-09-15 10:15:32 -0700217 bookmarks[0].obj->SetFor(
Wei Li5227e572016-03-04 15:49:17 -0800218 "Last", new CPDF_Reference(m_pIndirectObjs, bookmarks[2].num));
219
dsinclair38fd8442016-09-15 10:15:32 -0700220 m_pRootObj->SetFor("Outlines",
221 new CPDF_Reference(m_pIndirectObjs, bookmarks[0].num));
Wei Li5227e572016-03-04 15:49:17 -0800222
223 // Title with no match.
224 std::unique_ptr<unsigned short, pdfium::FreeDeleter> title =
225 GetFPDFWideString(L"Chapter 8");
226 EXPECT_EQ(nullptr, FPDFBookmark_Find(m_pDoc.get(), title.get()));
227
228 // Title with a match.
229 title = GetFPDFWideString(L"Chapter 3");
230 EXPECT_EQ(bookmarks[3].obj, FPDFBookmark_Find(m_pDoc.get(), title.get()));
231 }
232}