1 // Copyright 2016 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 #include "public/fpdf_structtree.h"
6
7 #include <memory>
8
9 #include "core/fpdfapi/page/cpdf_page.h"
10 #include "core/fpdfapi/parser/cpdf_array.h"
11 #include "core/fpdfapi/parser/cpdf_dictionary.h"
12 #include "core/fpdfdoc/cpdf_structelement.h"
13 #include "core/fpdfdoc/cpdf_structtree.h"
14 #include "core/fxcrt/compiler_specific.h"
15 #include "core/fxcrt/fx_memcpy_wrappers.h"
16 #include "core/fxcrt/fx_safe_types.h"
17 #include "core/fxcrt/numerics/safe_conversions.h"
18 #include "core/fxcrt/stl_util.h"
19 #include "fpdfsdk/cpdfsdk_helpers.h"
20
21 namespace {
22
WideStringToBuffer(const WideString & str,void * buffer,unsigned long buflen)23 UNSAFE_BUFFER_USAGE unsigned long WideStringToBuffer(const WideString& str,
24 void* buffer,
25 unsigned long buflen) {
26 if (str.IsEmpty()) {
27 return 0;
28 }
29 // SAFETY: required from caller and enforced by UNSAFE_BUFFER_USAGE.
30 return Utf16EncodeMaybeCopyAndReturnLength(
31 str, UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen)));
32 }
33
GetMcidFromDict(const CPDF_Dictionary * dict)34 int GetMcidFromDict(const CPDF_Dictionary* dict) {
35 if (dict && dict->GetNameFor("Type") == "MCR") {
36 RetainPtr<const CPDF_Object> obj = dict->GetObjectFor("MCID");
37 if (obj && obj->IsNumber())
38 return obj->GetInteger();
39 }
40 return -1;
41 }
42
43 } // namespace
44
45 FPDF_EXPORT FPDF_STRUCTTREE FPDF_CALLCONV
FPDF_StructTree_GetForPage(FPDF_PAGE page)46 FPDF_StructTree_GetForPage(FPDF_PAGE page) {
47 CPDF_Page* pPage = CPDFPageFromFPDFPage(page);
48 if (!pPage)
49 return nullptr;
50
51 // Caller takes onwership.
52 return FPDFStructTreeFromCPDFStructTree(
53 CPDF_StructTree::LoadPage(pPage->GetDocument(), pPage->GetDict())
54 .release());
55 }
56
57 FPDF_EXPORT void FPDF_CALLCONV
FPDF_StructTree_Close(FPDF_STRUCTTREE struct_tree)58 FPDF_StructTree_Close(FPDF_STRUCTTREE struct_tree) {
59 std::unique_ptr<CPDF_StructTree>(
60 CPDFStructTreeFromFPDFStructTree(struct_tree));
61 }
62
63 FPDF_EXPORT int FPDF_CALLCONV
FPDF_StructTree_CountChildren(FPDF_STRUCTTREE struct_tree)64 FPDF_StructTree_CountChildren(FPDF_STRUCTTREE struct_tree) {
65 CPDF_StructTree* tree = CPDFStructTreeFromFPDFStructTree(struct_tree);
66 if (!tree)
67 return -1;
68
69 FX_SAFE_INT32 tmp_size = tree->CountTopElements();
70 return tmp_size.ValueOrDefault(-1);
71 }
72
73 FPDF_EXPORT FPDF_STRUCTELEMENT FPDF_CALLCONV
FPDF_StructTree_GetChildAtIndex(FPDF_STRUCTTREE struct_tree,int index)74 FPDF_StructTree_GetChildAtIndex(FPDF_STRUCTTREE struct_tree, int index) {
75 CPDF_StructTree* tree = CPDFStructTreeFromFPDFStructTree(struct_tree);
76 if (!tree || index < 0 ||
77 static_cast<size_t>(index) >= tree->CountTopElements()) {
78 return nullptr;
79 }
80 return FPDFStructElementFromCPDFStructElement(
81 tree->GetTopElement(static_cast<size_t>(index)));
82 }
83
84 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_StructElement_GetAltText(FPDF_STRUCTELEMENT struct_element,void * buffer,unsigned long buflen)85 FPDF_StructElement_GetAltText(FPDF_STRUCTELEMENT struct_element,
86 void* buffer,
87 unsigned long buflen) {
88 CPDF_StructElement* elem =
89 CPDFStructElementFromFPDFStructElement(struct_element);
90 if (!elem) {
91 return 0;
92 }
93 // SAFETY: required from caller.
94 return UNSAFE_BUFFERS(WideStringToBuffer(elem->GetAltText(), buffer, buflen));
95 }
96
97 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_StructElement_GetActualText(FPDF_STRUCTELEMENT struct_element,void * buffer,unsigned long buflen)98 FPDF_StructElement_GetActualText(FPDF_STRUCTELEMENT struct_element,
99 void* buffer,
100 unsigned long buflen) {
101 CPDF_StructElement* elem =
102 CPDFStructElementFromFPDFStructElement(struct_element);
103 if (!elem) {
104 return 0;
105 }
106 // SAFETY: required from caller.
107 return UNSAFE_BUFFERS(
108 WideStringToBuffer(elem->GetActualText(), buffer, buflen));
109 }
110
111 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_StructElement_GetID(FPDF_STRUCTELEMENT struct_element,void * buffer,unsigned long buflen)112 FPDF_StructElement_GetID(FPDF_STRUCTELEMENT struct_element,
113 void* buffer,
114 unsigned long buflen) {
115 CPDF_StructElement* elem =
116 CPDFStructElementFromFPDFStructElement(struct_element);
117 if (!elem) {
118 return 0;
119 }
120 std::optional<WideString> id = elem->GetID();
121 if (!id.has_value()) {
122 return 0;
123 }
124 // SAFETY: required from caller.
125 return Utf16EncodeMaybeCopyAndReturnLength(
126 id.value(), UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen)));
127 }
128
129 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_StructElement_GetLang(FPDF_STRUCTELEMENT struct_element,void * buffer,unsigned long buflen)130 FPDF_StructElement_GetLang(FPDF_STRUCTELEMENT struct_element,
131 void* buffer,
132 unsigned long buflen) {
133 CPDF_StructElement* elem =
134 CPDFStructElementFromFPDFStructElement(struct_element);
135 if (!elem) {
136 return 0;
137 }
138 std::optional<WideString> lang = elem->GetLang();
139 if (!lang.has_value()) {
140 return 0;
141 }
142 // SAFETY: required from caller.
143 return Utf16EncodeMaybeCopyAndReturnLength(
144 lang.value(), UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen)));
145 }
146
147 FPDF_EXPORT int FPDF_CALLCONV
FPDF_StructElement_GetAttributeCount(FPDF_STRUCTELEMENT struct_element)148 FPDF_StructElement_GetAttributeCount(FPDF_STRUCTELEMENT struct_element) {
149 CPDF_StructElement* elem =
150 CPDFStructElementFromFPDFStructElement(struct_element);
151 if (!elem)
152 return -1;
153 RetainPtr<const CPDF_Object> attr_obj = elem->GetA();
154 if (!attr_obj) {
155 return -1;
156 }
157 attr_obj = attr_obj->GetDirect();
158 if (!attr_obj)
159 return -1;
160 if (attr_obj->IsArray())
161 return fxcrt::CollectionSize<int>(*attr_obj->AsArray());
162 return attr_obj->IsDictionary() ? 1 : -1;
163 }
164
165 FPDF_EXPORT FPDF_STRUCTELEMENT_ATTR FPDF_CALLCONV
FPDF_StructElement_GetAttributeAtIndex(FPDF_STRUCTELEMENT struct_element,int index)166 FPDF_StructElement_GetAttributeAtIndex(FPDF_STRUCTELEMENT struct_element,
167 int index) {
168 CPDF_StructElement* elem =
169 CPDFStructElementFromFPDFStructElement(struct_element);
170 if (!elem)
171 return nullptr;
172
173 RetainPtr<const CPDF_Object> attr_obj = elem->GetA();
174 if (!attr_obj)
175 return nullptr;
176
177 attr_obj = attr_obj->GetDirect();
178 if (!attr_obj) {
179 return nullptr;
180 }
181 if (attr_obj->IsDictionary()) {
182 return index == 0 ? FPDFStructElementAttrFromCPDFDictionary(
183 attr_obj->AsDictionary())
184 : nullptr;
185 }
186 if (attr_obj->IsArray()) {
187 const CPDF_Array* array = attr_obj->AsArray();
188 if (index < 0 || static_cast<size_t>(index) >= array->size())
189 return nullptr;
190
191 // TODO(tsepez): should embedder take a reference here?
192 // Unretained reference in public API. NOLINTNEXTLINE
193 return FPDFStructElementAttrFromCPDFDictionary(array->GetDictAt(index));
194 }
195 return nullptr;
196 }
197
198 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_StructElement_GetStringAttribute(FPDF_STRUCTELEMENT struct_element,FPDF_BYTESTRING attr_name,void * buffer,unsigned long buflen)199 FPDF_StructElement_GetStringAttribute(FPDF_STRUCTELEMENT struct_element,
200 FPDF_BYTESTRING attr_name,
201 void* buffer,
202 unsigned long buflen) {
203 CPDF_StructElement* elem =
204 CPDFStructElementFromFPDFStructElement(struct_element);
205 if (!elem)
206 return 0;
207 RetainPtr<const CPDF_Array> array = ToArray(elem->GetA());
208 if (!array)
209 return 0;
210 CPDF_ArrayLocker locker(array);
211 for (const RetainPtr<CPDF_Object>& obj : locker) {
212 const CPDF_Dictionary* obj_dict = obj->AsDictionary();
213 if (!obj_dict) {
214 continue;
215 }
216 RetainPtr<const CPDF_Object> attr = obj_dict->GetObjectFor(attr_name);
217 if (!attr || !(attr->IsString() || attr->IsName())) {
218 continue;
219 }
220 // SAFETY: required from caller.
221 return Utf16EncodeMaybeCopyAndReturnLength(
222 attr->GetUnicodeText(),
223 UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen)));
224 }
225 return 0;
226 }
227
228 FPDF_EXPORT int FPDF_CALLCONV
FPDF_StructElement_GetMarkedContentID(FPDF_STRUCTELEMENT struct_element)229 FPDF_StructElement_GetMarkedContentID(FPDF_STRUCTELEMENT struct_element) {
230 CPDF_StructElement* elem =
231 CPDFStructElementFromFPDFStructElement(struct_element);
232 if (!elem)
233 return -1;
234 RetainPtr<const CPDF_Object> p = elem->GetK();
235 return p && p->IsNumber() ? p->GetInteger() : -1;
236 }
237
238 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_StructElement_GetType(FPDF_STRUCTELEMENT struct_element,void * buffer,unsigned long buflen)239 FPDF_StructElement_GetType(FPDF_STRUCTELEMENT struct_element,
240 void* buffer,
241 unsigned long buflen) {
242 CPDF_StructElement* elem =
243 CPDFStructElementFromFPDFStructElement(struct_element);
244 if (!elem) {
245 return 0;
246 }
247 // SAFETY: required from caller.
248 return UNSAFE_BUFFERS(WideStringToBuffer(
249 WideString::FromUTF8(elem->GetType().AsStringView()), buffer, buflen));
250 }
251
252 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_StructElement_GetObjType(FPDF_STRUCTELEMENT struct_element,void * buffer,unsigned long buflen)253 FPDF_StructElement_GetObjType(FPDF_STRUCTELEMENT struct_element,
254 void* buffer,
255 unsigned long buflen) {
256 CPDF_StructElement* elem =
257 CPDFStructElementFromFPDFStructElement(struct_element);
258 if (!elem) {
259 return 0;
260 }
261 // SAFETY: required from caller.
262 return UNSAFE_BUFFERS(WideStringToBuffer(
263 WideString::FromUTF8(elem->GetObjType().AsStringView()), buffer, buflen));
264 }
265
266 FPDF_EXPORT unsigned long FPDF_CALLCONV
FPDF_StructElement_GetTitle(FPDF_STRUCTELEMENT struct_element,void * buffer,unsigned long buflen)267 FPDF_StructElement_GetTitle(FPDF_STRUCTELEMENT struct_element,
268 void* buffer,
269 unsigned long buflen) {
270 CPDF_StructElement* elem =
271 CPDFStructElementFromFPDFStructElement(struct_element);
272 if (!elem) {
273 return 0;
274 }
275 // SAFETY: required from caller.
276 return UNSAFE_BUFFERS(WideStringToBuffer(elem->GetTitle(), buffer, buflen));
277 }
278
279 FPDF_EXPORT int FPDF_CALLCONV
FPDF_StructElement_CountChildren(FPDF_STRUCTELEMENT struct_element)280 FPDF_StructElement_CountChildren(FPDF_STRUCTELEMENT struct_element) {
281 CPDF_StructElement* elem =
282 CPDFStructElementFromFPDFStructElement(struct_element);
283 if (!elem)
284 return -1;
285
286 FX_SAFE_INT32 tmp_size = elem->CountKids();
287 return tmp_size.ValueOrDefault(-1);
288 }
289
290 FPDF_EXPORT FPDF_STRUCTELEMENT FPDF_CALLCONV
FPDF_StructElement_GetChildAtIndex(FPDF_STRUCTELEMENT struct_element,int index)291 FPDF_StructElement_GetChildAtIndex(FPDF_STRUCTELEMENT struct_element,
292 int index) {
293 CPDF_StructElement* elem =
294 CPDFStructElementFromFPDFStructElement(struct_element);
295 if (!elem || index < 0 || static_cast<size_t>(index) >= elem->CountKids())
296 return nullptr;
297
298 return FPDFStructElementFromCPDFStructElement(elem->GetKidIfElement(index));
299 }
300
301 FPDF_EXPORT int FPDF_CALLCONV
FPDF_StructElement_GetChildMarkedContentID(FPDF_STRUCTELEMENT struct_element,int index)302 FPDF_StructElement_GetChildMarkedContentID(FPDF_STRUCTELEMENT struct_element,
303 int index) {
304 CPDF_StructElement* elem =
305 CPDFStructElementFromFPDFStructElement(struct_element);
306 if (!elem || index < 0 || static_cast<size_t>(index) >= elem->CountKids()) {
307 return -1;
308 }
309
310 return elem->GetKidContentId(index);
311 }
312
313 FPDF_EXPORT FPDF_STRUCTELEMENT FPDF_CALLCONV
FPDF_StructElement_GetParent(FPDF_STRUCTELEMENT struct_element)314 FPDF_StructElement_GetParent(FPDF_STRUCTELEMENT struct_element) {
315 CPDF_StructElement* elem =
316 CPDFStructElementFromFPDFStructElement(struct_element);
317 CPDF_StructElement* parent = elem ? elem->GetParent() : nullptr;
318 if (!parent) {
319 return nullptr;
320 }
321 return FPDFStructElementFromCPDFStructElement(parent);
322 }
323
324 FPDF_EXPORT int FPDF_CALLCONV
FPDF_StructElement_Attr_GetCount(FPDF_STRUCTELEMENT_ATTR struct_attribute)325 FPDF_StructElement_Attr_GetCount(FPDF_STRUCTELEMENT_ATTR struct_attribute) {
326 const CPDF_Dictionary* dict =
327 CPDFDictionaryFromFPDFStructElementAttr(struct_attribute);
328 if (!dict)
329 return -1;
330 return fxcrt::CollectionSize<int>(*dict);
331 }
332
333 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_StructElement_Attr_GetName(FPDF_STRUCTELEMENT_ATTR struct_attribute,int index,void * buffer,unsigned long buflen,unsigned long * out_buflen)334 FPDF_StructElement_Attr_GetName(FPDF_STRUCTELEMENT_ATTR struct_attribute,
335 int index,
336 void* buffer,
337 unsigned long buflen,
338 unsigned long* out_buflen) {
339 if (!out_buflen) {
340 return false;
341 }
342
343 const CPDF_Dictionary* dict =
344 CPDFDictionaryFromFPDFStructElementAttr(struct_attribute);
345 if (!dict)
346 return false;
347
348 CPDF_DictionaryLocker locker(dict);
349 for (auto& it : locker) {
350 if (index == 0) {
351 // SAFETY: required from caller.
352 *out_buflen = NulTerminateMaybeCopyAndReturnLength(
353 it.first, UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen)));
354 return true;
355 }
356 --index;
357 }
358 return false;
359 }
360
361 FPDF_EXPORT FPDF_STRUCTELEMENT_ATTR_VALUE FPDF_CALLCONV
FPDF_StructElement_Attr_GetValue(FPDF_STRUCTELEMENT_ATTR struct_attribute,FPDF_BYTESTRING name)362 FPDF_StructElement_Attr_GetValue(FPDF_STRUCTELEMENT_ATTR struct_attribute,
363 FPDF_BYTESTRING name) {
364 const CPDF_Dictionary* dict =
365 CPDFDictionaryFromFPDFStructElementAttr(struct_attribute);
366 if (!dict) {
367 return nullptr;
368 }
369 return FPDFStructElementAttrValueFromCPDFObject(
370 dict->GetDirectObjectFor(name));
371 }
372
373 FPDF_EXPORT FPDF_OBJECT_TYPE FPDF_CALLCONV
FPDF_StructElement_Attr_GetType(FPDF_STRUCTELEMENT_ATTR_VALUE value)374 FPDF_StructElement_Attr_GetType(FPDF_STRUCTELEMENT_ATTR_VALUE value) {
375 const CPDF_Object* obj = CPDFObjectFromFPDFStructElementAttrValue(value);
376 return obj ? obj->GetType() : FPDF_OBJECT_UNKNOWN;
377 }
378
379 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_StructElement_Attr_GetBooleanValue(FPDF_STRUCTELEMENT_ATTR_VALUE value,FPDF_BOOL * out_value)380 FPDF_StructElement_Attr_GetBooleanValue(FPDF_STRUCTELEMENT_ATTR_VALUE value,
381 FPDF_BOOL* out_value) {
382 if (!out_value) {
383 return false;
384 }
385
386 const CPDF_Object* obj = CPDFObjectFromFPDFStructElementAttrValue(value);
387 if (!obj || !obj->IsBoolean()) {
388 return false;
389 }
390
391 *out_value = obj->GetInteger();
392 return true;
393 }
394
395 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_StructElement_Attr_GetNumberValue(FPDF_STRUCTELEMENT_ATTR_VALUE value,float * out_value)396 FPDF_StructElement_Attr_GetNumberValue(FPDF_STRUCTELEMENT_ATTR_VALUE value,
397 float* out_value) {
398 if (!out_value) {
399 return false;
400 }
401
402 const CPDF_Object* obj = CPDFObjectFromFPDFStructElementAttrValue(value);
403 if (!obj || !obj->IsNumber()) {
404 return false;
405 }
406
407 *out_value = obj->GetNumber();
408 return true;
409 }
410
411 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_StructElement_Attr_GetStringValue(FPDF_STRUCTELEMENT_ATTR_VALUE value,void * buffer,unsigned long buflen,unsigned long * out_buflen)412 FPDF_StructElement_Attr_GetStringValue(FPDF_STRUCTELEMENT_ATTR_VALUE value,
413 void* buffer,
414 unsigned long buflen,
415 unsigned long* out_buflen) {
416 if (!out_buflen) {
417 return false;
418 }
419
420 const CPDF_Object* obj = CPDFObjectFromFPDFStructElementAttrValue(value);
421 if (!obj || !(obj->IsString() || obj->IsName())) {
422 return false;
423 }
424
425 // SAFETY: required from caller.
426 *out_buflen = Utf16EncodeMaybeCopyAndReturnLength(
427 WideString::FromUTF8(obj->GetString().AsStringView()),
428 UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen)));
429 return true;
430 }
431
432 FPDF_EXPORT FPDF_BOOL FPDF_CALLCONV
FPDF_StructElement_Attr_GetBlobValue(FPDF_STRUCTELEMENT_ATTR_VALUE value,void * buffer,unsigned long buflen,unsigned long * out_buflen)433 FPDF_StructElement_Attr_GetBlobValue(FPDF_STRUCTELEMENT_ATTR_VALUE value,
434 void* buffer,
435 unsigned long buflen,
436 unsigned long* out_buflen) {
437 if (!out_buflen) {
438 return false;
439 }
440
441 const CPDF_Object* obj = CPDFObjectFromFPDFStructElementAttrValue(value);
442 if (!obj || !obj->IsString()) {
443 return false;
444 }
445
446 // SAFETY: required from caller.
447 auto result_span = UNSAFE_BUFFERS(SpanFromFPDFApiArgs(buffer, buflen));
448 ByteString blob_value = obj->GetString();
449 fxcrt::try_spancpy(result_span, blob_value.span());
450 *out_buflen = pdfium::checked_cast<unsigned long>(blob_value.span().size());
451 return true;
452 }
453
454 FPDF_EXPORT int FPDF_CALLCONV
FPDF_StructElement_Attr_CountChildren(FPDF_STRUCTELEMENT_ATTR_VALUE value)455 FPDF_StructElement_Attr_CountChildren(FPDF_STRUCTELEMENT_ATTR_VALUE value) {
456 const CPDF_Array* array =
457 ToArray(CPDFObjectFromFPDFStructElementAttrValue(value));
458 return array ? fxcrt::CollectionSize<int>(*array) : -1;
459 }
460
461 FPDF_EXPORT FPDF_STRUCTELEMENT_ATTR_VALUE FPDF_CALLCONV
FPDF_StructElement_Attr_GetChildAtIndex(FPDF_STRUCTELEMENT_ATTR_VALUE value,int index)462 FPDF_StructElement_Attr_GetChildAtIndex(FPDF_STRUCTELEMENT_ATTR_VALUE value,
463 int index) {
464 if (index < 0) {
465 return nullptr;
466 }
467
468 const auto* array = ToArray(CPDFObjectFromFPDFStructElementAttrValue(value));
469 if (!array) {
470 return nullptr;
471 }
472
473 return FPDFStructElementAttrValueFromCPDFObject(array->GetObjectAt(index));
474 }
475
476 FPDF_EXPORT int FPDF_CALLCONV
FPDF_StructElement_GetMarkedContentIdCount(FPDF_STRUCTELEMENT struct_element)477 FPDF_StructElement_GetMarkedContentIdCount(FPDF_STRUCTELEMENT struct_element) {
478 CPDF_StructElement* elem =
479 CPDFStructElementFromFPDFStructElement(struct_element);
480 if (!elem)
481 return -1;
482 RetainPtr<const CPDF_Object> p = elem->GetK();
483 if (!p)
484 return -1;
485
486 if (p->IsNumber() || p->IsDictionary())
487 return 1;
488
489 return p->IsArray() ? fxcrt::CollectionSize<int>(*p->AsArray()) : -1;
490 }
491
492 FPDF_EXPORT int FPDF_CALLCONV
FPDF_StructElement_GetMarkedContentIdAtIndex(FPDF_STRUCTELEMENT struct_element,int index)493 FPDF_StructElement_GetMarkedContentIdAtIndex(FPDF_STRUCTELEMENT struct_element,
494 int index) {
495 CPDF_StructElement* elem =
496 CPDFStructElementFromFPDFStructElement(struct_element);
497 if (!elem)
498 return -1;
499 RetainPtr<const CPDF_Object> p = elem->GetK();
500 if (!p)
501 return -1;
502
503 if (p->IsNumber())
504 return index == 0 ? p->GetInteger() : -1;
505
506 if (p->IsDictionary())
507 return GetMcidFromDict(p->GetDict().Get());
508
509 if (p->IsArray()) {
510 const CPDF_Array* array = p->AsArray();
511 if (index < 0 || static_cast<size_t>(index) >= array->size())
512 return -1;
513 RetainPtr<const CPDF_Object> array_elem = array->GetObjectAt(index);
514 if (array_elem->IsNumber())
515 return array_elem->GetInteger();
516 if (array_elem->IsDictionary()) {
517 return GetMcidFromDict(array_elem->GetDict().Get());
518 }
519 }
520 return -1;
521 }
522