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