Coverage Report

Created: 2026-11-12 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mupdf/source/pdf/pdf-run.c
Line
Count
Source
1
// Copyright (C) 2004-2026 Artifex Software, Inc.
2
//
3
// This file is part of MuPDF.
4
//
5
// MuPDF is free software: you can redistribute it and/or modify it under the
6
// terms of the GNU Affero General Public License as published by the Free
7
// Software Foundation, either version 3 of the License, or (at your option)
8
// any later version.
9
//
10
// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
// details.
14
//
15
// You should have received a copy of the GNU Affero General Public License
16
// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17
//
18
// Alternative licensing terms are available from the licensor.
19
// For commercial licensing, see <https://www.artifex.com/> or contact
20
// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21
// CA 94129, USA, for further information.
22
23
#include "mupdf/fitz.h"
24
#include "pdf-annot-imp.h"
25
26
static void
27
pdf_run_annot_with_usage(fz_context *ctx, pdf_document *doc, pdf_page *page, pdf_annot *annot, fz_device *dev, fz_matrix ctm, const char *usage, fz_cookie *cookie)
28
445
{
29
445
  fz_matrix page_ctm;
30
445
  fz_rect mediabox;
31
445
  pdf_processor *proc = NULL;
32
445
  fz_default_colorspaces *default_cs = NULL;
33
445
  int resources_pushed = 0;
34
445
  int struct_parent_num;
35
445
  pdf_obj *struct_parent;
36
37
445
  fz_var(proc);
38
445
  fz_var(default_cs);
39
445
  fz_var(resources_pushed);
40
41
445
  if (cookie && page->super.incomplete)
42
0
    cookie->incomplete = 1;
43
44
445
  pdf_annot_push_local_xref(ctx, annot);
45
46
890
  fz_try(ctx)
47
890
  {
48
445
    default_cs = pdf_load_default_colorspaces(ctx, doc, page);
49
445
    if (default_cs)
50
445
      fz_set_default_colorspaces(ctx, dev, default_cs);
51
52
445
    pdf_page_transform(ctx, page, &mediabox, &page_ctm);
53
54
445
    ctm = fz_concat(page_ctm, ctm);
55
56
445
    struct_parent = pdf_dict_getl(ctx, page->obj, PDF_NAME(StructParent), NULL);
57
445
    struct_parent_num = pdf_to_int_default(ctx, struct_parent, -1);
58
59
445
    proc = pdf_new_run_processor(ctx, page->doc, dev, ctm, struct_parent_num, usage, NULL, default_cs, cookie, NULL, NULL);
60
445
    pdf_processor_push_resources(ctx, proc, pdf_page_resources(ctx, annot->page));
61
445
    resources_pushed = 1;
62
445
    pdf_process_annot(ctx, proc, annot, cookie);
63
445
    pdf_close_processor(ctx, proc);
64
445
  }
65
890
  fz_always(ctx)
66
445
  {
67
445
    if (resources_pushed)
68
445
      pdf_drop_obj(ctx, pdf_processor_pop_resources(ctx, proc));
69
445
    pdf_drop_processor(ctx, proc);
70
445
    fz_drop_default_colorspaces(ctx, default_cs);
71
445
    pdf_annot_pop_local_xref(ctx, annot);
72
445
  }
73
445
  fz_catch(ctx)
74
0
    fz_rethrow(ctx);
75
445
}
76
77
static fz_rect pdf_page_cropbox(fz_context *ctx, pdf_page *page)
78
326
{
79
326
  pdf_obj *obj = pdf_dict_get_inheritable(ctx, page->obj, PDF_NAME(CropBox));
80
326
  if (!obj)
81
201
    obj = pdf_dict_get_inheritable(ctx, page->obj, PDF_NAME(MediaBox));
82
326
  return pdf_to_rect(ctx, obj);
83
326
}
84
85
static fz_rect pdf_page_mediabox(fz_context *ctx, pdf_page *page)
86
326
{
87
326
  return pdf_dict_get_inheritable_rect(ctx, page->obj, PDF_NAME(MediaBox));
88
326
}
89
90
static void
91
pdf_run_page_contents_with_usage_imp(fz_context *ctx, pdf_document *doc, pdf_page *page, fz_device *dev, fz_matrix ctm, const char *usage, fz_cookie *cookie)
92
326
{
93
326
  fz_matrix page_ctm;
94
326
  pdf_obj *resources;
95
326
  pdf_obj *contents;
96
326
  fz_rect fitzbox;
97
326
  fz_rect mediabox, cropbox;
98
326
  pdf_processor *proc = NULL;
99
326
  fz_default_colorspaces *default_cs = NULL;
100
326
  fz_colorspace *colorspace = NULL;
101
326
  fz_path *path = NULL;
102
326
  int struct_parent_num;
103
326
  pdf_obj *struct_parent;
104
105
326
  fz_var(proc);
106
326
  fz_var(colorspace);
107
326
  fz_var(default_cs);
108
326
  fz_var(path);
109
110
326
  if (cookie && page->super.incomplete)
111
0
    cookie->incomplete = 1;
112
113
652
  fz_try(ctx)
114
652
  {
115
326
    default_cs = pdf_load_default_colorspaces(ctx, doc, page);
116
326
    if (default_cs)
117
326
      fz_set_default_colorspaces(ctx, dev, default_cs);
118
119
326
    pdf_page_transform(ctx, page, &fitzbox, &page_ctm);
120
326
    ctm = fz_concat(page_ctm, ctm);
121
326
    fitzbox = fz_transform_rect(fitzbox, ctm);
122
123
326
    resources = pdf_page_resources(ctx, page);
124
326
    contents = pdf_page_contents(ctx, page);
125
126
326
    mediabox = pdf_page_mediabox(ctx, page);
127
326
    cropbox = pdf_page_cropbox(ctx, page);
128
129
326
    if (page->transparency)
130
90
    {
131
90
      pdf_obj *group = pdf_page_group(ctx, page);
132
133
90
      if (group)
134
1
      {
135
1
        pdf_obj *cs = pdf_dict_get(ctx, group, PDF_NAME(CS));
136
1
        if (cs)
137
1
        {
138
2
          fz_try(ctx)
139
2
            colorspace = pdf_load_colorspace(ctx, cs);
140
2
          fz_catch(ctx)
141
0
          {
142
0
            fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
143
0
            fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
144
0
            fz_report_error(ctx);
145
0
            fz_warn(ctx, "Ignoring Page blending colorspace.");
146
0
          }
147
1
          if (!fz_is_valid_blend_colorspace(ctx, colorspace))
148
0
          {
149
0
            fz_warn(ctx, "Ignoring invalid Page blending colorspace: %s.", colorspace->name);
150
0
            fz_drop_colorspace(ctx, colorspace);
151
0
            colorspace = NULL;
152
0
          }
153
1
        }
154
1
      }
155
89
      else
156
89
        colorspace = fz_keep_colorspace(ctx, fz_default_output_intent(ctx, default_cs));
157
158
90
      fz_begin_group(ctx, dev, fitzbox, colorspace, 1, 0, 0, 1);
159
90
    }
160
161
326
    struct_parent = pdf_dict_get(ctx, page->obj, PDF_NAME(StructParents));
162
326
    struct_parent_num = pdf_to_int_default(ctx, struct_parent, -1);
163
164
    /* Clip content to CropBox if it is smaller than the MediaBox */
165
326
    if (cropbox.x0 > mediabox.x0 || cropbox.x1 < mediabox.x1 || cropbox.y0 > mediabox.y0 || cropbox.y1 < mediabox.y1)
166
10
    {
167
10
      path = fz_new_path(ctx);
168
10
      fz_rectto(ctx, path, cropbox.x0, cropbox.y0, cropbox.x1, cropbox.y1);
169
10
      fz_clip_path(ctx, dev, path, 1, ctm, fz_infinite_rect);
170
10
    }
171
172
326
    proc = pdf_new_run_processor(ctx, page->doc, dev, ctm, struct_parent_num, usage, NULL, default_cs, cookie, NULL, NULL);
173
326
    pdf_process_contents(ctx, proc, doc, resources, contents, cookie, NULL);
174
326
    pdf_close_processor(ctx, proc);
175
176
326
    if (cropbox.x0 > mediabox.x0 || cropbox.x1 < mediabox.x1 || cropbox.y0 > mediabox.y0 || cropbox.y1 < mediabox.y1)
177
10
    {
178
10
      fz_pop_clip(ctx, dev);
179
10
    }
180
181
326
    if (page->transparency)
182
88
    {
183
88
      fz_end_group(ctx, dev);
184
88
    }
185
326
  }
186
652
  fz_always(ctx)
187
326
  {
188
326
    fz_drop_path(ctx, path);
189
326
    pdf_drop_processor(ctx, proc);
190
326
    fz_drop_colorspace(ctx, colorspace);
191
326
    fz_drop_default_colorspaces(ctx, default_cs);
192
326
  }
193
326
  fz_catch(ctx)
194
4
  {
195
4
    fz_rethrow(ctx);
196
4
  }
197
326
}
198
199
void pdf_run_page_contents_with_usage(fz_context *ctx, pdf_page *page, fz_device *dev, fz_matrix ctm, const char *usage, fz_cookie *cookie)
200
326
{
201
326
  pdf_document *doc = page->doc;
202
326
  int nocache;
203
204
326
  nocache = !!(dev->hints & FZ_NO_CACHE);
205
326
  if (nocache)
206
0
    pdf_mark_xref(ctx, doc);
207
208
652
  fz_try(ctx)
209
652
  {
210
326
    pdf_run_page_contents_with_usage_imp(ctx, doc, page, dev, ctm, usage, cookie);
211
326
  }
212
652
  fz_always(ctx)
213
326
  {
214
326
    if (nocache)
215
0
      pdf_clear_xref_to_mark(ctx, doc);
216
326
  }
217
326
  fz_catch(ctx)
218
4
  {
219
4
    fz_rethrow(ctx);
220
4
  }
221
326
}
222
223
void pdf_run_page_contents(fz_context *ctx, pdf_page *page, fz_device *dev, fz_matrix ctm, fz_cookie *cookie)
224
326
{
225
326
  pdf_run_page_contents_with_usage(ctx, page, dev, ctm, "View", cookie);
226
326
}
227
228
void pdf_run_annot(fz_context *ctx, pdf_annot *annot, fz_device *dev, fz_matrix ctm, fz_cookie *cookie)
229
0
{
230
0
  pdf_page *page = annot->page;
231
0
  pdf_document *doc;
232
0
  int nocache;
233
234
0
  if (!page)
235
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "annotation not bound to any page");
236
237
0
  pdf_update_page(ctx, page);
