1 #include "mupdf/fitz.h"
2 #include "mupdf/pdf.h"
3
4 #include <stdlib.h>
5 #include <string.h>
6 #include <limits.h>
7
8 int
pdf_count_pages(fz_context * ctx,pdf_document * doc)9 pdf_count_pages(fz_context *ctx, pdf_document *doc)
10 {
11 /* FIXME: We should reset linear_page_count to 0 when editing starts
12 * (or when linear loading ends) */
13 if (doc->linear_page_count != 0)
14 return doc->linear_page_count;
15 return pdf_to_int(ctx, pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/Pages/Count"));
16 }
17
pdf_count_pages_imp(fz_context * ctx,fz_document * doc,int chapter)18 int pdf_count_pages_imp(fz_context *ctx, fz_document *doc, int chapter)
19 {
20 return pdf_count_pages(ctx, (pdf_document*)doc);
21 }
22
23 static int
pdf_load_page_tree_imp(fz_context * ctx,pdf_document * doc,pdf_obj * node,int idx)24 pdf_load_page_tree_imp(fz_context *ctx, pdf_document *doc, pdf_obj *node, int idx)
25 {
26 pdf_obj *type = pdf_dict_get(ctx, node, PDF_NAME(Type));
27 if (pdf_name_eq(ctx, type, PDF_NAME(Pages)))
28 {
29 pdf_obj *kids = pdf_dict_get(ctx, node, PDF_NAME(Kids));
30 int i, n = pdf_array_len(ctx, kids);
31
32 if (pdf_mark_obj(ctx, node))
33 fz_throw(ctx, FZ_ERROR_GENERIC, "cycle in page tree");
34 fz_try(ctx)
35 for (i = 0; i < n; ++i)
36 idx = pdf_load_page_tree_imp(ctx, doc, pdf_array_get(ctx, kids, i), idx);
37 fz_always(ctx)
38 pdf_unmark_obj(ctx, node);
39 fz_catch(ctx)
40 fz_rethrow(ctx);
41 }
42 else if (pdf_name_eq(ctx, type, PDF_NAME(Page)))
43 {
44 if (idx >= doc->rev_page_count)
45 fz_throw(ctx, FZ_ERROR_GENERIC, "too many kids in page tree");
46 doc->rev_page_map[idx].page = idx;
47 doc->rev_page_map[idx].object = pdf_to_num(ctx, node);
48 ++idx;
49 }
50 else
51 {
52 fz_throw(ctx, FZ_ERROR_GENERIC, "non-page object in page tree");
53 }
54 return idx;
55 }
56
57 static int
cmp_rev_page_map(const void * va,const void * vb)58 cmp_rev_page_map(const void *va, const void *vb)
59 {
60 const pdf_rev_page_map *a = va;
61 const pdf_rev_page_map *b = vb;
62 return a->object - b->object;
63 }
64
65 void
pdf_load_page_tree(fz_context * ctx,pdf_document * doc)66 pdf_load_page_tree(fz_context *ctx, pdf_document *doc)
67 {
68 if (!doc->rev_page_map)
69 {
70 doc->rev_page_count = pdf_count_pages(ctx, doc);
71 doc->rev_page_map = Memento_label(fz_malloc_array(ctx, doc->rev_page_count, pdf_rev_page_map), "pdf_rev_page_map");
72 pdf_load_page_tree_imp(ctx, doc, pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/Pages"), 0);
73 qsort(doc->rev_page_map, doc->rev_page_count, sizeof *doc->rev_page_map, cmp_rev_page_map);
74 }
75 }
76
77 void
pdf_drop_page_tree(fz_context * ctx,pdf_document * doc)78 pdf_drop_page_tree(fz_context *ctx, pdf_document *doc)
79 {
80 fz_free(ctx, doc->rev_page_map);
81 doc->rev_page_map = NULL;
82 doc->rev_page_count = 0;
83 }
84
85 enum
86 {
87 LOCAL_STACK_SIZE = 16
88 };
89
90 static pdf_obj *
pdf_lookup_page_loc_imp(fz_context * ctx,pdf_document * doc,pdf_obj * node,int * skip,pdf_obj ** parentp,int * indexp)91 pdf_lookup_page_loc_imp(fz_context *ctx, pdf_document *doc, pdf_obj *node, int *skip, pdf_obj **parentp, int *indexp)
92 {
93 pdf_obj *kids;
94 pdf_obj *hit = NULL;
95 int i, len;
96 pdf_obj *local_stack[LOCAL_STACK_SIZE];
97 pdf_obj **stack = &local_stack[0];
98 int stack_max = LOCAL_STACK_SIZE;
99 int stack_len = 0;
100
101 fz_var(hit);
102 fz_var(stack);
103 fz_var(stack_len);
104 fz_var(stack_max);
105
106 fz_try(ctx)
107 {
108 do
109 {
110 kids = pdf_dict_get(ctx, node, PDF_NAME(Kids));
111 len = pdf_array_len(ctx, kids);
112
113 if (len == 0)
114 fz_throw(ctx, FZ_ERROR_GENERIC, "malformed page tree");
115
116 /* Every node we need to unmark goes into the stack */
117 if (stack_len == stack_max)
118 {
119 if (stack == &local_stack[0])
120 {
121 stack = fz_malloc_array(ctx, stack_max * 2, pdf_obj*);
122 memcpy(stack, &local_stack[0], stack_max * sizeof(*stack));
123 }
124 else
125 {
126 stack = fz_realloc_array(ctx, stack, stack_max * 2, pdf_obj*);
127 }
128 stack_max *= 2;
129 }
130 stack[stack_len++] = node;
131
132 if (pdf_mark_obj(ctx, node))
133 fz_throw(ctx, FZ_ERROR_GENERIC, "cycle in page tree");
134
135 for (i = 0; i < len; i++)
136 {
137 pdf_obj *kid = pdf_array_get(ctx, kids, i);
138 pdf_obj *type = pdf_dict_get(ctx, kid, PDF_NAME(Type));
139 if (type ? pdf_name_eq(ctx, type, PDF_NAME(Pages)) : pdf_dict_get(ctx, kid, PDF_NAME(Kids)) && !pdf_dict_get(ctx, kid, PDF_NAME(MediaBox)))
140 {
141 int count = pdf_dict_get_int(ctx, kid, PDF_NAME(Count));
142 if (*skip < count)
143 {
144 node = kid;
145 break;
146 }
147 else
148 {
149 *skip -= count;
150 }
151 }
152 else
153 {
154 if (type ? !pdf_name_eq(ctx, type, PDF_NAME(Page)) : !pdf_dict_get(ctx, kid, PDF_NAME(MediaBox)))
155 fz_warn(ctx, "non-page object in page tree (%s)", pdf_to_name(ctx, type));
156 if (*skip == 0)
157 {
158 if (parentp) *parentp = node;
159 if (indexp) *indexp = i;
160 hit = kid;
161 break;
162 }
163 else
164 {
165 (*skip)--;
166 }
167 }
168 }
169 }
170 /* If i < len && hit != NULL the desired page was found in the
171 Kids array, done. If i < len && hit == NULL the found page tree
172 node contains a Kids array that contains the desired page, loop
173 back to top to extract it. When i == len the Kids array has been
174 exhausted without finding the desired page, give up.
175 */
176 while (hit == NULL && i < len);
177 }
178 fz_always(ctx)
179 {
180 for (i = stack_len; i > 0; i--)
181 pdf_unmark_obj(ctx, stack[i-1]);
182 if (stack != &local_stack[0])
183 fz_free(ctx, stack);
184 }
185 fz_catch(ctx)
186 {
187 fz_rethrow(ctx);
188 }
189
190 return hit;
191 }
192
193 pdf_obj *
pdf_lookup_page_loc(fz_context * ctx,pdf_document * doc,int needle,pdf_obj ** parentp,int * indexp)194 pdf_lookup_page_loc(fz_context *ctx, pdf_document *doc, int needle, pdf_obj **parentp, int *indexp)
195 {
196 pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root));
197 pdf_obj *node = pdf_dict_get(ctx, root, PDF_NAME(Pages));
198 int skip = needle;
199 pdf_obj *hit;
200
201 if (!node)
202 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find page tree");
203
204 hit = pdf_lookup_page_loc_imp(ctx, doc, node, &skip, parentp, indexp);
205 if (!hit)
206 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find page %d in page tree", needle+1);
207 return hit;
208 }
209
210 pdf_obj *
pdf_lookup_page_obj(fz_context * ctx,pdf_document * doc,int needle)211 pdf_lookup_page_obj(fz_context *ctx, pdf_document *doc, int needle)
212 {
213 return pdf_lookup_page_loc(ctx, doc, needle, NULL, NULL);
214 }
215
216 static int
pdf_count_pages_before_kid(fz_context * ctx,pdf_document * doc,pdf_obj * parent,int kid_num)217 pdf_count_pages_before_kid(fz_context *ctx, pdf_document *doc, pdf_obj *parent, int kid_num)
218 {
219 pdf_obj *kids = pdf_dict_get(ctx, parent, PDF_NAME(Kids));
220 int i, total = 0, len = pdf_array_len(ctx, kids);
221 for (i = 0; i < len; i++)
222 {
223 pdf_obj *kid = pdf_array_get(ctx, kids, i);
224 if (pdf_to_num(ctx, kid) == kid_num)
225 return total;
226 if (pdf_name_eq(ctx, pdf_dict_get(ctx, kid, PDF_NAME(Type)), PDF_NAME(Pages)))
227 {
228 pdf_obj *count = pdf_dict_get(ctx, kid, PDF_NAME(Count));
229 int n = pdf_to_int(ctx, count);
230 if (!pdf_is_int(ctx, count) || n < 0)
231 fz_throw(ctx, FZ_ERROR_GENERIC, "illegal or missing count in pages tree");
232 total += n;
233 }
234 else
235 total++;
236 }
237 fz_throw(ctx, FZ_ERROR_GENERIC, "kid not found in parent's kids array");
238 }
239
240 static int
pdf_lookup_page_number_slow(fz_context * ctx,pdf_document * doc,pdf_obj * node)241 pdf_lookup_page_number_slow(fz_context *ctx, pdf_document *doc, pdf_obj *node)
242 {
243 int needle = pdf_to_num(ctx, node);
244 int total = 0;
245 pdf_obj *parent, *parent2;
246
247 if (!pdf_name_eq(ctx, pdf_dict_get(ctx, node, PDF_NAME(Type)), PDF_NAME(Page)))
248 fz_throw(ctx, FZ_ERROR_GENERIC, "invalid page object");
249
250 parent2 = parent = pdf_dict_get(ctx, node, PDF_NAME(Parent));
251 fz_var(parent);
252 fz_try(ctx)
253 {
254 while (pdf_is_dict(ctx, parent))
255 {
256 if (pdf_mark_obj(ctx, parent))
257 fz_throw(ctx, FZ_ERROR_GENERIC, "cycle in page tree (parents)");
258 total += pdf_count_pages_before_kid(ctx, doc, parent, needle);
259 needle = pdf_to_num(ctx, parent);
260 parent = pdf_dict_get(ctx, parent, PDF_NAME(Parent));
261 }
262 }
263 fz_always(ctx)
264 {
265 /* Run back and unmark */
266 while (parent2)
267 {
268 pdf_unmark_obj(ctx, parent2);
269 if (parent2 == parent)
270 break;
271 parent2 = pdf_dict_get(ctx, parent2, PDF_NAME(Parent));
272 }
273 }
274 fz_catch(ctx)
275 {
276 fz_rethrow(ctx);
277 }
278
279 return total;
280 }
281
282 static int
pdf_lookup_page_number_fast(fz_context * ctx,pdf_document * doc,int needle)283 pdf_lookup_page_number_fast(fz_context *ctx, pdf_document *doc, int needle)
284 {
285 int l = 0;
286 int r = doc->rev_page_count - 1;
287 while (l <= r)
288 {
289 int m = (l + r) >> 1;
290 int c = needle - doc->rev_page_map[m].object;
291 if (c < 0)
292 r = m - 1;
293 else if (c > 0)
294 l = m + 1;
295 else
296 return doc->rev_page_map[m].page;
297 }
298 return -1;
299 }
300
301 int
pdf_lookup_page_number(fz_context * ctx,pdf_document * doc,pdf_obj * page)302 pdf_lookup_page_number(fz_context *ctx, pdf_document *doc, pdf_obj *page)
303 {
304 if (doc->rev_page_map)
305 return pdf_lookup_page_number_fast(ctx, doc, pdf_to_num(ctx, page));
306 else
307 return pdf_lookup_page_number_slow(ctx, doc, page);
308 }
309
310 int
pdf_lookup_anchor(fz_context * ctx,pdf_document * doc,const char * name,float * xp,float * yp)311 pdf_lookup_anchor(fz_context *ctx, pdf_document *doc, const char *name, float *xp, float *yp)
312 {
313 pdf_obj *needle, *dest = NULL;
314 char *uri;
315
316 if (xp) *xp = 0;
317 if (yp) *yp = 0;
318
319 needle = pdf_new_string(ctx, name, strlen(name));
320 fz_try(ctx)
321 dest = pdf_lookup_dest(ctx, doc, needle);
322 fz_always(ctx)
323 pdf_drop_obj(ctx, needle);
324 fz_catch(ctx)
325 fz_rethrow(ctx);
326
327 if (dest)
328 {
329 uri = pdf_parse_link_dest(ctx, doc, dest);
330 return pdf_resolve_link(ctx, doc, uri, xp, yp);
331 }
332
333 if (!strncmp(name, "page=", 5))
334 return fz_atoi(name + 5) - 1;
335
336 return fz_atoi(name) - 1;
337 }
338
339 static void
pdf_flatten_inheritable_page_item(fz_context * ctx,pdf_obj * page,pdf_obj * key)340 pdf_flatten_inheritable_page_item(fz_context *ctx, pdf_obj *page, pdf_obj *key)
341 {
342 pdf_obj *val = pdf_dict_get_inheritable(ctx, page, key);
343 if (val)
344 pdf_dict_put(ctx, page, key, val);
345 }
346
347 void
pdf_flatten_inheritable_page_items(fz_context * ctx,pdf_obj * page)348 pdf_flatten_inheritable_page_items(fz_context *ctx, pdf_obj *page)
349 {
350 pdf_flatten_inheritable_page_item(ctx, page, PDF_NAME(MediaBox));
351 pdf_flatten_inheritable_page_item(ctx, page, PDF_NAME(CropBox));
352 pdf_flatten_inheritable_page_item(ctx, page, PDF_NAME(Rotate));
353 pdf_flatten_inheritable_page_item(ctx, page, PDF_NAME(Resources));
354 }
355
356 /* We need to know whether to install a page-level transparency group */
357
358 /*
359 * Object memo flags - allows us to secretly remember "a memo" (a bool) in an
360 * object, and to read back whether there was a memo, and if so, what it was.
361 */
362 enum
363 {
364 PDF_FLAGS_MEMO_BM = 0,
365 PDF_FLAGS_MEMO_OP = 1
366 };
367
368 static int pdf_resources_use_blending(fz_context *ctx, pdf_obj *rdb);
369
370 static int
pdf_extgstate_uses_blending(fz_context * ctx,pdf_obj * dict)371 pdf_extgstate_uses_blending(fz_context *ctx, pdf_obj *dict)
372 {
373 pdf_obj *obj = pdf_dict_get(ctx, dict, PDF_NAME(BM));
374 if (obj && !pdf_name_eq(ctx, obj, PDF_NAME(Normal)))
375 return 1;
376 return 0;
377 }
378
379 static int
pdf_pattern_uses_blending(fz_context * ctx,pdf_obj * dict)380 pdf_pattern_uses_blending(fz_context *ctx, pdf_obj *dict)
381 {
382 pdf_obj *obj;
383 obj = pdf_dict_get(ctx, dict, PDF_NAME(Resources));
384 if (pdf_resources_use_blending(ctx, obj))
385 return 1;
386 obj = pdf_dict_get(ctx, dict, PDF_NAME(ExtGState));
387 return pdf_extgstate_uses_blending(ctx, obj);
388 }
389
390 static int
pdf_xobject_uses_blending(fz_context * ctx,pdf_obj * dict)391 pdf_xobject_uses_blending(fz_context *ctx, pdf_obj *dict)
392 {
393 pdf_obj *obj = pdf_dict_get(ctx, dict, PDF_NAME(Resources));
394 if (pdf_name_eq(ctx, pdf_dict_getp(ctx, dict, "Group/S"), PDF_NAME(Transparency)))
395 return 1;
396 return pdf_resources_use_blending(ctx, obj);
397 }
398
399 static int
pdf_resources_use_blending(fz_context * ctx,pdf_obj * rdb)400 pdf_resources_use_blending(fz_context *ctx, pdf_obj *rdb)
401 {
402 pdf_obj *obj;
403 int i, n, useBM = 0;
404
405 if (!rdb)
406 return 0;
407
408 /* Have we been here before and remembered an answer? */
409 if (pdf_obj_memo(ctx, rdb, PDF_FLAGS_MEMO_BM, &useBM))
410 return useBM;
411
412 /* stop on cyclic resource dependencies */
413 if (pdf_mark_obj(ctx, rdb))
414 return 0;
415
416 fz_try(ctx)
417 {
418 obj = pdf_dict_get(ctx, rdb, PDF_NAME(ExtGState));
419 n = pdf_dict_len(ctx, obj);
420 for (i = 0; i < n; i++)
421 if (pdf_extgstate_uses_blending(ctx, pdf_dict_get_val(ctx, obj, i)))
422 goto found;
423
424 obj = pdf_dict_get(ctx, rdb, PDF_NAME(Pattern));
425 n = pdf_dict_len(ctx, obj);
426 for (i = 0; i < n; i++)
427 if (pdf_pattern_uses_blending(ctx, pdf_dict_get_val(ctx, obj, i)))
428 goto found;
429
430 obj = pdf_dict_get(ctx, rdb, PDF_NAME(XObject));
431 n = pdf_dict_len(ctx, obj);
432 for (i = 0; i < n; i++)
433 if (pdf_xobject_uses_blending(ctx, pdf_dict_get_val(ctx, obj, i)))
434 goto found;
435 if (0)
436 {
437 found:
438 useBM = 1;
439 }
440 }
441 fz_always(ctx)
442 {
443 pdf_unmark_obj(ctx, rdb);
444 }
445 fz_catch(ctx)
446 {
447 fz_rethrow(ctx);
448 }
449
450 pdf_set_obj_memo(ctx, rdb, PDF_FLAGS_MEMO_BM, useBM);
451 return useBM;
452 }
453
454 static int pdf_resources_use_overprint(fz_context *ctx, pdf_obj *rdb);
455
456 static int
pdf_extgstate_uses_overprint(fz_context * ctx,pdf_obj * dict)457 pdf_extgstate_uses_overprint(fz_context *ctx, pdf_obj *dict)
458 {
459 pdf_obj *obj = pdf_dict_get(ctx, dict, PDF_NAME(OP));
460 if (obj && pdf_to_bool(ctx, obj))
461 return 1;
462 return 0;
463 }
464
465 static int
pdf_pattern_uses_overprint(fz_context * ctx,pdf_obj * dict)466 pdf_pattern_uses_overprint(fz_context *ctx, pdf_obj *dict)
467 {
468 pdf_obj *obj;
469 obj = pdf_dict_get(ctx, dict, PDF_NAME(Resources));
470 if (pdf_resources_use_overprint(ctx, obj))
471 return 1;
472 obj = pdf_dict_get(ctx, dict, PDF_NAME(ExtGState));
473 return pdf_extgstate_uses_overprint(ctx, obj);
474 }
475
476 static int
pdf_xobject_uses_overprint(fz_context * ctx,pdf_obj * dict)477 pdf_xobject_uses_overprint(fz_context *ctx, pdf_obj *dict)
478 {
479 pdf_obj *obj = pdf_dict_get(ctx, dict, PDF_NAME(Resources));
480 return pdf_resources_use_overprint(ctx, obj);
481 }
482
483 static int
pdf_resources_use_overprint(fz_context * ctx,pdf_obj * rdb)484 pdf_resources_use_overprint(fz_context *ctx, pdf_obj *rdb)
485 {
486 pdf_obj *obj;
487 int i, n, useOP = 0;
488
489 if (!rdb)
490 return 0;
491
492 /* Have we been here before and remembered an answer? */
493 if (pdf_obj_memo(ctx, rdb, PDF_FLAGS_MEMO_OP, &useOP))
494 return useOP;
495
496 /* stop on cyclic resource dependencies */
497 if (pdf_mark_obj(ctx, rdb))
498 return 0;
499
500 fz_try(ctx)
501 {
502 obj = pdf_dict_get(ctx, rdb, PDF_NAME(ExtGState));
503 n = pdf_dict_len(ctx, obj);
504 for (i = 0; i < n; i++)
505 if (pdf_extgstate_uses_overprint(ctx, pdf_dict_get_val(ctx, obj, i)))
506 goto found;
507
508 obj = pdf_dict_get(ctx, rdb, PDF_NAME(Pattern));
509 n = pdf_dict_len(ctx, obj);
510 for (i = 0; i < n; i++)
511 if (pdf_pattern_uses_overprint(ctx, pdf_dict_get_val(ctx, obj, i)))
512 goto found;
513
514 obj = pdf_dict_get(ctx, rdb, PDF_NAME(XObject));
515 n = pdf_dict_len(ctx, obj);
516 for (i = 0; i < n; i++)
517 if (pdf_xobject_uses_overprint(ctx, pdf_dict_get_val(ctx, obj, i)))
518 goto found;
519 if (0)
520 {
521 found:
522 useOP = 1;
523 }
524 }
525 fz_always(ctx)
526 {
527 pdf_unmark_obj(ctx, rdb);
528 }
529 fz_catch(ctx)
530 {
531 fz_rethrow(ctx);
532 }
533
534 pdf_set_obj_memo(ctx, rdb, PDF_FLAGS_MEMO_OP, useOP);
535 return useOP;
536 }
537
538 fz_transition *
pdf_page_presentation(fz_context * ctx,pdf_page * page,fz_transition * transition,float * duration)539 pdf_page_presentation(fz_context *ctx, pdf_page *page, fz_transition *transition, float *duration)
540 {
541 pdf_obj *obj, *transdict;
542
543 *duration = pdf_dict_get_real(ctx, page->obj, PDF_NAME(Dur));
544
545 transdict = pdf_dict_get(ctx, page->obj, PDF_NAME(Trans));
546 if (!transdict)
547 return NULL;
548
549 obj = pdf_dict_get(ctx, transdict, PDF_NAME(D));
550
551 transition->duration = (obj ? pdf_to_real(ctx, obj) : 1);
552
553 transition->vertical = !pdf_name_eq(ctx, pdf_dict_get(ctx, transdict, PDF_NAME(Dm)), PDF_NAME(H));
554 transition->outwards = !pdf_name_eq(ctx, pdf_dict_get(ctx, transdict, PDF_NAME(M)), PDF_NAME(I));
555 /* FIXME: If 'Di' is None, it should be handled differently, but
556 * this only affects Fly, and we don't implement that currently. */
557 transition->direction = (pdf_dict_get_int(ctx, transdict, PDF_NAME(Di)));
558 /* FIXME: Read SS for Fly when we implement it */
559 /* FIXME: Read B for Fly when we implement it */
560
561 obj = pdf_dict_get(ctx, transdict, PDF_NAME(S));
562 if (pdf_name_eq(ctx, obj, PDF_NAME(Split)))
563 transition->type = FZ_TRANSITION_SPLIT;
564 else if (pdf_name_eq(ctx, obj, PDF_NAME(Blinds)))
565 transition->type = FZ_TRANSITION_BLINDS;
566 else if (pdf_name_eq(ctx, obj, PDF_NAME(Box)))
567 transition->type = FZ_TRANSITION_BOX;
568 else if (pdf_name_eq(ctx, obj, PDF_NAME(Wipe)))
569 transition->type = FZ_TRANSITION_WIPE;
570 else if (pdf_name_eq(ctx, obj, PDF_NAME(Dissolve)))
571 transition->type = FZ_TRANSITION_DISSOLVE;
572 else if (pdf_name_eq(ctx, obj, PDF_NAME(Glitter)))
573 transition->type = FZ_TRANSITION_GLITTER;
574 else if (pdf_name_eq(ctx, obj, PDF_NAME(Fly)))
575 transition->type = FZ_TRANSITION_FLY;
576 else if (pdf_name_eq(ctx, obj, PDF_NAME(Push)))
577 transition->type = FZ_TRANSITION_PUSH;
578 else if (pdf_name_eq(ctx, obj, PDF_NAME(Cover)))
579 transition->type = FZ_TRANSITION_COVER;
580 else if (pdf_name_eq(ctx, obj, PDF_NAME(Uncover)))
581 transition->type = FZ_TRANSITION_UNCOVER;
582 else if (pdf_name_eq(ctx, obj, PDF_NAME(Fade)))
583 transition->type = FZ_TRANSITION_FADE;
584 else
585 transition->type = FZ_TRANSITION_NONE;
586
587 return transition;
588 }
589
590 fz_rect
pdf_bound_page(fz_context * ctx,pdf_page * page)591 pdf_bound_page(fz_context *ctx, pdf_page *page)
592 {
593 fz_matrix page_ctm;
594 fz_rect mediabox;
595 pdf_page_transform(ctx, page, &mediabox, &page_ctm);
596 return fz_transform_rect(mediabox, page_ctm);
597 }
598
599 fz_link *
pdf_load_links(fz_context * ctx,pdf_page * page)600 pdf_load_links(fz_context *ctx, pdf_page *page)
601 {
602 return fz_keep_link(ctx, page->links);
603 }
604
605 pdf_obj *
pdf_page_resources(fz_context * ctx,pdf_page * page)606 pdf_page_resources(fz_context *ctx, pdf_page *page)
607 {
608 return pdf_dict_get_inheritable(ctx, page->obj, PDF_NAME(Resources));
609 }
610
611 pdf_obj *
pdf_page_contents(fz_context * ctx,pdf_page * page)612 pdf_page_contents(fz_context *ctx, pdf_page *page)
613 {
614 return pdf_dict_get(ctx, page->obj, PDF_NAME(Contents));
615 }
616
617 pdf_obj *
pdf_page_group(fz_context * ctx,pdf_page * page)618 pdf_page_group(fz_context *ctx, pdf_page *page)
619 {
620 return pdf_dict_get(ctx, page->obj, PDF_NAME(Group));
621 }
622
623 void
pdf_page_obj_transform(fz_context * ctx,pdf_obj * pageobj,fz_rect * page_mediabox,fz_matrix * page_ctm)624 pdf_page_obj_transform(fz_context *ctx, pdf_obj *pageobj, fz_rect *page_mediabox, fz_matrix *page_ctm)
625 {
626 pdf_obj *obj;
627 fz_rect mediabox, cropbox, realbox, pagebox;
628 float userunit = 1;
629 int rotate;
630
631 if (!page_mediabox)
632 page_mediabox = &pagebox;
633
634 obj = pdf_dict_get(ctx, pageobj, PDF_NAME(UserUnit));
635 if (pdf_is_real(ctx, obj))
636 userunit = pdf_to_real(ctx, obj);
637
638 mediabox = pdf_to_rect(ctx, pdf_dict_get_inheritable(ctx, pageobj, PDF_NAME(MediaBox)));
639 if (fz_is_empty_rect(mediabox))
640 {
641 mediabox.x0 = 0;
642 mediabox.y0 = 0;
643 mediabox.x1 = 612;
644 mediabox.y1 = 792;
645 }
646
647 cropbox = pdf_to_rect(ctx, pdf_dict_get_inheritable(ctx, pageobj, PDF_NAME(CropBox)));
648 if (!fz_is_empty_rect(cropbox))
649 mediabox = fz_intersect_rect(mediabox, cropbox);
650
651 page_mediabox->x0 = fz_min(mediabox.x0, mediabox.x1);
652 page_mediabox->y0 = fz_min(mediabox.y0, mediabox.y1);
653 page_mediabox->x1 = fz_max(mediabox.x0, mediabox.x1);
654 page_mediabox->y1 = fz_max(mediabox.y0, mediabox.y1);
655
656 if (page_mediabox->x1 - page_mediabox->x0 < 1 || page_mediabox->y1 - page_mediabox->y0 < 1)
657 *page_mediabox = fz_unit_rect;
658
659 rotate = pdf_to_int(ctx, pdf_dict_get_inheritable(ctx, pageobj, PDF_NAME(Rotate)));
660
661 /* Snap page rotation to 0, 90, 180 or 270 */
662 if (rotate < 0)
663 rotate = 360 - ((-rotate) % 360);
664 if (rotate >= 360)
665 rotate = rotate % 360;
666 rotate = 90*((rotate + 45)/90);
667 if (rotate >= 360)
668 rotate = 0;
669
670 /* Compute transform from fitz' page space (upper left page origin, y descending, 72 dpi)
671 * to PDF user space (arbitrary page origin, y ascending, UserUnit dpi). */
672
673 /* Make left-handed and scale by UserUnit */
674 *page_ctm = fz_scale(userunit, -userunit);
675
676 /* Rotate */
677 *page_ctm = fz_pre_rotate(*page_ctm, -rotate);
678
679 /* Translate page origin to 0,0 */
680 realbox = fz_transform_rect(*page_mediabox, *page_ctm);
681 *page_ctm = fz_concat(*page_ctm, fz_translate(-realbox.x0, -realbox.y0));
682 }
683
684 void
pdf_page_transform(fz_context * ctx,pdf_page * page,fz_rect * page_mediabox,fz_matrix * page_ctm)685 pdf_page_transform(fz_context *ctx, pdf_page *page, fz_rect *page_mediabox, fz_matrix *page_ctm)
686 {
687 pdf_page_obj_transform(ctx, page->obj, page_mediabox, page_ctm);
688 }
689
690 static void
find_seps(fz_context * ctx,fz_separations ** seps,pdf_obj * obj,pdf_obj * clearme)691 find_seps(fz_context *ctx, fz_separations **seps, pdf_obj *obj, pdf_obj *clearme)
692 {
693 int i, n;
694 pdf_obj *nameobj, *cols;
695
696 nameobj = pdf_array_get(ctx, obj, 0);
697 if (pdf_name_eq(ctx, nameobj, PDF_NAME(Separation)))
698 {
699 fz_colorspace *cs;
700 const char *name = pdf_to_name(ctx, pdf_array_get(ctx, obj, 1));
701
702 /* Skip 'special' colorants. */
703 if (!strcmp(name, "Black") ||
704 !strcmp(name, "Cyan") ||
705 !strcmp(name, "Magenta") ||
706 !strcmp(name, "Yellow") ||
707 !strcmp(name, "All") ||
708 !strcmp(name, "None"))
709 return;
710
711 n = fz_count_separations(ctx, *seps);
712 for (i = 0; i < n; i++)
713 {
714 if (!strcmp(name, fz_separation_name(ctx, *seps, i)))
715 return; /* Got that one already */
716 }
717
718 fz_try(ctx)
719 cs = pdf_load_colorspace(ctx, obj);
720 fz_catch(ctx)
721 {
722 fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
723 return; /* ignore broken colorspace */
724 }
725 fz_try(ctx)
726 {
727 if (!*seps)
728 *seps = fz_new_separations(ctx, 0);
729 fz_add_separation(ctx, *seps, name, cs, 0);
730 }
731 fz_always(ctx)
732 fz_drop_colorspace(ctx, cs);
733 fz_catch(ctx)
734 fz_rethrow(ctx);
735 }
736 else if (pdf_name_eq(ctx, nameobj, PDF_NAME(Indexed)))
737 {
738 if (pdf_is_indirect(ctx, obj))
739 {
740 if (pdf_mark_obj(ctx, obj))
741 return; /* already been here */
742 /* remember to clear this colorspace dictionary at the end */
743 pdf_array_push(ctx, clearme, obj);
744 }
745
746 find_seps(ctx, seps, pdf_array_get(ctx, obj, 1), clearme);
747 }
748 else if (pdf_name_eq(ctx, nameobj, PDF_NAME(DeviceN)))
749 {
750 if (pdf_is_indirect(ctx, obj))
751 {
752 if (pdf_mark_obj(ctx, obj))
753 return; /* already been here */
754 /* remember to clear this colorspace dictionary at the end */
755 pdf_array_push(ctx, clearme, obj);
756 }
757
758 /* If the separation colorants exists for this DeviceN color space
759 * add those prior to our search for DeviceN color */
760 cols = pdf_dict_get(ctx, pdf_array_get(ctx, obj, 4), PDF_NAME(Colorants));
761 n = pdf_dict_len(ctx, cols);
762 for (i = 0; i < n; i++)
763 find_seps(ctx, seps, pdf_dict_get_val(ctx, cols, i), clearme);
764 }
765 }
766
767 static void
find_devn(fz_context * ctx,fz_separations ** seps,pdf_obj * obj,pdf_obj * clearme)768 find_devn(fz_context *ctx, fz_separations **seps, pdf_obj *obj, pdf_obj *clearme)
769 {
770 int i, j, n, m;
771 pdf_obj *arr;
772 pdf_obj *nameobj = pdf_array_get(ctx, obj, 0);
773
774 if (!pdf_name_eq(ctx, nameobj, PDF_NAME(DeviceN)))
775 return;
776
777 arr = pdf_array_get(ctx, obj, 1);
778 m = pdf_array_len(ctx, arr);
779 for (j = 0; j < m; j++)
780 {
781 fz_colorspace *cs;
782 const char *name = pdf_to_name(ctx, pdf_array_get(ctx, arr, j));
783
784 /* Skip 'special' colorants. */
785 if (!strcmp(name, "Black") ||
786 !strcmp(name, "Cyan") ||
787 !strcmp(name, "Magenta") ||
788 !strcmp(name, "Yellow") ||
789 !strcmp(name, "All") ||
790 !strcmp(name, "None"))
791 continue;
792
793 n = fz_count_separations(ctx, *seps);
794 for (i = 0; i < n; i++)
795 {
796 if (!strcmp(name, fz_separation_name(ctx, *seps, i)))
797 break; /* Got that one already */
798 }
799
800 if (i == n)
801 {
802 fz_try(ctx)
803 cs = pdf_load_colorspace(ctx, obj);
804 fz_catch(ctx)
805 {
806 fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
807 continue; /* ignore broken colorspace */
808 }
809 fz_try(ctx)
810 {
811 if (!*seps)
812 *seps = fz_new_separations(ctx, 0);
813 fz_add_separation(ctx, *seps, name, cs, j);
814 }
815 fz_always(ctx)
816 fz_drop_colorspace(ctx, cs);
817 fz_catch(ctx)
818 fz_rethrow(ctx);
819 }
820 }
821 }
822
823 typedef void (res_finder_fn)(fz_context *ctx, fz_separations **seps, pdf_obj *obj, pdf_obj *clearme);
824
825 static void
scan_page_seps(fz_context * ctx,pdf_obj * res,fz_separations ** seps,res_finder_fn * fn,pdf_obj * clearme)826 scan_page_seps(fz_context *ctx, pdf_obj *res, fz_separations **seps, res_finder_fn *fn, pdf_obj *clearme)
827 {
828 pdf_obj *dict;
829 pdf_obj *obj;
830 int i, n;
831
832 if (pdf_mark_obj(ctx, res))
833 return; /* already been here */
834
835 /* remember to clear this resource dictionary at the end */
836 pdf_array_push(ctx, clearme, res);
837
838 dict = pdf_dict_get(ctx, res, PDF_NAME(ColorSpace));
839 n = pdf_dict_len(ctx, dict);
840 for (i = 0; i < n; i++)
841 {
842 obj = pdf_dict_get_val(ctx, dict, i);
843 fn(ctx, seps, obj, clearme);
844 }
845
846 dict = pdf_dict_get(ctx, res, PDF_NAME(Shading));
847 n = pdf_dict_len(ctx, dict);
848 for (i = 0; i < n; i++)
849 {
850 obj = pdf_dict_get_val(ctx, dict, i);
851 fn(ctx, seps, pdf_dict_get(ctx, obj, PDF_NAME(ColorSpace)), clearme);
852 }
853
854 dict = pdf_dict_get(ctx, res, PDF_NAME(XObject));
855 n = pdf_dict_len(ctx, dict);
856 for (i = 0; i < n; i++)
857 {
858 obj = pdf_dict_get_val(ctx, dict, i);
859 fn(ctx, seps, pdf_dict_get(ctx, obj, PDF_NAME(ColorSpace)), clearme);
860 /* Recurse on XObject forms. */
861 scan_page_seps(ctx, pdf_dict_get(ctx, obj, PDF_NAME(Resources)), seps, fn, clearme);
862 }
863 }
864
865 fz_separations *
pdf_page_separations(fz_context * ctx,pdf_page * page)866 pdf_page_separations(fz_context *ctx, pdf_page *page)
867 {
868 pdf_obj *res = pdf_page_resources(ctx, page);
869 pdf_obj *clearme = NULL;
870 fz_separations *seps = NULL;
871
872 clearme = pdf_new_array(ctx, page->doc, 100);
873 fz_try(ctx)
874 {
875 /* Run through and look for separations first. This is
876 * because separations are simplest to deal with, and
877 * because DeviceN may be implemented on top of separations.
878 */
879 scan_page_seps(ctx, res, &seps, find_seps, clearme);
880 }
881 fz_always(ctx)
882 {
883 int i, n = pdf_array_len(ctx, clearme);
884 for (i = 0; i < n; ++i)
885 pdf_unmark_obj(ctx, pdf_array_get(ctx, clearme, i));
886 pdf_drop_obj(ctx, clearme);
887 }
888 fz_catch(ctx)
889 {
890 fz_drop_separations(ctx, seps);
891 fz_rethrow(ctx);
892 }
893
894 clearme = pdf_new_array(ctx, page->doc, 100);
895 fz_try(ctx)
896 {
897 /* Now run through again, and look for DeviceNs. These may
898 * have spot colors in that aren't defined in terms of
899 * separations. */
900 scan_page_seps(ctx, res, &seps, find_devn, clearme);
901 }
902 fz_always(ctx)
903 {
904 int i, n = pdf_array_len(ctx, clearme);
905 for (i = 0; i < n; ++i)
906 pdf_unmark_obj(ctx, pdf_array_get(ctx, clearme, i));
907 pdf_drop_obj(ctx, clearme);
908 }
909 fz_catch(ctx)
910 {
911 fz_drop_separations(ctx, seps);
912 fz_rethrow(ctx);
913 }
914
915 return seps;
916 }
917
918 int
pdf_page_uses_overprint(fz_context * ctx,pdf_page * page)919 pdf_page_uses_overprint(fz_context *ctx, pdf_page *page)
920 {
921 return page ? page->overprint : 0;
922 }
923
924 static void
pdf_drop_page_imp(fz_context * ctx,pdf_page * page)925 pdf_drop_page_imp(fz_context *ctx, pdf_page *page)
926 {
927 fz_drop_link(ctx, page->links);
928 pdf_drop_annots(ctx, page->annots);
929 pdf_drop_widgets(ctx, page->widgets);
930
931 pdf_drop_obj(ctx, page->obj);
932
933 fz_drop_document(ctx, &page->doc->super);
934 }
935
936 static pdf_page *
pdf_new_page(fz_context * ctx,pdf_document * doc)937 pdf_new_page(fz_context *ctx, pdf_document *doc)
938 {
939 pdf_page *page = fz_new_derived_page(ctx, pdf_page);
940
941 page->doc = (pdf_document*) fz_keep_document(ctx, &doc->super);
942
943 page->super.drop_page = (fz_page_drop_page_fn*)pdf_drop_page_imp;
944 page->super.load_links = (fz_page_load_links_fn*)pdf_load_links;
945 page->super.bound_page = (fz_page_bound_page_fn*)pdf_bound_page;
946 page->super.run_page_contents = (fz_page_run_page_fn*)pdf_run_page_contents;
947 page->super.run_page_annots = (fz_page_run_page_fn*)pdf_run_page_annots;
948 page->super.run_page_widgets = (fz_page_run_page_fn*)pdf_run_page_widgets;
949 page->super.page_presentation = (fz_page_page_presentation_fn*)pdf_page_presentation;
950 page->super.separations = (fz_page_separations_fn *)pdf_page_separations;
951 page->super.overprint = (fz_page_uses_overprint_fn *)pdf_page_uses_overprint;
952
953 page->obj = NULL;
954
955 page->transparency = 0;
956 page->links = NULL;
957 page->annots = NULL;
958 page->annot_tailp = &page->annots;
959 page->widgets = NULL;
960 page->widget_tailp = &page->widgets;
961
962 return page;
963 }
964
965 static void
pdf_load_default_colorspaces_imp(fz_context * ctx,fz_default_colorspaces * default_cs,pdf_obj * obj)966 pdf_load_default_colorspaces_imp(fz_context *ctx, fz_default_colorspaces *default_cs, pdf_obj *obj)
967 {
968 pdf_obj *cs_obj;
969
970 /* The spec says to ignore any colors we can't understand */
971
972 cs_obj = pdf_dict_get(ctx, obj, PDF_NAME(DefaultGray));
973 if (cs_obj)
974 {
975 fz_try(ctx)
976 {
977 fz_colorspace *cs = pdf_load_colorspace(ctx, cs_obj);
978 fz_set_default_gray(ctx, default_cs, cs);
979 fz_drop_colorspace(ctx, cs);
980 }
981 fz_catch(ctx)
982 fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
983 }
984
985 cs_obj = pdf_dict_get(ctx, obj, PDF_NAME(DefaultRGB));
986 if (cs_obj)
987 {
988 fz_try(ctx)
989 {
990 fz_colorspace *cs = pdf_load_colorspace(ctx, cs_obj);
991 fz_set_default_rgb(ctx, default_cs, cs);
992 fz_drop_colorspace(ctx, cs);
993 }
994 fz_catch(ctx)
995 fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
996 }
997
998 cs_obj = pdf_dict_get(ctx, obj, PDF_NAME(DefaultCMYK));
999 if (cs_obj)
1000 {
1001 fz_try(ctx)
1002 {
1003 fz_colorspace *cs = pdf_load_colorspace(ctx, cs_obj);
1004 fz_set_default_cmyk(ctx, default_cs, cs);
1005 fz_drop_colorspace(ctx, cs);
1006 }
1007 fz_catch(ctx)
1008 fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
1009 }
1010 }
1011
1012 fz_default_colorspaces *
pdf_load_default_colorspaces(fz_context * ctx,pdf_document * doc,pdf_page * page)1013 pdf_load_default_colorspaces(fz_context *ctx, pdf_document *doc, pdf_page *page)
1014 {
1015 pdf_obj *res;
1016 pdf_obj *obj;
1017 fz_default_colorspaces *default_cs;
1018 fz_colorspace *oi;
1019
1020 default_cs = fz_new_default_colorspaces(ctx);
1021
1022 fz_try(ctx)
1023 {
1024 res = pdf_page_resources(ctx, page);
1025 obj = pdf_dict_get(ctx, res, PDF_NAME(ColorSpace));
1026 if (obj)
1027 pdf_load_default_colorspaces_imp(ctx, default_cs, obj);
1028
1029 oi = pdf_document_output_intent(ctx, doc);
1030 if (oi)
1031 fz_set_default_output_intent(ctx, default_cs, oi);
1032 }
1033 fz_catch(ctx)
1034 {
1035 if (fz_caught(ctx) != FZ_ERROR_TRYLATER)
1036 {
1037 fz_drop_default_colorspaces(ctx, default_cs);
1038 fz_rethrow(ctx);
1039 }
1040 page->super.incomplete = 1;
1041 }
1042
1043 return default_cs;
1044 }
1045
1046 fz_default_colorspaces *
pdf_update_default_colorspaces(fz_context * ctx,fz_default_colorspaces * old_cs,pdf_obj * res)1047 pdf_update_default_colorspaces(fz_context *ctx, fz_default_colorspaces *old_cs, pdf_obj *res)
1048 {
1049 pdf_obj *obj;
1050 fz_default_colorspaces *new_cs;
1051
1052 obj = pdf_dict_get(ctx, res, PDF_NAME(ColorSpace));
1053 if (!obj)
1054 return fz_keep_default_colorspaces(ctx, old_cs);
1055
1056 new_cs = fz_clone_default_colorspaces(ctx, old_cs);
1057 fz_try(ctx)
1058 pdf_load_default_colorspaces_imp(ctx, new_cs, obj);
1059 fz_catch(ctx)
1060 {
1061 fz_drop_default_colorspaces(ctx, new_cs);
1062 fz_rethrow(ctx);
1063 }
1064
1065 return new_cs;
1066 }
1067
1068 pdf_page *
pdf_load_page(fz_context * ctx,pdf_document * doc,int number)1069 pdf_load_page(fz_context *ctx, pdf_document *doc, int number)
1070 {
1071 pdf_page *page;
1072 pdf_annot *annot;
1073 pdf_obj *pageobj, *obj;
1074
1075 if (doc->file_reading_linearly)
1076 {
1077 pageobj = pdf_progressive_advance(ctx, doc, number);
1078 if (pageobj == NULL)
1079 fz_throw(ctx, FZ_ERROR_TRYLATER, "page %d not available yet", number);
1080 }
1081 else
1082 pageobj = pdf_lookup_page_obj(ctx, doc, number);
1083
1084 page = pdf_new_page(ctx, doc);
1085 page->obj = pdf_keep_obj(ctx, pageobj);
1086
1087 /* Pre-load annotations and links */
1088 fz_try(ctx)
1089 {
1090 obj = pdf_dict_get(ctx, pageobj, PDF_NAME(Annots));
1091 if (obj)
1092 {
1093 fz_rect page_mediabox;
1094 fz_matrix page_ctm;
1095 pdf_page_transform(ctx, page, &page_mediabox, &page_ctm);
1096 page->links = pdf_load_link_annots(ctx, doc, obj, number, page_ctm);
1097 pdf_load_annots(ctx, page, obj);
1098 }
1099 }
1100 fz_catch(ctx)
1101 {
1102 if (fz_caught(ctx) != FZ_ERROR_TRYLATER)
1103 {
1104 fz_drop_page(ctx, &page->super);
1105 fz_rethrow(ctx);
1106 }
1107 page->super.incomplete = 1;
1108 fz_drop_link(ctx, page->links);
1109 page->links = NULL;
1110 }
1111
1112 /* Scan for transparency and overprint */
1113 fz_try(ctx)
1114 {
1115 pdf_obj *resources = pdf_page_resources(ctx, page);
1116 if (pdf_name_eq(ctx, pdf_dict_getp(ctx, pageobj, "Group/S"), PDF_NAME(Transparency)))
1117 page->transparency = 1;
1118 else if (pdf_resources_use_blending(ctx, resources))
1119 page->transparency = 1;
1120 for (annot = page->annots; annot && !page->transparency; annot = annot->next)
1121 if (annot->ap && pdf_resources_use_blending(ctx, pdf_xobject_resources(ctx, annot->ap)))
1122 page->transparency = 1;
1123 if (pdf_resources_use_overprint(ctx, resources))
1124 page->overprint = 1;
1125 for (annot = page->annots; annot && !page->overprint; annot = annot->next)
1126 if (annot->ap && pdf_resources_use_overprint(ctx, pdf_xobject_resources(ctx, annot->ap)))
1127 page->overprint = 1;
1128 }
1129 fz_catch(ctx)
1130 {
1131 if (fz_caught(ctx) != FZ_ERROR_TRYLATER)
1132 {
1133 fz_drop_page(ctx, &page->super);
1134 fz_rethrow(ctx);
1135 }
1136 page->super.incomplete = 1;
1137 }
1138
1139 return page;
1140 }
1141
pdf_load_page_imp(fz_context * ctx,fz_document * doc,int chapter,int number)1142 fz_page *pdf_load_page_imp(fz_context *ctx, fz_document *doc, int chapter, int number)
1143 {
1144 return (fz_page*)pdf_load_page(ctx, (pdf_document*)doc, number);
1145 }
1146
1147 void
pdf_delete_page(fz_context * ctx,pdf_document * doc,int at)1148 pdf_delete_page(fz_context *ctx, pdf_document *doc, int at)
1149 {
1150 pdf_obj *parent, *kids;
1151 int i;
1152
1153 pdf_lookup_page_loc(ctx, doc, at, &parent, &i);
1154 kids = pdf_dict_get(ctx, parent, PDF_NAME(Kids));
1155 pdf_array_delete(ctx, kids, i);
1156
1157 while (parent)
1158 {
1159 int count = pdf_dict_get_int(ctx, parent, PDF_NAME(Count));
1160 pdf_dict_put_int(ctx, parent, PDF_NAME(Count), count - 1);
1161 parent = pdf_dict_get(ctx, parent, PDF_NAME(Parent));
1162 }
1163 }
1164
1165 void
pdf_delete_page_range(fz_context * ctx,pdf_document * doc,int start,int end)1166 pdf_delete_page_range(fz_context *ctx, pdf_document *doc, int start, int end)
1167 {
1168 int count = pdf_count_pages(ctx, doc);
1169
1170 if (end < 0 || end > count)
1171 end = count+1;
1172 if (start < 0)
1173 start = 0;
1174 while (start < end)
1175 {
1176 pdf_delete_page(ctx, doc, start);
1177 end--;
1178 }
1179 }
1180
1181 pdf_obj *
pdf_add_page(fz_context * ctx,pdf_document * doc,fz_rect mediabox,int rotate,pdf_obj * resources,fz_buffer * contents)1182 pdf_add_page(fz_context *ctx, pdf_document *doc, fz_rect mediabox, int rotate, pdf_obj *resources, fz_buffer *contents)
1183 {
1184 pdf_obj *page_obj = pdf_new_dict(ctx, doc, 5);
1185 fz_try(ctx)
1186 {
1187 pdf_dict_put(ctx, page_obj, PDF_NAME(Type), PDF_NAME(Page));
1188 pdf_dict_put_rect(ctx, page_obj, PDF_NAME(MediaBox), mediabox);
1189 pdf_dict_put_int(ctx, page_obj, PDF_NAME(Rotate), rotate);
1190
1191 if (pdf_is_indirect(ctx, resources))
1192 pdf_dict_put(ctx, page_obj, PDF_NAME(Resources), resources);
1193 else if (pdf_is_dict(ctx, resources))
1194 pdf_dict_put_drop(ctx, page_obj, PDF_NAME(Resources), pdf_add_object(ctx, doc, resources));
1195 else
1196 pdf_dict_put_dict(ctx, page_obj, PDF_NAME(Resources), 1);
1197
1198 if (contents)
1199 pdf_dict_put_drop(ctx, page_obj, PDF_NAME(Contents), pdf_add_stream(ctx, doc, contents, NULL, 0));
1200 }
1201 fz_catch(ctx)
1202 {
1203 pdf_drop_obj(ctx, page_obj);
1204 fz_rethrow(ctx);
1205 }
1206 return pdf_add_object_drop(ctx, doc, page_obj);
1207 }
1208
1209 void
pdf_insert_page(fz_context * ctx,pdf_document * doc,int at,pdf_obj * page_ref)1210 pdf_insert_page(fz_context *ctx, pdf_document *doc, int at, pdf_obj *page_ref)
1211 {
1212 int count = pdf_count_pages(ctx, doc);
1213 pdf_obj *parent, *kids;
1214 int i;
1215
1216 if (at < 0)
1217 at = count;
1218 if (at == INT_MAX)
1219 at = count;
1220 if (at > count)
1221 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot insert page beyond end of page tree");
1222
1223 if (count == 0)
1224 {
1225 pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root));
1226 parent = pdf_dict_get(ctx, root, PDF_NAME(Pages));
1227 if (!parent)
1228 fz_throw(ctx, FZ_ERROR_GENERIC, "cannot find page tree");
1229 kids = pdf_dict_get(ctx, parent, PDF_NAME(Kids));
1230 if (!kids)
1231 fz_throw(ctx, FZ_ERROR_GENERIC, "malformed page tree");
1232 pdf_array_insert(ctx, kids, page_ref, 0);
1233 }
1234 else if (at == count)
1235 {
1236 /* append after last page */
1237 pdf_lookup_page_loc(ctx, doc, count - 1, &parent, &i);
1238 kids = pdf_dict_get(ctx, parent, PDF_NAME(Kids));
1239 pdf_array_insert(ctx, kids, page_ref, i + 1);
1240 }
1241 else
1242 {
1243 /* insert before found page */
1244 pdf_lookup_page_loc(ctx, doc, at, &parent, &i);
1245 kids = pdf_dict_get(ctx, parent, PDF_NAME(Kids));
1246 pdf_array_insert(ctx, kids, page_ref, i);
1247 }
1248
1249 pdf_dict_put(ctx, page_ref, PDF_NAME(Parent), parent);
1250
1251 /* Adjust page counts */
1252 while (parent)
1253 {
1254 count = pdf_dict_get_int(ctx, parent, PDF_NAME(Count));
1255 pdf_dict_put_int(ctx, parent, PDF_NAME(Count), count + 1);
1256 parent = pdf_dict_get(ctx, parent, PDF_NAME(Parent));
1257 }
1258 }
1259