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