238
239
0
  doc = page->doc;
240
241
0
  nocache = !!(dev->hints & FZ_NO_CACHE);
242
0
  if (nocache)
243
0
    pdf_mark_xref(ctx, doc);
244
0
  fz_try(ctx)
245
0
  {
246
0
    pdf_run_annot_with_usage(ctx, doc, page, annot, dev, ctm, "View", cookie);
247
0
  }
248
0
  fz_always(ctx)
249
0
  {
250
0
    if (nocache)
251
0
      pdf_clear_xref_to_mark(ctx, doc);
252
0
  }
253
0
  fz_catch(ctx)
254
0
  {
255
0
    fz_rethrow(ctx);
256
0
  }
257
0
}
258
259
static void
260
pdf_run_page_widgets_with_usage_imp(fz_context *ctx, pdf_document *doc, pdf_page *page, fz_device *dev, fz_matrix ctm, const char *usage, fz_cookie *cookie)
261
322
{
262
322
  pdf_annot *widget;
263
264
322
  if (cookie && cookie->progress_max != (size_t)-1)
265
0
  {
266
0
    int count = 1;
267
0
    for (widget = page->widgets; widget; widget = widget->next)
268
0
      count++;
269
0
    cookie->progress_max += count;
270
0
  }
271
272
682
  for (widget = page->widgets; widget; widget = widget->next)
273
360
  {
274
    /* Check the cookie for aborting */
275
360
    if (cookie)
276
0
    {
277
0
      if (cookie->abort)
278
0
        break;
279
0
      cookie->progress++;
280
0
    }
281
282
360
    pdf_run_annot_with_usage(ctx, doc, page, widget, dev, ctm, usage, cookie);
283
360
  }
284
322
}
285
286
static void
287
pdf_run_page_annots_with_usage_imp(fz_context *ctx, pdf_document *doc, pdf_page *page, fz_device *dev, fz_matrix ctm, const char *usage, fz_cookie *cookie)
288
322
{
289
322
  pdf_annot *annot;
290
291
322
  if (cookie && cookie->progress_max != (size_t)-1)
292
0
  {
293
0
    int count = 1;
294
0
    for (annot = page->annots; annot; annot = annot->next)
295
0
      count++;
296
0
    cookie->progress_max += count;
297
0
  }
298
299
407
  for (annot = page->annots; annot; annot = annot->next)
300
85
  {
301
    /* Check the cookie for aborting */
302
85
    if (cookie)
303
0
    {
304
0
      if (cookie->abort)
305
0
        break;
306
0
      cookie->progress++;
307
0
    }
308
309
85
    pdf_run_annot_with_usage(ctx, doc, page, annot, dev, ctm, usage, cookie);
310
85
  }
311
322
}
312
313
void pdf_run_page_annots_with_usage(fz_context *ctx, pdf_page *page, fz_device *dev, fz_matrix ctm, const char *usage, fz_cookie *cookie)
314
322
{
315
322
  pdf_document *doc = page->doc;
316
322
  int nocache;
317
318
322
  pdf_update_page(ctx, page);
319
320
322
  nocache = !!(dev->hints & FZ_NO_CACHE);
321
322
  if (nocache)
322
0
    pdf_mark_xref(ctx, doc);
323
324
644
  fz_try(ctx)
325
644
  {
326
322
    pdf_run_page_annots_with_usage_imp(ctx, doc, page, dev, ctm, usage, cookie);
327
322
  }
328
644
  fz_always(ctx)
329
322
  {
330
322
    if (nocache)
331
0
      pdf_clear_xref_to_mark(ctx, doc);
332
322
  }
333
322
  fz_catch(ctx)
334
0
  {
335
0
    fz_rethrow(ctx);
336
0
  }
337
322
}
338
339
void pdf_run_page_annots(fz_context *ctx, pdf_page *page, fz_device *dev, fz_matrix ctm, fz_cookie *cookie)
340
322
{
341
322
  pdf_run_page_annots_with_usage(ctx, page, dev, ctm, "View", cookie);
342
322
}
343
344
void pdf_run_page_widgets_with_usage(fz_context *ctx, pdf_page *page, fz_device *dev, fz_matrix ctm, const char *usage, fz_cookie *cookie)
345
322
{
346
322
  pdf_document *doc = page->doc;
347
322
  int nocache;
348
349
322
  pdf_update_page(ctx, page);
350
351
322
  nocache = !!(dev->hints & FZ_NO_CACHE);
352
322
  if (nocache)
353
0
    pdf_mark_xref(ctx, doc);
354
355
644
  fz_try(ctx)
356
644
  {
357
322
    pdf_run_page_widgets_with_usage_imp(ctx, doc, page, dev, ctm, usage, cookie);
358
322
  }
359
644
  fz_always(ctx)
360
322
  {
361
322
    if (nocache)
362
0
      pdf_clear_xref_to_mark(ctx, doc);
363
322
  }
364
322
  fz_catch(ctx)
365
0
  {
366
0
    fz_rethrow(ctx);
367
0
  }
368
322
}
369
370
void pdf_run_page_widgets(fz_context *ctx, pdf_page *page, fz_device *dev, fz_matrix ctm, fz_cookie *cookie)
371
322
{
372
322
  pdf_run_page_widgets_with_usage(ctx, page, dev, ctm, "View", cookie);
373
322
}
374
375
void
376
pdf_run_page_with_usage(fz_context *ctx, pdf_page *page, fz_device *dev, fz_matrix ctm, const char *usage, fz_cookie *cookie)
377
0
{
378
0
  pdf_document *doc = page->doc;
379
0
  int nocache = !!(dev->hints & FZ_NO_CACHE);
380
381
0
  pdf_update_page(ctx, page);
382
383
0
  if (nocache)
384
0
    pdf_mark_xref(ctx, doc);
385
0
  fz_try(ctx)
386
0
  {
387
0
    pdf_run_page_contents_with_usage_imp(ctx, doc, page, dev, ctm, usage, cookie);
388
0
    pdf_run_page_annots_with_usage_imp(ctx, doc, page, dev, ctm, usage, cookie);
389
0
    pdf_run_page_widgets_with_usage_imp(ctx, doc, page, dev, ctm, usage, cookie);
390
0
  }
391
0
  fz_always(ctx)
392
0
  {
393
0
    if (nocache)
394
0
      pdf_clear_xref_to_mark(ctx, doc);
395
0
  }
396
0
  fz_catch(ctx)
397
0
  {
398
0
    fz_rethrow(ctx);
399
0
  }
400
0
}
401
402
void
403
pdf_run_page(fz_context *ctx, pdf_page *page, fz_device *dev, fz_matrix ctm, fz_cookie *cookie)
404
0
{
405
0
  pdf_run_page_with_usage(ctx, page, dev, ctm, "View", cookie);
406
0
}
407
408
void
409
pdf_run_glyph(fz_context *ctx, pdf_document *doc, pdf_obj *resources, fz_buffer *contents, fz_device *dev, fz_matrix ctm, void *gstate, fz_default_colorspaces *default_cs, void *fill_gstate, void *stroke_gstate)
410
0
{
411
0
  pdf_processor *proc;
412
413
0
  proc = pdf_new_run_processor(ctx, doc, dev, ctm, -1, "View", gstate, default_cs, NULL, fill_gstate, stroke_gstate);
414
0
  fz_try(ctx)
415
0
  {
416
0
    pdf_process_glyph(ctx, proc, doc, resources, contents);
417
0
    pdf_close_processor(ctx, proc);
418
0
  }
419
0
  fz_always(ctx)
420
0
    pdf_drop_processor(ctx, proc);
421
0
  fz_catch(ctx)
422
0
    fz_rethrow(ctx);
423
0
}
424
425
fz_structure
426
pdf_structure_type(fz_context *ctx, pdf_obj *role_map, pdf_obj *tag)
427
0
{
428
  /* Perform Structure mapping to go from tag to standard. */
429
0
  if (role_map)
430
0
  {
431
0
    pdf_obj *o = pdf_dict_get(ctx, role_map, tag);
432
0
    if (o)
433
0
      tag = o;
434
0
  }
435
436
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Document)))
437
0
    return FZ_STRUCTURE_DOCUMENT;
