• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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