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