438
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Part)))
439
0
    return FZ_STRUCTURE_PART;
440
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Art)))
441
0
    return FZ_STRUCTURE_ART;
442
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Sect)))
443
0
    return FZ_STRUCTURE_SECT;
444
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Div)))
445
0
    return FZ_STRUCTURE_DIV;
446
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(BlockQuote)))
447
0
    return FZ_STRUCTURE_BLOCKQUOTE;
448
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Caption)))
449
0
    return FZ_STRUCTURE_CAPTION;
450
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(TOC)))
451
0
    return FZ_STRUCTURE_TOC;
452
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(TOCI)))
453
0
    return FZ_STRUCTURE_TOCI;
454
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Index)))
455
0
    return FZ_STRUCTURE_INDEX;
456
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(NonStruct)))
457
0
    return FZ_STRUCTURE_NONSTRUCT;
458
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Private)))
459
0
    return FZ_STRUCTURE_PRIVATE;
460
  /* Grouping elements (PDF 2.0 - Table 364) */
461
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(DocumentFragment)))
462
0
    return FZ_STRUCTURE_DOCUMENTFRAGMENT;
463
  /* Grouping elements (PDF 2.0 - Table 365) */
464
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Aside)))
465
0
    return FZ_STRUCTURE_ASIDE;
