comparison mupdf-source/source/pdf/pdf-run.c @ 2:b50eed0cc0ef upstream

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