• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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 // 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 "constants/form_fields.h"
14 #include "core/fpdfapi/page/cpdf_annotcontext.h"
15 #include "core/fpdfapi/page/cpdf_page.h"
16 #include "core/fpdfapi/parser/cpdf_array.h"
17 #include "core/fpdfapi/parser/cpdf_dictionary.h"
18 #include "core/fpdfapi/parser/cpdf_document.h"
19 #include "core/fpdfapi/parser/cpdf_number.h"
20 #include "core/fpdfapi/parser/cpdf_string.h"
21 #include "core/fpdfapi/parser/fpdf_parser_decode.h"
22 #include "core/fpdfdoc/cpdf_aaction.h"
23 #include "core/fpdfdoc/cpdf_bookmark.h"
24 #include "core/fpdfdoc/cpdf_bookmarktree.h"
25 #include "core/fpdfdoc/cpdf_dest.h"
26 #include "core/fpdfdoc/cpdf_linklist.h"
27 #include "core/fpdfdoc/cpdf_pagelabel.h"
28 #include "fpdfsdk/cpdfsdk_helpers.h"
29 #include "public/fpdf_formfill.h"
30 #include "third_party/base/check.h"
31 #include "third_party/base/containers/contains.h"
32 #include "third_party/base/numerics/safe_conversions.h"
33 
34 namespace {
35 
FindBookmark(const CPDF_BookmarkTree & tree,CPDF_Bookmark bookmark,const WideString & title,std::set<const CPDF_Dictionary * > * visited)36 CPDF_Bookmark FindBookmark(const CPDF_BookmarkTree& tree,
37                            CPDF_Bookmark bookmark,
38                            const WideString& title,
39                            std::set<const CPDF_Dictionary*>* visited) {
40   // Return if already checked to avoid circular calling.
41   if (pdfium::Contains(*visited, bookmark.GetDict()))
42     return CPDF_Bookmark();
43   visited->insert(bookmark.GetDict());
44 
45   if (bookmark.GetDict() &&
46       bookmark.GetTitle().CompareNoCase(title.c_str()) == 0) {
47     // First check this item.
48     return bookmark;
49   }
50 
51   // Go into children items.
52   CPDF_Bookmark child = tree.GetFirstChild(bookmark);
53   while (child.GetDict() && !pdfium::Contains(*visited, child.GetDict())) {
54     // Check this item and its children.
55     CPDF_Bookmark found = FindBookmark(tree, child, title, visited);
56     if (found.GetDict())
57       return found;
58     child = tree.GetNextSibling(child);
59   }
60   return CPDF_Bookmark();
61 }
62 
GetLinkList(CPDF_Page * page)63 CPDF_LinkList* GetLinkList(CPDF_Page* page) {
64   CPDF_Document* pDoc = page->GetDocument();
65   auto* pList = static_cast<CPDF_LinkList*>(pDoc->GetLinksContext());
66   if (pList)
67     return pList;
68 
69   auto pNewList = std::make_unique<CPDF_LinkList>();
70   pList = pNewList.get();
71   pDoc->SetLinksContext(std::move(pNewList));
72   return pList;
73 }
74 
75 }  // namespace
76 
77 FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
FPDFBookmark_GetFirstChild(FPDF_DOCUMENT document,FPDF_BOOKMARK bookmark)78 FPDFBookmark_GetFirstChild(FPDF_DOCUMENT document, FPDF_BOOKMARK bookmark) {
79   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
80   if (!pDoc)
81     return nullptr;
82   CPDF_BookmarkTree tree(pDoc);
83   CPDF_Bookmark cBookmark(
84       pdfium::WrapRetain(CPDFDictionaryFromFPDFBookmark(bookmark)));
85   return FPDFBookmarkFromCPDFDictionary(
86       tree.GetFirstChild(cBookmark).GetDict());
87 }
88 
89 FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
FPDFBookmark_GetNextSibling(FPDF_DOCUMENT document,FPDF_BOOKMARK bookmark)90 FPDFBookmark_GetNextSibling(FPDF_DOCUMENT document, FPDF_BOOKMARK bookmark) {
91   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
92   if (!pDoc)
93     return nullptr;
94 
95   if (!bookmark)
96     return nullptr;
97 
98   CPDF_BookmarkTree tree(pDoc);
99   CPDF_Bookmark cBookmark(
100       pdfium::WrapRetain(CPDFDictionaryFromFPDFBookmark(bookmark)));
101   return FPDFBookmarkFromCPDFDictionary(
102       tree.GetNextSibling(cBookmark).GetDict());
103 }
104 
105 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFBookmark_GetTitle(FPDF_BOOKMARK bookmark,void * buffer,unsigned long buflen)106 FPDFBookmark_GetTitle(FPDF_BOOKMARK bookmark,
107                       void* buffer,
108                       unsigned long buflen) {
109   if (!bookmark)
110     return 0;
111   CPDF_Bookmark cBookmark(
112       pdfium::WrapRetain(CPDFDictionaryFromFPDFBookmark(bookmark)));
113   WideString title = cBookmark.GetTitle();
114   return Utf16EncodeMaybeCopyAndReturnLength(title, buffer, buflen);
115 }
116 
FPDFBookmark_GetCount(FPDF_BOOKMARK bookmark)117 FPDF_EXPORT int FPDF_CALLCONV FPDFBookmark_GetCount(FPDF_BOOKMARK bookmark) {
118   if (!bookmark)
119     return 0;
120   CPDF_Bookmark cBookmark(
121       pdfium::WrapRetain(CPDFDictionaryFromFPDFBookmark(bookmark)));
122   return cBookmark.GetCount();
123 }
124 
125 FPDF_EXPORT FPDF_BOOKMARK FPDF_CALLCONV
FPDFBookmark_Find(FPDF_DOCUMENT document,FPDF_WIDESTRING title)126 FPDFBookmark_Find(FPDF_DOCUMENT document, FPDF_WIDESTRING title) {
127   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
128   if (!pDoc)
129     return nullptr;
130 
131   WideString encodedTitle = WideStringFromFPDFWideString(title);
132   if (encodedTitle.IsEmpty())
133     return nullptr;
134 
135   CPDF_BookmarkTree tree(pDoc);
136   std::set<const CPDF_Dictionary*> visited;
137   return FPDFBookmarkFromCPDFDictionary(
138       FindBookmark(tree, CPDF_Bookmark(), encodedTitle, &visited).GetDict());
139 }
140 
141 FPDF_EXPORT FPDF_DEST FPDF_CALLCONV
FPDFBookmark_GetDest(FPDF_DOCUMENT document,FPDF_BOOKMARK bookmark)142 FPDFBookmark_GetDest(FPDF_DOCUMENT document, FPDF_BOOKMARK bookmark) {
143   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
144   if (!pDoc)
145     return nullptr;
146 
147   if (!bookmark)
148     return nullptr;
149 
150   CPDF_Bookmark cBookmark(
151       pdfium::WrapRetain(CPDFDictionaryFromFPDFBookmark(bookmark)));
152   CPDF_Dest dest = cBookmark.GetDest(pDoc);
153   if (dest.GetArray())
154     return FPDFDestFromCPDFArray(dest.GetArray());
155   // If this bookmark is not directly associated with a dest, we try to get
156   // action
157   CPDF_Action action = cBookmark.GetAction();
158   if (!action.HasDict())
159     return nullptr;
160   return FPDFDestFromCPDFArray(action.GetDest(pDoc).GetArray());
161 }
162 
163 FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV
FPDFBookmark_GetAction(FPDF_BOOKMARK bookmark)164 FPDFBookmark_GetAction(FPDF_BOOKMARK bookmark) {
165   if (!bookmark)
166     return nullptr;
167 
168   CPDF_Bookmark cBookmark(
169       pdfium::WrapRetain(CPDFDictionaryFromFPDFBookmark(bookmark)));
170   return FPDFActionFromCPDFDictionary(cBookmark.GetAction().GetDict());
171 }
172 
FPDFAction_GetType(FPDF_ACTION action)173 FPDF_EXPORT unsigned long FPDF_CALLCONV FPDFAction_GetType(FPDF_ACTION action) {
174   if (!action)
175     return PDFACTION_UNSUPPORTED;
176 
177   CPDF_Action cAction(pdfium::WrapRetain(CPDFDictionaryFromFPDFAction(action)));
178   switch (cAction.GetType()) {
179     case CPDF_Action::Type::kGoTo:
180       return PDFACTION_GOTO;
181     case CPDF_Action::Type::kGoToR:
182       return PDFACTION_REMOTEGOTO;
183     case CPDF_Action::Type::kGoToE:
184       return PDFACTION_EMBEDDEDGOTO;
185     case CPDF_Action::Type::kURI:
186       return PDFACTION_URI;
187     case CPDF_Action::Type::kLaunch:
188       return PDFACTION_LAUNCH;
189     default:
190       return PDFACTION_UNSUPPORTED;
191   }
192 }
193 
FPDFAction_GetDest(FPDF_DOCUMENT document,FPDF_ACTION action)194 FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDFAction_GetDest(FPDF_DOCUMENT document,
195                                                        FPDF_ACTION action) {
196   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
197   if (!pDoc)
198     return nullptr;
199 
200   unsigned long type = FPDFAction_GetType(action);
201   if (type != PDFACTION_GOTO && type != PDFACTION_REMOTEGOTO &&
202       type != PDFACTION_EMBEDDEDGOTO) {
203     return nullptr;
204   }
205   CPDF_Action cAction(pdfium::WrapRetain(CPDFDictionaryFromFPDFAction(action)));
206   return FPDFDestFromCPDFArray(cAction.GetDest(pDoc).GetArray());
207 }
208 
209 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAction_GetFilePath(FPDF_ACTION action,void * buffer,unsigned long buflen)210 FPDFAction_GetFilePath(FPDF_ACTION action, void* buffer, unsigned long buflen) {
211   unsigned long type = FPDFAction_GetType(action);
212   if (type != PDFACTION_REMOTEGOTO && type != PDFACTION_EMBEDDEDGOTO &&
213       type != PDFACTION_LAUNCH) {
214     return 0;
215   }
216 
217   CPDF_Action cAction(pdfium::WrapRetain(CPDFDictionaryFromFPDFAction(action)));
218   ByteString path = cAction.GetFilePath().ToUTF8();
219   return NulTerminateMaybeCopyAndReturnLength(path, buffer, buflen);
220 }
221 
222 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFAction_GetURIPath(FPDF_DOCUMENT document,FPDF_ACTION action,void * buffer,unsigned long buflen)223 FPDFAction_GetURIPath(FPDF_DOCUMENT document,
224                       FPDF_ACTION action,
225                       void* buffer,
226                       unsigned long buflen) {
227   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
228   if (!pDoc)
229     return 0;
230 
231   unsigned long type = FPDFAction_GetType(action);
232   if (type != PDFACTION_URI)
233     return 0;
234 
235   CPDF_Action cAction(pdfium::WrapRetain(CPDFDictionaryFromFPDFAction(action)));
236   ByteString path = cAction.GetURI(pDoc);
237 
238   const unsigned long len =
239       pdfium::base::checked_cast<unsigned long>(path.GetLength() + 1);
240   if (buffer && len <= buflen)
241     memcpy(buffer, path.c_str(), len);
242   return len;
243 }
244 
FPDFDest_GetDestPageIndex(FPDF_DOCUMENT document,FPDF_DEST dest)245 FPDF_EXPORT int FPDF_CALLCONV FPDFDest_GetDestPageIndex(FPDF_DOCUMENT document,
246                                                         FPDF_DEST dest) {
247   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
248   if (!pDoc)
249     return -1;
250 
251   if (!dest)
252     return -1;
253 
254   CPDF_Dest destination(pdfium::WrapRetain(CPDFArrayFromFPDFDest(dest)));
255   return destination.GetDestPageIndex(pDoc);
256 }
257 
258 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDFDest_GetView(FPDF_DEST dest,unsigned long * pNumParams,FS_FLOAT * pParams)259 FPDFDest_GetView(FPDF_DEST dest, unsigned long* pNumParams, FS_FLOAT* pParams) {
260   if (!dest) {
261     *pNumParams = 0;
262     return 0;
263   }
264 
265   CPDF_Dest destination(pdfium::WrapRetain(CPDFArrayFromFPDFDest(dest)));
266   const unsigned long nParams =
267       pdfium::base::checked_cast<unsigned long>(destination.GetNumParams());
268   DCHECK(nParams <= 4);
269   *pNumParams = nParams;
270   for (unsigned long i = 0; i < nParams; ++i)
271     pParams[i] = destination.GetParam(i);
272   return destination.GetZoomMode();
273 }
274 
275 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)276 FPDFDest_GetLocationInPage(FPDF_DEST dest,
277                            FPDF_BOOL* hasXVal,
278                            FPDF_BOOL* hasYVal,
279                            FPDF_BOOL* hasZoomVal,
280                            FS_FLOAT* x,
281                            FS_FLOAT* y,
282                            FS_FLOAT* zoom) {
283   if (!dest)
284     return false;
285 
286   CPDF_Dest destination(pdfium::WrapRetain(CPDFArrayFromFPDFDest(dest)));
287 
288   // FPDF_BOOL is an int, GetXYZ expects bools.
289   bool bHasX;
290   bool bHasY;
291   bool bHasZoom;
292   if (!destination.GetXYZ(&bHasX, &bHasY, &bHasZoom, x, y, zoom))
293     return false;
294 
295   *hasXVal = bHasX;
296   *hasYVal = bHasY;
297   *hasZoomVal = bHasZoom;
298   return true;
299 }
300 
FPDFLink_GetLinkAtPoint(FPDF_PAGE page,double x,double y)301 FPDF_EXPORT FPDF_LINK FPDF_CALLCONV FPDFLink_GetLinkAtPoint(FPDF_PAGE page,
302                                                             double x,
303                                                             double y) {
304   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
305   if (!pPage)
306     return nullptr;
307 
308   CPDF_LinkList* pLinkList = GetLinkList(pPage);
309   if (!pLinkList)
310     return nullptr;
311 
312   CPDF_Link link = pLinkList->GetLinkAtPoint(
313       pPage, CFX_PointF(static_cast<float>(x), static_cast<float>(y)), nullptr);
314 
315   // Unretained reference in public API. NOLINTNEXTLINE
316   return FPDFLinkFromCPDFDictionary(link.GetMutableDict());
317 }
318 
FPDFLink_GetLinkZOrderAtPoint(FPDF_PAGE page,double x,double y)319 FPDF_EXPORT int FPDF_CALLCONV FPDFLink_GetLinkZOrderAtPoint(FPDF_PAGE page,
320                                                             double x,
321                                                             double y) {
322   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
323   if (!pPage)
324     return -1;
325 
326   CPDF_LinkList* pLinkList = GetLinkList(pPage);
327   if (!pLinkList)
328     return -1;
329 
330   int z_order = -1;
331   pLinkList->GetLinkAtPoint(
332       pPage, CFX_PointF(static_cast<float>(x), static_cast<float>(y)),
333       &z_order);
334   return z_order;
335 }
336 
FPDFLink_GetDest(FPDF_DOCUMENT document,FPDF_LINK link)337 FPDF_EXPORT FPDF_DEST FPDF_CALLCONV FPDFLink_GetDest(FPDF_DOCUMENT document,
338                                                      FPDF_LINK link) {
339   if (!link)
340     return nullptr;
341   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
342   if (!pDoc)
343     return nullptr;
344   CPDF_Link cLink(pdfium::WrapRetain(CPDFDictionaryFromFPDFLink(link)));
345   FPDF_DEST dest = FPDFDestFromCPDFArray(cLink.GetDest(pDoc).GetArray());
346   if (dest)
347     return dest;
348   // If this link is not directly associated with a dest, we try to get action
349   CPDF_Action action = cLink.GetAction();
350   if (!action.HasDict())
351     return nullptr;
352   return FPDFDestFromCPDFArray(action.GetDest(pDoc).GetArray());
353 }
354 
FPDFLink_GetAction(FPDF_LINK link)355 FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV FPDFLink_GetAction(FPDF_LINK link) {
356   if (!link)
357     return nullptr;
358 
359   CPDF_Link cLink(pdfium::WrapRetain(CPDFDictionaryFromFPDFLink(link)));
360   return FPDFActionFromCPDFDictionary(cLink.GetAction().GetDict());
361 }
362 
FPDFLink_Enumerate(FPDF_PAGE page,int * start_pos,FPDF_LINK * link_annot)363 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_Enumerate(FPDF_PAGE page,
364                                                        int* start_pos,
365                                                        FPDF_LINK* link_annot) {
366   if (!start_pos || !link_annot)
367     return false;
368   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
369   if (!pPage)
370     return false;
371   RetainPtr<CPDF_Array> pAnnots = pPage->GetMutableAnnotsArray();
372   if (!pAnnots)
373     return false;
374   for (size_t i = *start_pos; i < pAnnots->size(); i++) {
375     RetainPtr<CPDF_Dictionary> pDict =
376         ToDictionary(pAnnots->GetMutableDirectObjectAt(i));
377     if (!pDict)
378       continue;
379     if (pDict->GetByteStringFor("Subtype") == "Link") {
380       *start_pos = static_cast<int>(i + 1);
381       *link_annot = FPDFLinkFromCPDFDictionary(pDict.Get());
382       return true;
383     }
384   }
385   return false;
386 }
387 
388 FPDF_EXPORT FPDF_ANNOTATION FPDF_CALLCONV
FPDFLink_GetAnnot(FPDF_PAGE page,FPDF_LINK link_annot)389 FPDFLink_GetAnnot(FPDF_PAGE page, FPDF_LINK link_annot) {
390   CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
391   RetainPtr<CPDF_Dictionary> pAnnotDict(CPDFDictionaryFromFPDFLink(link_annot));
392   if (!pPage || !pAnnotDict)
393     return nullptr;
394 
395   auto pAnnotContext = std::make_unique<CPDF_AnnotContext>(
396       std::move(pAnnotDict), IPDFPageFromFPDFPage(page));
397 
398   // Caller takes the ownership of the object.
399   return FPDFAnnotationFromCPDFAnnotContext(pAnnotContext.release());
400 }
401 
FPDFLink_GetAnnotRect(FPDF_LINK link_annot,FS_RECTF * rect)402 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV FPDFLink_GetAnnotRect(FPDF_LINK link_annot,
403                                                           FS_RECTF* rect) {
404   if (!link_annot || !rect)
405     return false;
406 
407   CPDF_Dictionary* pAnnotDict = CPDFDictionaryFromFPDFLink(link_annot);
408   *rect = FSRectFFromCFXFloatRect(pAnnotDict->GetRectFor("Rect"));
409   return true;
410 }
411 
FPDFLink_CountQuadPoints(FPDF_LINK link_annot)412 FPDF_EXPORT int FPDF_CALLCONV FPDFLink_CountQuadPoints(FPDF_LINK link_annot) {
413   RetainPtr<const CPDF_Array> pArray =
414       GetQuadPointsArrayFromDictionary(CPDFDictionaryFromFPDFLink(link_annot));
415   return pArray ? static_cast<int>(pArray->size() / 8) : 0;
416 }
417 
418 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDFLink_GetQuadPoints(FPDF_LINK link_annot,int quad_index,FS_QUADPOINTSF * quad_points)419 FPDFLink_GetQuadPoints(FPDF_LINK link_annot,
420                        int quad_index,
421                        FS_QUADPOINTSF* quad_points) {
422   if (!quad_points || quad_index < 0)
423     return false;
424 
425   const CPDF_Dictionary* pLinkDict = CPDFDictionaryFromFPDFLink(link_annot);
426   if (!pLinkDict)
427     return false;
428 
429   RetainPtr<const CPDF_Array> pArray =
430       GetQuadPointsArrayFromDictionary(pLinkDict);
431   if (!pArray)
432     return false;
433 
434   return GetQuadPointsAtIndex(std::move(pArray),
435                               static_cast<size_t>(quad_index), quad_points);
436 }
437 
FPDF_GetPageAAction(FPDF_PAGE page,int aa_type)438 FPDF_EXPORT FPDF_ACTION FPDF_CALLCONV FPDF_GetPageAAction(FPDF_PAGE page,
439                                                           int aa_type) {
440   CPDF_Page* pdf_page = CPDFPageFromFPDFPage(page);
441   if (!pdf_page)
442     return nullptr;
443 
444   CPDF_AAction aa(pdf_page->GetDict()->GetDictFor(pdfium::form_fields::kAA));
445   CPDF_AAction::AActionType type;
446   if (aa_type == FPDFPAGE_AACTION_OPEN)
447     type = CPDF_AAction::kOpenPage;
448   else if (aa_type == FPDFPAGE_AACTION_CLOSE)
449     type = CPDF_AAction::kClosePage;
450   else
451     return nullptr;
452 
453   if (!aa.ActionExist(type))
454     return nullptr;
455 
456   CPDF_Action action = aa.GetAction(type);
457   return FPDFActionFromCPDFDictionary(action.GetDict());
458 }
459 
460 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_GetFileIdentifier(FPDF_DOCUMENT document,FPDF_FILEIDTYPE id_type,void * buffer,unsigned long buflen)461 FPDF_GetFileIdentifier(FPDF_DOCUMENT document,
462                        FPDF_FILEIDTYPE id_type,
463                        void* buffer,
464                        unsigned long buflen) {
465   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
466   if (!pDoc)
467     return 0;
468 
469   // Check if |id_type| is valid.
470   if (id_type != FILEIDTYPE_PERMANENT && id_type != FILEIDTYPE_CHANGING)
471     return 0;
472 
473   RetainPtr<const CPDF_Array> pFileId = pDoc->GetFileIdentifier();
474   if (!pFileId)
475     return 0;
476 
477   size_t nIndex = id_type == FILEIDTYPE_PERMANENT ? 0 : 1;
478   RetainPtr<const CPDF_String> pValue =
479       ToString(pFileId->GetDirectObjectAt(nIndex));
480   if (!pValue)
481     return 0;
482 
483   return NulTerminateMaybeCopyAndReturnLength(pValue->GetString(), buffer,
484                                               buflen);
485 }
486 
FPDF_GetMetaText(FPDF_DOCUMENT document,FPDF_BYTESTRING tag,void * buffer,unsigned long buflen)487 FPDF_EXPORT unsigned long FPDF_CALLCONV FPDF_GetMetaText(FPDF_DOCUMENT document,
488                                                          FPDF_BYTESTRING tag,
489                                                          void* buffer,
490                                                          unsigned long buflen) {
491   if (!tag)
492     return 0;
493   CPDF_Document* pDoc = CPDFDocumentFromFPDFDocument(document);
494   if (!pDoc)
495     return 0;
496 
497   RetainPtr<const CPDF_Dictionary> pInfo = pDoc->GetInfo();
498   if (!pInfo)
499     return 0;
500 
501   WideString text = pInfo->GetUnicodeTextFor(tag);
502   return Utf16EncodeMaybeCopyAndReturnLength(text, buffer, buflen);
503 }
504 
505 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_GetPageLabel(FPDF_DOCUMENT document,int page_index,void * buffer,unsigned long buflen)506 FPDF_GetPageLabel(FPDF_DOCUMENT document,
507                   int page_index,
508                   void* buffer,
509                   unsigned long buflen) {
510   if (page_index < 0)
511     return 0;
512 
513   // CPDF_PageLabel can deal with NULL |document|.
514   CPDF_PageLabel label(CPDFDocumentFromFPDFDocument(document));
515   absl::optional<WideString> str = label.GetLabel(page_index);
516   return str.has_value()
517              ? Utf16EncodeMaybeCopyAndReturnLength(str.value(), buffer, buflen)
518              : 0;
519 }
520