466
  /* Grouping elements (PDF 2.0 - Table 366) */
467
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Title)))
468
0
    return FZ_STRUCTURE_TITLE;
469
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(FENote)))
470
0
    return FZ_STRUCTURE_FENOTE;
471
  /* Grouping elements (PDF 2.0 - Table 367) */
472
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Sub)))
473
0
    return FZ_STRUCTURE_SUB;
474
475
  /* Paragraphlike elements (PDF 1.7 - Table 10.21) */
476
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(P)))
477
0
    return FZ_STRUCTURE_P;
478
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(H)))
479
0
    return FZ_STRUCTURE_H;
480
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(H1)))
481
0
    return FZ_STRUCTURE_H1;
482
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(H2)))
483
0
    return FZ_STRUCTURE_H2;
484
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(H3)))
485
0
    return FZ_STRUCTURE_H3;
486
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(H4)))
487
0
    return FZ_STRUCTURE_H4;
488
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(H5)))
489
0
    return FZ_STRUCTURE_H5;
490
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(H6)))
491
0
    return FZ_STRUCTURE_H6;
492
493
  /* List elements (PDF 1.7 - Table 10.23) */
494
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(L)))
495
0
    return FZ_STRUCTURE_LIST;
496
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(LI)))
497
0
    return FZ_STRUCTURE_LISTITEM;
