• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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