xref: /aosp_15_r20/external/pdfium/fpdfsdk/fpdf_doc_unittest.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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 "public/fpdf_doc.h"
6 
7 #include <memory>
8 #include <vector>
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_dictionary.h"
13 #include "core/fpdfapi/parser/cpdf_document.h"
14 #include "core/fpdfapi/parser/cpdf_name.h"
15 #include "core/fpdfapi/parser/cpdf_null.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 "core/fpdfdoc/cpdf_dest.h"
22 #include "fpdfsdk/cpdfsdk_helpers.h"
23 #include "public/cpp/fpdf_scopers.h"
24 #include "testing/fx_string_testhelpers.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 
27 class PDFDocTest : public TestWithPageModule {
28  public:
29   struct DictObjInfo {
30     uint32_t num;
31     RetainPtr<CPDF_Dictionary> obj;
32   };
33 
SetUp()34   void SetUp() override {
35     TestWithPageModule::SetUp();
36     auto pTestDoc = std::make_unique<CPDF_TestDocument>();
37     m_pIndirectObjs = pTestDoc.get();
38     m_pRootObj = m_pIndirectObjs->NewIndirect<CPDF_Dictionary>();
39     pTestDoc->SetRoot(m_pRootObj);
40     m_pDoc.reset(FPDFDocumentFromCPDFDocument(pTestDoc.release()));
41   }
42 
TearDown()43   void TearDown() override {
44     m_pRootObj = nullptr;
45     m_pIndirectObjs = nullptr;
46     m_pDoc.reset();
47     TestWithPageModule::TearDown();
48   }
49 
CreateDictObjs(int num)50   std::vector<DictObjInfo> CreateDictObjs(int num) {
51     std::vector<DictObjInfo> info;
52     for (int i = 0; i < num; ++i) {
53       auto obj = m_pIndirectObjs->NewIndirect<CPDF_Dictionary>();
54       info.push_back({obj->GetObjNum(), obj});
55     }
56     return info;
57   }
58 
59  protected:
60   ScopedFPDFDocument m_pDoc;
61   UnownedPtr<CPDF_IndirectObjectHolder> m_pIndirectObjs;
62   RetainPtr<CPDF_Dictionary> m_pRootObj;
63 };
64 
TEST_F(PDFDocTest,FindBookmark)65 TEST_F(PDFDocTest, FindBookmark) {
66   {
67     // No bookmark information.
68     ScopedFPDFWideString title = GetFPDFWideString(L"");
69     EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get()));
70 
71     title = GetFPDFWideString(L"Preface");
72     EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get()));
73   }
74   {
75     // Empty bookmark tree.
76     m_pRootObj->SetNewFor<CPDF_Dictionary>("Outlines");
77     ScopedFPDFWideString title = GetFPDFWideString(L"");
78     EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get()));
79 
80     title = GetFPDFWideString(L"Preface");
81     EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get()));
82   }
83   {
84     // Check on a regular bookmark tree.
85     auto bookmarks = CreateDictObjs(3);
86 
87     bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
88     bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
89                                                 bookmarks[0].num);
90     bookmarks[1].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs,
91                                                 bookmarks[2].num);
92 
93     bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
94     bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
95                                                 bookmarks[0].num);
96     bookmarks[2].obj->SetNewFor<CPDF_Reference>("Prev", m_pIndirectObjs,
97                                                 bookmarks[1].num);
98 
99     bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
100     bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
101     bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs,
102                                                 bookmarks[1].num);
103     bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs,
104                                                 bookmarks[2].num);
105 
106     m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs,
107                                           bookmarks[0].num);
108 
109     // Title with no match.
110     ScopedFPDFWideString title = GetFPDFWideString(L"Chapter 3");
111     EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get()));
112 
113     // Title with partial match only.
114     title = GetFPDFWideString(L"Chapter");
115     EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get()));
116 
117     // Title with a match.
118     title = GetFPDFWideString(L"Chapter 2");
119     EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[2].obj.Get()),
120               FPDFBookmark_Find(m_pDoc.get(), title.get()));
121 
122     // Title match is case insensitive.
123     title = GetFPDFWideString(L"cHaPter 2");
124     EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[2].obj.Get()),
125               FPDFBookmark_Find(m_pDoc.get(), title.get()));
126   }
127   {
128     // Circular bookmarks in depth.
129     auto bookmarks = CreateDictObjs(3);
130 
131     bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
132     bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
133                                                 bookmarks[0].num);
134     bookmarks[1].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs,
135                                                 bookmarks[2].num);
136 
137     bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
138     bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
139                                                 bookmarks[1].num);
140     bookmarks[2].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs,
141                                                 bookmarks[1].num);
142 
143     bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
144     bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
145     bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs,
146                                                 bookmarks[1].num);
147     bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs,
148                                                 bookmarks[2].num);
149 
150     m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs,
151                                           bookmarks[0].num);
152 
153     // Title with no match.
154     ScopedFPDFWideString title = GetFPDFWideString(L"Chapter 3");
155     EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get()));
156 
157     // Title with a match.
158     title = GetFPDFWideString(L"Chapter 2");
159     EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[2].obj.Get()),
160               FPDFBookmark_Find(m_pDoc.get(), title.get()));
161   }
162   {
163     // Circular bookmarks in breadth.
164     auto bookmarks = CreateDictObjs(4);
165 
166     bookmarks[1].obj->SetNewFor<CPDF_String>("Title", L"Chapter 1");
167     bookmarks[1].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
168                                                 bookmarks[0].num);
169     bookmarks[1].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs,
170                                                 bookmarks[2].num);
171 
172     bookmarks[2].obj->SetNewFor<CPDF_String>("Title", L"Chapter 2");
173     bookmarks[2].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
174                                                 bookmarks[0].num);
175     bookmarks[2].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs,
176                                                 bookmarks[3].num);
177 
178     bookmarks[3].obj->SetNewFor<CPDF_String>("Title", L"Chapter 3");
179     bookmarks[3].obj->SetNewFor<CPDF_Reference>("Parent", m_pIndirectObjs,
180                                                 bookmarks[0].num);
181     bookmarks[3].obj->SetNewFor<CPDF_Reference>("Next", m_pIndirectObjs,
182                                                 bookmarks[1].num);
183 
184     bookmarks[0].obj->SetNewFor<CPDF_Name>("Type", "Outlines");
185     bookmarks[0].obj->SetNewFor<CPDF_Number>("Count", 2);
186     bookmarks[0].obj->SetNewFor<CPDF_Reference>("First", m_pIndirectObjs,
187                                                 bookmarks[1].num);
188     bookmarks[0].obj->SetNewFor<CPDF_Reference>("Last", m_pIndirectObjs,
189                                                 bookmarks[2].num);
190 
191     m_pRootObj->SetNewFor<CPDF_Reference>("Outlines", m_pIndirectObjs,
192                                           bookmarks[0].num);
193 
194     // Title with no match.
195     ScopedFPDFWideString title = GetFPDFWideString(L"Chapter 8");
196     EXPECT_FALSE(FPDFBookmark_Find(m_pDoc.get(), title.get()));
197 
198     // Title with a match.
199     title = GetFPDFWideString(L"Chapter 3");
200     EXPECT_EQ(FPDFBookmarkFromCPDFDictionary(bookmarks[3].obj.Get()),
201               FPDFBookmark_Find(m_pDoc.get(), title.get()));
202   }
203 }
204 
TEST_F(PDFDocTest,GetLocationInPage)205 TEST_F(PDFDocTest, GetLocationInPage) {
206   auto array = pdfium::MakeRetain<CPDF_Array>();
207   array->AppendNew<CPDF_Number>(0);  // Page Index.
208   array->AppendNew<CPDF_Name>("XYZ");
209   array->AppendNew<CPDF_Number>(4);  // X
210   array->AppendNew<CPDF_Number>(5);  // Y
211   array->AppendNew<CPDF_Number>(6);  // Zoom.
212 
213   FPDF_BOOL hasX;
214   FPDF_BOOL hasY;
215   FPDF_BOOL hasZoom;
216   FS_FLOAT x;
217   FS_FLOAT y;
218   FS_FLOAT zoom;
219 
220   EXPECT_TRUE(FPDFDest_GetLocationInPage(FPDFDestFromCPDFArray(array.Get()),
221                                          &hasX, &hasY, &hasZoom, &x, &y,
222                                          &zoom));
223   EXPECT_TRUE(hasX);
224   EXPECT_TRUE(hasY);
225   EXPECT_TRUE(hasZoom);
226   EXPECT_EQ(4, x);
227   EXPECT_EQ(5, y);
228   EXPECT_EQ(6, zoom);
229 
230   array->SetNewAt<CPDF_Null>(2);
231   array->SetNewAt<CPDF_Null>(3);
232   array->SetNewAt<CPDF_Null>(4);
233   EXPECT_TRUE(FPDFDest_GetLocationInPage(FPDFDestFromCPDFArray(array.Get()),
234                                          &hasX, &hasY, &hasZoom, &x, &y,
235                                          &zoom));
236   EXPECT_FALSE(hasX);
237   EXPECT_FALSE(hasY);
238   EXPECT_FALSE(hasZoom);
239 
240   array = pdfium::MakeRetain<CPDF_Array>();
241   EXPECT_FALSE(FPDFDest_GetLocationInPage(FPDFDestFromCPDFArray(array.Get()),
242                                           &hasX, &hasY, &hasZoom, &x, &y,
243                                           &zoom));
244 }
245