498
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Lbl)))
499
0
    return FZ_STRUCTURE_LABEL;
500
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(LBody)))
501
0
    return FZ_STRUCTURE_LISTBODY;
502
503
  /* Table elements (PDF 1.7 - Table 10.24) */
504
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Table)))
505
0
    return FZ_STRUCTURE_TABLE;
506
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(TR)))
507
0
    return FZ_STRUCTURE_TR;
508
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(TH)))
509
0
    return FZ_STRUCTURE_TH;
510
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(TD)))
511
0
    return FZ_STRUCTURE_TD;
512
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(THead)))
513
0
    return FZ_STRUCTURE_THEAD;
514
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(TBody)))
515
0
    return FZ_STRUCTURE_TBODY;
516
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(TFoot)))
517
0
    return FZ_STRUCTURE_TFOOT;
518
519
  /* Inline elements (PDF 1.7 - Table 10.25) */
520
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Span)))
521
0
    return FZ_STRUCTURE_SPAN;
522
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Quote)))
523
0
    return FZ_STRUCTURE_QUOTE;
524
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Note)))
525
0
    return FZ_STRUCTURE_NOTE;
526
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Reference)))
527
0
    return FZ_STRUCTURE_REFERENCE;
528
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(BibEntry)))
529
0
    return FZ_STRUCTURE_BIBENTRY;
