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