1 // Copyright 2014 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 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7 #include "public/fpdf_doc.h"
8
9 #include <memory>
10 #include <set>
11
12 #include "core/fpdfapi/page/cpdf_page.h"
13 #include "core/fpdfapi/parser/cpdf_array.h"
14 #include "core/fpdfapi/parser/cpdf_document.h"
15 #include "core/fpdfdoc/cpdf_bookmark.h"
16 #include "core/fpdfdoc/cpdf_bookmarktree.h"
17 #include "core/fpdfdoc/cpdf_dest.h"
18 #include "core/fpdfdoc/cpdf_pagelabel.h"
19 #include "fpdfsdk/fsdk_define.h"
20 #include "third_party/base/ptr_util.h"
21 #include "third_party/base/stl_util.h"
22
23 namespace {
24
FindBookmark(const CPDF_BookmarkTree & tree,CPDF_Bookmark bookmark,const WideString & title,std::set<CPDF_Dictionary * > * visited)25 CPDF_Bookmark FindBookmark(const CPDF_BookmarkTree& tree,
26 CPDF_Bookmark bookmark,
27 const WideString& title,
28 std::set<CPDF_Dictionary*>* visited) {
29 // Return if already checked to avoid circular calling.
30 if (pdfium::ContainsKey(*visited, bookmark.GetDict()))
31 return CPDF_Bookmark();
32 visited->insert(bookmark.GetDict());
33
34 if (bookmark.GetDict() &&
35 bookmark.GetTitle().CompareNoCase(title.c_str()) == 0) {
36 // First check this item.
37 return bookmark;
38 }
39
40 // Go into children items.
41 CPDF_Bookmark child = tree.GetFirstChild(bookmark);
42 while (child.GetDict() && !pdfium::ContainsKey(*visited, child.GetDict())) {
43 // Check this item and its children.
44 CPDF_Bookmark found = FindBookmark(tree, child, title, visited);
45 if (found.GetDict())
46 return found;
47 child = tree.GetNextSibling(child);
48 }
49 return CPDF_Bookmark();
50 }
51
GetLinkList(CPDF_Page * page)52 CPDF_LinkList* GetLinkList(CPDF_Page* page) {
53 if (!page)
54 return nullptr;
55
56 CPDF_Document* pDoc = page->m_pDocument.Get();
57 std::unique_ptr<CPDF_LinkList>* pHolder = pDoc->LinksContext();
58 if (!pHolder->get())
59 *pHolder = pdfium::MakeUnique<CPDF_LinkList>();
60 return pHolder->get();
61 }
62
63 } // namespace
64
65 FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
FPDFBookmark_GetFirstChild(FPDF_DOCUMENT document,FPDF_BOOKMARK pDict)66 FPDFBookmark_GetFirstChild(FPDF_DOCUMENT document, FPDF_BOOKMARK pDict) {
67 CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
68 if (!pDoc)
69 return nullptr;
70 CPDF_BookmarkTree tree(pDoc);
71 CPDF_Bookmark bookmark =
72 CPDF_Bookmark(ToDictionary(static_cast<CPDF_Object*>(pDict)));
73 return tree.GetFirstChild(bookmark).GetDict();
74 }
75
76 FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
FPDFBookmark_GetNextSibling(FPDF_DOCUMENT document,FPDF_BOOKMARK pDict)77 FPDFBookmark_GetNextSibling(FPDF_DOCUMENT document, FPDF_BOOKMARK pDict) {
78 if (!pDict)
79 return nullptr;
80 CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
81 if (!pDoc)
82 return nullptr;
83 CPDF_BookmarkTree tree(pDoc);
84 CPDF_Bookmark bookmark =
85 CPDF_Bookmark(ToDictionary(static_cast<CPDF_Object*>(pDict)));
86 return tree.GetNextSibling(bookmark).GetDict();
87 }
88
89 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFBookmark_GetTitle(FPDF_BOOKMARK pDict,void * buffer,unsigned long buflen)90 FPDFBookmark_GetTitle(FPDF_BOOKMARK pDict, void* buffer, unsigned long buflen) {
91 if (!pDict)
92 return 0;
93 CPDF_Bookmark bookmark(ToDictionary(static_cast<CPDF_Object*>(pDict)));
94 WideString title = bookmark.GetTitle();
95 return Utf16EncodeMaybeCopyAndReturnLength(title, buffer, buflen);
96 }
97
98 FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
FPDFBookmark_Find(FPDF_DOCUMENT document,FPDF_WIDESTRING title)99 FPDFBookmark_Find(FPDF_DOCUMENT document, FPDF_WIDESTRING title) {
100 if (!title || title[0] == 0)
101 return nullptr;
102 CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
103 if (!pDoc)
104 return nullptr;
105 CPDF_BookmarkTree tree(pDoc);
106 size_t len = WideString::WStringLength(title);
107 WideString encodedTitle = WideString::FromUTF16LE(title, len);
108 std::set<CPDF_Dictionary*> visited;
109 return FindBookmark(tree, CPDF_Bookmark(), encodedTitle, &visited).GetDict();
110 }
111
FPDFBookmark_GetDest(FPDF_DOCUMENT document,FPDF_BOOKMARK pDict)112 FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDFBookmark_GetDest(FPDF_DOCUMENT document,
113 FPDF_BOOKMARK pDict) {
114 if (!pDict)
115 return nullptr;
116 CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
117 if (!pDoc)
118 return nullptr;
119 CPDF_Bookmark bookmark(ToDictionary(static_cast<CPDF_Object*>(pDict)));
120 CPDF_Dest dest = bookmark.GetDest(pDoc);
121 if (dest.GetObject())
122 return dest.GetObject();
123 // If this bookmark is not directly associated with a dest, we try to get
124 // action
125 CPDF_Action action = bookmark.GetAction();
126 if (!action.GetDict())
127 return nullptr;
128 return action.GetDest(pDoc).GetObject();
129 }
130
131 FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV
FPDFBookmark_GetAction(FPDF_BOOKMARK pDict)132 FPDFBookmark_GetAction(FPDF_BOOKMARK pDict) {
133 if (!pDict)
134 return nullptr;
135 CPDF_Bookmark bookmark(ToDictionary(static_cast<CPDF_Object*>(pDict)));
136 return bookmark.GetAction().GetDict();
137 }
138
FPDFAction_GetType(FPDF_ACTION pDict)139 FPDF_EXPORT unsigned long FPDF_CALLCONV FPDFAction_GetType(FPDF_ACTION pDict) {
140 if (!pDict)
141 return PDFACTION_UNSUPPORTED;
142
143 CPDF_Action action(ToDictionary(static_cast<CPDF_Object*>(pDict)));
144 CPDF_Action::ActionType type = action.GetType();
145 switch (type) {
146 case CPDF_Action::GoTo:
147 return PDFACTION_GOTO;
148 case CPDF_Action::GoToR:
149 return PDFACTION_REMOTEGOTO;
150 case CPDF_Action::URI:
151 return PDFACTION_URI;
152 case CPDF_Action::Launch:
153 return PDFACTION_LAUNCH;
154 default:
155 return PDFACTION_UNSUPPORTED;
156 }
157 }
158
FPDFAction_GetDest(FPDF_DOCUMENT document,FPDF_ACTION pDict)159 FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDFAction_GetDest(FPDF_DOCUMENT document,
160 FPDF_ACTION pDict) {
161 if (!pDict)
162 return nullptr;
163 CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
164 if (!pDoc)
165 return nullptr;
166 CPDF_Action action(ToDictionary(static_cast<CPDF_Object*>(pDict)));
167 return action.GetDest(pDoc).GetObject();
168 }
169
170 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAction_GetFilePath(FPDF_ACTION pDict,void * buffer,unsigned long buflen)171 FPDFAction_GetFilePath(FPDF_ACTION pDict, void* buffer, unsigned long buflen) {
172 unsigned long type = FPDFAction_GetType(pDict);
173 if (type != PDFACTION_REMOTEGOTO && type != PDFACTION_LAUNCH)
174 return 0;
175
176 CPDF_Action action(ToDictionary(static_cast<CPDF_Object*>(pDict)));
177 ByteString path = action.GetFilePath().UTF8Encode();
178 unsigned long len = path.GetLength() + 1;
179 if (buffer && len <= buflen)
180 memcpy(buffer, path.c_str(), len);
181 return len;
182 }
183
184 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAction_GetURIPath(FPDF_DOCUMENT document,FPDF_ACTION pDict,void * buffer,unsigned long buflen)185 FPDFAction_GetURIPath(FPDF_DOCUMENT document,
186 FPDF_ACTION pDict,
187 void* buffer,
188 unsigned long buflen) {
189 if (!pDict)
190 return 0;
191 CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
192 if (!pDoc)
193 return 0;
194 CPDF_Action action(ToDictionary(static_cast<CPDF_Object*>(pDict)));
195 ByteString path = action.GetURI(pDoc);
196 unsigned long len = path.GetLength() + 1;
197 if (buffer && len <= buflen)
198 memcpy(buffer, path.c_str(), len);
199 return len;
200 }
201
202 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFDest_GetPageIndex(FPDF_DOCUMENT document,FPDF_DEST pDict)203 FPDFDest_GetPageIndex(FPDF_DOCUMENT document, FPDF_DEST pDict) {
204 if (!pDict)
205 return 0;
206 CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
207 if (!pDoc)
208 return 0;
209 CPDF_Dest dest(static_cast<CPDF_Array*>(pDict));
210 return dest.GetPageIndex(pDoc);
211 }
212
213 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFDest_GetView(FPDF_DEST pDict,unsigned long * pNumParams,FS_FLOAT * pParams)214 FPDFDest_GetView(FPDF_DEST pDict,
215 unsigned long* pNumParams,
216 FS_FLOAT* pParams) {
217 if (!pDict) {
218 *pNumParams = 0;
219 return 0;
220 }
221
222 CPDF_Dest dest(static_cast<CPDF_Array*>(pDict));
223 unsigned long nParams = dest.GetNumParams();
224 ASSERT(nParams <= 4);
225 *pNumParams = nParams;
226 for (unsigned long i = 0; i < nParams; ++i)
227 pParams[i] = dest.GetParam(i);
228 return dest.GetZoomMode();
229 }
230
231 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFDest_GetLocationInPage(FPDF_DEST pDict,FPDF_BOOL * hasXVal,FPDF_BOOL * hasYVal,FPDF_BOOL * hasZoomVal,FS_FLOAT * x,FS_FLOAT * y,FS_FLOAT * zoom)232 FPDFDest_GetLocationInPage(FPDF_DEST pDict,
233 FPDF_BOOL* hasXVal,
234 FPDF_BOOL* hasYVal,
235 FPDF_BOOL* hasZoomVal,
236 FS_FLOAT* x,
237 FS_FLOAT* y,
238 FS_FLOAT* zoom) {
239 if (!pDict)
240 return false;
241
242 auto dest = pdfium::MakeUnique<CPDF_Dest>(static_cast<CPDF_Object*>(pDict));
243
244 // FPDF_BOOL is an int, GetXYZ expects bools.
245 bool bHasX;
246 bool bHasY;
247 bool bHasZoom;
248 if (!dest->GetXYZ(&bHasX, &bHasY, &bHasZoom, x, y, zoom))
249 return false;
250
251 *hasXVal = bHasX;
252 *hasYVal = bHasY;
253 *hasZoomVal = bHasZoom;
254 return true;
255 }
256
FPDFLink_GetLinkAtPoint(FPDF_PAGE page,double x,double y)257 FPDF_EXPORT FPDF_LINK FPDF_CALLCONV FPDFLink_GetLinkAtPoint(FPDF_PAGE page,
258 double x,
259 double y) {
260 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
261 if (!pPage)
262 return nullptr;
263
264 CPDF_LinkList* pLinkList = GetLinkList(pPage);
265 if (!pLinkList)
266 return nullptr;
267
268 return pLinkList
269 ->GetLinkAtPoint(pPage,
270 CFX_PointF(static_cast<float>(x), static_cast<float>(y)),
271 nullptr)
272 .GetDict();
273 }
274
FPDFLink_GetLinkZOrderAtPoint(FPDF_PAGE page,double x,double y)275 FPDF_EXPORT int FPDF_CALLCONV FPDFLink_GetLinkZOrderAtPoint(FPDF_PAGE page,
276 double x,
277 double y) {
278 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
279 if (!pPage)
280 return -1;
281
282 CPDF_LinkList* pLinkList = GetLinkList(pPage);
283 if (!pLinkList)
284 return -1;
285
286 int z_order = -1;
287 pLinkList->GetLinkAtPoint(
288 pPage, CFX_PointF(static_cast<float>(x), static_cast<float>(y)),
289 &z_order);
290 return z_order;
291 }
292
FPDFLink_GetDest(FPDF_DOCUMENT document,FPDF_LINK pDict)293 FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDFLink_GetDest(FPDF_DOCUMENT document,
294 FPDF_LINK pDict) {
295 if (!pDict)
296 return nullptr;
297 CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
298 if (!pDoc)
299 return nullptr;
300 CPDF_Link link(ToDictionary(static_cast<CPDF_Object*>(pDict)));
301 FPDF_DEST dest = link.GetDest(pDoc).GetObject();
302 if (dest)
303 return dest;
304 // If this link is not directly associated with a dest, we try to get action
305 CPDF_Action action = link.GetAction();
306 if (!action.GetDict())
307 return nullptr;
308 return action.GetDest(pDoc).GetObject();
309 }
310
FPDFLink_GetAction(FPDF_LINK pDict)311 FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV FPDFLink_GetAction(FPDF_LINK pDict) {
312 if (!pDict)
313 return nullptr;
314
315 CPDF_Link link(ToDictionary(static_cast<CPDF_Object*>(pDict)));
316 return link.GetAction().GetDict();
317 }
318
FPDFLink_Enumerate(FPDF_PAGE page,int * startPos,FPDF_LINK * linkAnnot)319 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_Enumerate(FPDF_PAGE page,
320 int* startPos,
321 FPDF_LINK* linkAnnot) {
322 if (!startPos || !linkAnnot)
323 return false;
324 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
325 if (!pPage || !pPage->m_pFormDict)
326 return false;
327 CPDF_Array* pAnnots = pPage->m_pFormDict->GetArrayFor("Annots");
328 if (!pAnnots)
329 return false;
330 for (size_t i = *startPos; i < pAnnots->GetCount(); i++) {
331 CPDF_Dictionary* pDict =
332 ToDictionary(static_cast<CPDF_Object*>(pAnnots->GetDirectObjectAt(i)));
333 if (!pDict)
334 continue;
335 if (pDict->GetStringFor("Subtype") == "Link") {
336 *startPos = static_cast<int>(i + 1);
337 *linkAnnot = static_cast<FPDF_LINK>(pDict);
338 return true;
339 }
340 }
341 return false;
342 }
343
FPDFLink_GetAnnotRect(FPDF_LINK linkAnnot,FS_RECTF * rect)344 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_GetAnnotRect(FPDF_LINK linkAnnot,
345 FS_RECTF* rect) {
346 if (!linkAnnot || !rect)
347 return false;
348 CPDF_Dictionary* pAnnotDict =
349 ToDictionary(static_cast<CPDF_Object*>(linkAnnot));
350 FSRECTFFromCFXFloatRect(pAnnotDict->GetRectFor("Rect"), rect);
351 return true;
352 }
353
FPDFLink_CountQuadPoints(FPDF_LINK linkAnnot)354 FPDF_EXPORT int FPDF_CALLCONV FPDFLink_CountQuadPoints(FPDF_LINK linkAnnot) {
355 if (!linkAnnot)
356 return 0;
357 CPDF_Dictionary* pAnnotDict =
358 ToDictionary(static_cast<CPDF_Object*>(linkAnnot));
359 CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
360 if (!pArray)
361 return 0;
362 return static_cast<int>(pArray->GetCount() / 8);
363 }
364
365 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFLink_GetQuadPoints(FPDF_LINK linkAnnot,int quadIndex,FS_QUADPOINTSF * quadPoints)366 FPDFLink_GetQuadPoints(FPDF_LINK linkAnnot,
367 int quadIndex,
368 FS_QUADPOINTSF* quadPoints) {
369 if (!linkAnnot || !quadPoints)
370 return false;
371 CPDF_Dictionary* pAnnotDict =
372 ToDictionary(static_cast<CPDF_Object*>(linkAnnot));
373 CPDF_Array* pArray = pAnnotDict->GetArrayFor("QuadPoints");
374 if (!pArray)
375 return false;
376
377 if (quadIndex < 0 ||
378 static_cast<size_t>(quadIndex) >= pArray->GetCount() / 8 ||
379 (static_cast<size_t>(quadIndex * 8 + 7) >= pArray->GetCount())) {
380 return false;
381 }
382
383 quadPoints->x1 = pArray->GetNumberAt(quadIndex * 8);
384 quadPoints->y1 = pArray->GetNumberAt(quadIndex * 8 + 1);
385 quadPoints->x2 = pArray->GetNumberAt(quadIndex * 8 + 2);
386 quadPoints->y2 = pArray->GetNumberAt(quadIndex * 8 + 3);
387 quadPoints->x3 = pArray->GetNumberAt(quadIndex * 8 + 4);
388 quadPoints->y3 = pArray->GetNumberAt(quadIndex * 8 + 5);
389 quadPoints->x4 = pArray->GetNumberAt(quadIndex * 8 + 6);
390 quadPoints->y4 = pArray->GetNumberAt(quadIndex * 8 + 7);
391 return true;
392 }
393
FPDF_GetMetaText(FPDF_DOCUMENT document,FPDF_BYTESTRING tag,void * buffer,unsigned long buflen)394 FPDF_EXPORT unsigned long FPDF_CALLCONV FPDF_GetMetaText(FPDF_DOCUMENT document,
395 FPDF_BYTESTRING tag,
396 void* buffer,
397 unsigned long buflen) {
398 if (!tag)
399 return 0;
400 CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
401 if (!pDoc)
402 return 0;
403 pDoc->LoadDocumentInfo();
404 const CPDF_Dictionary* pInfo = pDoc->GetInfo();
405 if (!pInfo)
406 return 0;
407 WideString text = pInfo->GetUnicodeTextFor(tag);
408 return Utf16EncodeMaybeCopyAndReturnLength(text, buffer, buflen);
409 }
410
411 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_GetPageLabel(FPDF_DOCUMENT document,int page_index,void * buffer,unsigned long buflen)412 FPDF_GetPageLabel(FPDF_DOCUMENT document,
413 int page_index,
414 void* buffer,
415 unsigned long buflen) {
416 if (page_index < 0)
417 return 0;
418
419 // CPDF_PageLabel can deal with NULL |document|.
420 CPDF_PageLabel label(CPDFDocumentFromFPDFDocument(document));
421 Optional<WideString> str = label.GetLabel(page_index);
422 return str.has_value()
423 ? Utf16EncodeMaybeCopyAndReturnLength(str.value(), buffer, buflen)
424 : 0;
425 }
426