530
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Code)))
531
0
    return FZ_STRUCTURE_CODE;
532
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Link)))
533
0
    return FZ_STRUCTURE_LINK;
534
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Annot)))
535
0
    return FZ_STRUCTURE_ANNOT;
536
  /* Inline elements (PDF 2.0 - Table 368) */
537
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Em)))
538
0
    return FZ_STRUCTURE_EM;
539
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Strong)))
540
0
    return FZ_STRUCTURE_STRONG;
541
542
  /* Ruby inline element (PDF 1.7 - Table 10.26) */
543
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Ruby)))
544
0
    return FZ_STRUCTURE_RUBY;
545
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(RB)))
546
0
    return FZ_STRUCTURE_RB;
547
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(RT)))
548
0
    return FZ_STRUCTURE_RT;
549
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(RP)))
550
0
    return FZ_STRUCTURE_RP;
551
552
  /* Warichu inline element (PDF 1.7 - Table 10.26) */
553
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Warichu)))
554
0
    return FZ_STRUCTURE_WARICHU;
555
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(WT)))
556
0
    return FZ_STRUCTURE_WT;
557
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(WP)))
558
0
    return FZ_STRUCTURE_WP;
559
560
  /* Illustration elements (PDF 1.7 - Table 10.27) */
561
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Figure)))
562
0
    return FZ_STRUCTURE_FIGURE;
563
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Formula)))
564
0
    return FZ_STRUCTURE_FORMULA;
565
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Form)))
566
0
    return FZ_STRUCTURE_FORM;
567
568
  /* Artifact structure type (PDF 2.0 - Table 375) */
569
0
  if (pdf_name_eq(ctx, tag, PDF_NAME(Artifact)))
570
0
    return FZ_STRUCTURE_ARTIFACT;
