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