571
572
0
  return FZ_STRUCTURE_INVALID;
573
0
}
574
575
/* The recursive descent of the structure tree uses an fz_try at each level.
576
 * At the risk of creating a foot cannon... "no one will need more than ~64
577
 * levels of structure tree". */
578
static void
579
run_ds(fz_context *ctx, fz_device *dev, pdf_obj *role_map, pdf_obj *obj, int idx, fz_cookie *cookie)
580
0
{
581
0
  pdf_obj *k;
582
0
  int i, n;
583
584
  /* Check the cookie for aborting */
585
0
  if (cookie)
586
0
  {
587
0
    if (cookie->abort)
588
0
      return;
589
0
    cookie->progress++;
590
0
  }
591
592
0
  if (pdf_is_number(ctx, obj))
593
0
  {
594
    /* A marked-content identifier denoting a marked content sequence. WHAT? */
595
0
    return;
596
0
  }
597
598
0
  if (pdf_mark_obj(ctx, obj))
599
0
    return;
600
601
0
  fz_try(ctx)
602
0
  {
603
0
    fz_structure standard;
604
0
    pdf_obj *tag = pdf_dict_get(ctx, obj, PDF_NAME(S));
605
0
    if (!tag)
606
0
      break;
607
608
0
    standard = pdf_structure_type(ctx, role_map, tag);
609
0
    if (standard == FZ_STRUCTURE_INVALID)
610
0
      break;
611
0
    fz_begin_structure(ctx, dev, standard, pdf_to_name(ctx, tag), idx);
612
0
    k = pdf_dict_get(ctx, obj, PDF_NAME(K));
613
0
    if (k)
614
0
    {
615
0
      n = pdf_array_len(ctx, k);
616
0
      if (n == 0)
617
0
        run_ds(ctx, dev, role_map, k, 0, cookie);
618
0
      else
619
0
      {
620
0
        for (i = 0; i < n; i++)
621
0
          run_ds(ctx, dev, role_map, pdf_array_get(ctx, k, i), i, cookie);
622
0
      }
623
0
    }
624
0
    fz_end_structure(ctx, dev);
625
0
  }
626
0
  fz_always(ctx)
627
0
    pdf_unmark_obj(ctx, obj);
628
0
  fz_catch(ctx)
629
0
    fz_rethrow(ctx);
630
0
}
631
632
void pdf_run_document_structure(fz_context *ctx, pdf_document *doc, fz_device *dev, fz_cookie *cookie)
633
0
{
634
0
  int nocache;
635
0
  int marked = 0;
636
0
  pdf_obj *rm, *k, *st = NULL;
637
638
0
  fz_var(marked);
639
0
  fz_var(st);
640
641
0
  nocache = !!(dev->hints & FZ_NO_CACHE);
642
0
  if (nocache)
643
0
    pdf_mark_xref(ctx, doc);
644
645
0
  fz_try(ctx)
646
0
  {
647
0
    st = pdf_dict_get(ctx, pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root)), PDF_NAME(StructTreeRoot));
648
0
    rm = pdf_dict_get(ctx, st, PDF_NAME(RoleMap));
649
650
0
    if (pdf_mark_obj(ctx, st))
651
0
      break;
652
0
    marked = 1;
653
654
0
    k = pdf_dict_get(ctx, st, PDF_NAME(K));
655
0
    if (k)
656
0
    {
657
0
      int n = pdf_array_len(ctx, k);
658
0
      if (n == 0)
659
0
        run_ds(ctx, dev, rm, k, 0, cookie);
660
0
      else
661
0
      {
662
0
        int i;
663
0
        for (i = 0; i < n; i++)
664
0
          run_ds(ctx, dev, rm, pdf_array_get(ctx, k, i), i, cookie);
665
0
      }
666
0
    }
667
0
  }
668
0
  fz_always(ctx)
669
0
  {
670
0
    if (marked)
671
0
      pdf_unmark_obj(ctx, st);
672
0
    if (nocache)
673
0
      pdf_clear_xref_to_mark(ctx, doc);
674
0
  }
675
0
  fz_catch(ctx)
676
0
    fz_rethrow(ctx);
677
0
}