comparison mupdf-source/source/pdf/pdf-object.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 "mupdf/pdf.h"
25
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <math.h>
30
31 #define PDF_MAKE_NAME(STRING,NAME) STRING,
32 static const char *PDF_NAME_LIST[] = {
33 "", "", "", /* dummy slots for null, true, and false */
34 #include "mupdf/pdf/name-table.h"
35 };
36 #undef PDF_MAKE_NAME
37
38 typedef enum pdf_objkind_e
39 {
40 PDF_INT = 'i',
41 PDF_REAL = 'f',
42 PDF_STRING = 's',
43 PDF_NAME = 'n',
44 PDF_ARRAY = 'a',
45 PDF_DICT = 'd',
46 PDF_INDIRECT = 'r'
47 } pdf_objkind;
48
49 struct keyval
50 {
51 pdf_obj *k;
52 pdf_obj *v;
53 };
54
55 enum
56 {
57 PDF_FLAGS_MARKED = 1,
58 PDF_FLAGS_SORTED = 2,
59 PDF_FLAGS_DIRTY = 4,
60 PDF_FLAGS_MEMO_BASE = 8,
61 PDF_FLAGS_MEMO_BASE_BOOL = 16
62 };
63
64 struct pdf_obj
65 {
66 short refs;
67 unsigned char kind;
68 unsigned char flags;
69 };
70
71 typedef struct
72 {
73 pdf_obj super;
74 union
75 {
76 int64_t i;
77 float f;
78 } u;
79 } pdf_obj_num;
80
81 typedef struct
82 {
83 pdf_obj super;
84 char *text; /* utf8 encoded text string */
85 size_t len;
86 char buf[FZ_FLEXIBLE_ARRAY];
87 } pdf_obj_string;
88
89 typedef struct
90 {
91 pdf_obj super;
92 char n[FZ_FLEXIBLE_ARRAY];
93 } pdf_obj_name;
94
95 typedef struct
96 {
97 pdf_obj super;
98 pdf_document *doc;
99 int parent_num;
100 int len;
101 int cap;
102 pdf_obj **items;
103 } pdf_obj_array;
104
105 typedef struct
106 {
107 pdf_obj super;
108 pdf_document *doc;
109 int parent_num;
110 int len;
111 int cap;
112 struct keyval *items;
113 } pdf_obj_dict;
114
115 typedef struct
116 {
117 pdf_obj super;
118 pdf_document *doc; /* Only needed for arrays, dicts and indirects */
119 int num;
120 int gen;
121 } pdf_obj_ref;
122
123 /* Each journal fragment represents a change to a PDF xref object. */
124 typedef struct pdf_journal_fragment
125 {
126 struct pdf_journal_fragment *next;
127 struct pdf_journal_fragment *prev;
128
129 int obj_num;
130 int newobj;
131 pdf_obj *inactive;
132 fz_buffer *stream;
133 } pdf_journal_fragment;
134
135 /* A journal entry represents a single notional 'change' to the
136 * document, such as 'signing it' or 'filling in a field'. Each such
137 * change consists of 1 or more 'fragments'. */
138 typedef struct pdf_journal_entry
139 {
140 struct pdf_journal_entry *prev;
141 struct pdf_journal_entry *next;
142
143 char *title;
144 #ifdef PDF_DEBUG_JOURNAL
145 int changed_since_last_dumped;
146 #endif
147 pdf_journal_fragment *head;
148 pdf_journal_fragment *tail;
149 } pdf_journal_entry;
150
151 /* A journal consists of a list of journal entries, rooted at head.
152 * current is either NULL, or points to somewhere in the list. Anything
153 * between head and current inclusive represents a journalled change
154 * that is currently in force. Anything after current represents a
155 * journalled change that has been 'undone'. If current is NULL, then
156 * ALL changes in the list have been undone. */
157 struct pdf_journal
158 {
159 pdf_journal_entry *head;
160 pdf_journal_entry *current;
161 int nesting;
162 pdf_journal_entry *pending;
163 pdf_journal_entry *pending_tail;
164 };
165
166 #define NAME(obj) ((pdf_obj_name *)(obj))
167 #define NUM(obj) ((pdf_obj_num *)(obj))
168 #define STRING(obj) ((pdf_obj_string *)(obj))
169 #define DICT(obj) ((pdf_obj_dict *)(obj))
170 #define ARRAY(obj) ((pdf_obj_array *)(obj))
171 #define REF(obj) ((pdf_obj_ref *)(obj))
172
173 pdf_obj *
174 pdf_new_int(fz_context *ctx, int64_t i)
175 {
176 pdf_obj_num *obj;
177 obj = Memento_label(fz_malloc(ctx, sizeof(pdf_obj_num)), "pdf_obj(int)");
178 obj->super.refs = 1;
179 obj->super.kind = PDF_INT;
180 obj->super.flags = 0;
181 obj->u.i = i;
182 return &obj->super;
183 }
184
185 pdf_obj *
186 pdf_new_real(fz_context *ctx, float f)
187 {
188 pdf_obj_num *obj;
189 obj = Memento_label(fz_malloc(ctx, sizeof(pdf_obj_num)), "pdf_obj(real)");
190 obj->super.refs = 1;
191 obj->super.kind = PDF_REAL;
192 obj->super.flags = 0;
193 obj->u.f = f;
194 return &obj->super;
195 }
196
197 pdf_obj *
198 pdf_new_string(fz_context *ctx, const char *str, size_t len)
199 {
200 pdf_obj_string *obj;
201 unsigned int l = (unsigned int)len;
202
203 if ((size_t)l != len)
204 fz_throw(ctx, FZ_ERROR_LIMIT, "Overflow in pdf string");
205
206 obj = Memento_label(fz_malloc(ctx, offsetof(pdf_obj_string, buf) + len + 1), "pdf_obj(string)");
207 obj->super.refs = 1;
208 obj->super.kind = PDF_STRING;
209 obj->super.flags = 0;
210 obj->text = NULL;
211 obj->len = l;
212 memcpy(obj->buf, str, len);
213 obj->buf[len] = '\0';
214 return &obj->super;
215 }
216
217 pdf_obj *
218 pdf_new_name(fz_context *ctx, const char *str)
219 {
220 pdf_obj_name *obj;
221 int l = 3; /* skip dummy slots */
222 int r = nelem(PDF_NAME_LIST) - 1;
223 while (l <= r)
224 {
225 int m = (l + r) >> 1;
226 int c = strcmp(str, PDF_NAME_LIST[m]);
227 if (c < 0)
228 r = m - 1;
229 else if (c > 0)
230 l = m + 1;
231 else
232 return (pdf_obj*)(intptr_t)m;
233 }
234
235 obj = Memento_label(fz_malloc(ctx, offsetof(pdf_obj_name, n) + strlen(str) + 1), "pdf_obj(name)");
236 obj->super.refs = 1;
237 obj->super.kind = PDF_NAME;
238 obj->super.flags = 0;
239 strcpy(obj->n, str);
240 return &obj->super;
241 }
242
243 pdf_obj *
244 pdf_new_indirect(fz_context *ctx, pdf_document *doc, int num, int gen)
245 {
246 pdf_obj_ref *obj;
247 if (num < 0 || num > PDF_MAX_OBJECT_NUMBER)
248 {
249 fz_warn(ctx, "invalid object number (%d)", num);
250 return PDF_NULL;
251 }
252 if (gen < 0 || gen > PDF_MAX_GEN_NUMBER)
253 {
254 fz_warn(ctx, "invalid generation number (%d)", gen);
255 return PDF_NULL;
256 }
257 obj = Memento_label(fz_malloc(ctx, sizeof(pdf_obj_ref)), "pdf_obj(indirect)");
258 obj->super.refs = 1;
259 obj->super.kind = PDF_INDIRECT;
260 obj->super.flags = 0;
261 obj->doc = doc;
262 obj->num = num;
263 obj->gen = gen;
264 return &obj->super;
265 }
266
267 #define OBJ_IS_NULL(obj) (obj == PDF_NULL)
268 #define OBJ_IS_BOOL(obj) (obj == PDF_TRUE || obj == PDF_FALSE)
269 #define OBJ_IS_NAME(obj) ((obj > PDF_FALSE && obj < PDF_LIMIT) || (obj >= PDF_LIMIT && obj->kind == PDF_NAME))
270 #define OBJ_IS_INT(obj) \
271 (obj >= PDF_LIMIT && obj->kind == PDF_INT)
272 #define OBJ_IS_REAL(obj) \
273 (obj >= PDF_LIMIT && obj->kind == PDF_REAL)
274 #define OBJ_IS_NUMBER(obj) \
275 (obj >= PDF_LIMIT && (obj->kind == PDF_REAL || obj->kind == PDF_INT))
276 #define OBJ_IS_STRING(obj) \
277 (obj >= PDF_LIMIT && obj->kind == PDF_STRING)
278 #define OBJ_IS_ARRAY(obj) \
279 (obj >= PDF_LIMIT && obj->kind == PDF_ARRAY)
280 #define OBJ_IS_DICT(obj) \
281 (obj >= PDF_LIMIT && obj->kind == PDF_DICT)
282 #define OBJ_IS_INDIRECT(obj) \
283 (obj >= PDF_LIMIT && obj->kind == PDF_INDIRECT)
284
285 #define RESOLVE(obj) \
286 if (OBJ_IS_INDIRECT(obj)) \
287 obj = pdf_resolve_indirect_chain(ctx, obj); \
288
289 int pdf_is_indirect(fz_context *ctx, pdf_obj *obj)
290 {
291 return OBJ_IS_INDIRECT(obj);
292 }
293
294 int pdf_is_null(fz_context *ctx, pdf_obj *obj)
295 {
296 RESOLVE(obj);
297 return OBJ_IS_NULL(obj);
298 }
299
300 int pdf_is_bool(fz_context *ctx, pdf_obj *obj)
301 {
302 RESOLVE(obj);
303 return OBJ_IS_BOOL(obj);
304 }
305
306 int pdf_is_int(fz_context *ctx, pdf_obj *obj)
307 {
308 RESOLVE(obj);
309 return OBJ_IS_INT(obj);
310 }
311
312 int pdf_is_real(fz_context *ctx, pdf_obj *obj)
313 {
314 RESOLVE(obj);
315 return OBJ_IS_REAL(obj);
316 }
317
318 int pdf_is_number(fz_context *ctx, pdf_obj *obj)
319 {
320 RESOLVE(obj);
321 return OBJ_IS_NUMBER(obj);
322 }
323
324 int pdf_is_string(fz_context *ctx, pdf_obj *obj)
325 {
326 RESOLVE(obj);
327 return OBJ_IS_STRING(obj);
328 }
329
330 int pdf_is_name(fz_context *ctx, pdf_obj *obj)
331 {
332 RESOLVE(obj);
333 return OBJ_IS_NAME(obj);
334 }
335
336 int pdf_is_array(fz_context *ctx, pdf_obj *obj)
337 {
338 RESOLVE(obj);
339 return OBJ_IS_ARRAY(obj);
340 }
341
342 int pdf_is_dict(fz_context *ctx, pdf_obj *obj)
343 {
344 RESOLVE(obj);
345 return OBJ_IS_DICT(obj);
346 }
347
348 /* safe, silent failure, no error reporting on type mismatches */
349 int pdf_to_bool(fz_context *ctx, pdf_obj *obj)
350 {
351 RESOLVE(obj);
352 return obj == PDF_TRUE;
353 }
354
355 int pdf_to_bool_default(fz_context *ctx, pdf_obj *obj, int def)
356 {
357 RESOLVE(obj);
358 return obj == PDF_TRUE ? 1 : obj == PDF_FALSE ? 0 : def;
359 }
360
361 int pdf_to_int(fz_context *ctx, pdf_obj *obj)
362 {
363 RESOLVE(obj);
364 if (obj < PDF_LIMIT)
365 return 0;
366 if (obj->kind == PDF_INT)
367 return (int)NUM(obj)->u.i;
368 if (obj->kind == PDF_REAL)
369 return (int)floorf(NUM(obj)->u.f + 0.5);
370 return 0;
371 }
372
373 int pdf_to_int_default(fz_context *ctx, pdf_obj *obj, int def)
374 {
375 RESOLVE(obj);
376 if (obj < PDF_LIMIT)
377 return def;
378 if (obj->kind == PDF_INT)
379 return (int)NUM(obj)->u.i;
380 if (obj->kind == PDF_REAL)
381 return (int)floorf(NUM(obj)->u.f + 0.5);
382 return def;
383 }
384
385 int64_t pdf_to_int64(fz_context *ctx, pdf_obj *obj)
386 {
387 RESOLVE(obj);
388 if (obj < PDF_LIMIT)
389 return 0;
390 if (obj->kind == PDF_INT)
391 return NUM(obj)->u.i;
392 if (obj->kind == PDF_REAL)
393 return (int64_t)floorf(NUM(obj)->u.f + 0.5);
394 return 0;
395 }
396
397 float pdf_to_real(fz_context *ctx, pdf_obj *obj)
398 {
399 RESOLVE(obj);
400 if (obj < PDF_LIMIT)
401 return 0;
402 if (obj->kind == PDF_REAL)
403 return NUM(obj)->u.f;
404 if (obj->kind == PDF_INT)
405 return NUM(obj)->u.i;
406 return 0;
407 }
408
409 float pdf_to_real_default(fz_context *ctx, pdf_obj *obj, float def)
410 {
411 RESOLVE(obj);
412 if (obj < PDF_LIMIT)
413 return def;
414 if (obj->kind == PDF_REAL)
415 return NUM(obj)->u.f;
416 if (obj->kind == PDF_INT)
417 return NUM(obj)->u.i;
418 return def;
419 }
420
421 const char *pdf_to_name(fz_context *ctx, pdf_obj *obj)
422 {
423 RESOLVE(obj);
424 if (obj < PDF_LIMIT)
425 return PDF_NAME_LIST[((intptr_t)obj)];
426 if (obj->kind == PDF_NAME)
427 return NAME(obj)->n;
428 return "";
429 }
430
431 char *pdf_to_str_buf(fz_context *ctx, pdf_obj *obj)
432 {
433 RESOLVE(obj);
434 if (OBJ_IS_STRING(obj))
435 return STRING(obj)->buf;
436 return "";
437 }
438
439 size_t pdf_to_str_len(fz_context *ctx, pdf_obj *obj)
440 {
441 RESOLVE(obj);
442 if (OBJ_IS_STRING(obj))
443 return STRING(obj)->len;
444 return 0;
445 }
446
447 const char *pdf_to_string(fz_context *ctx, pdf_obj *obj, size_t *sizep)
448 {
449 RESOLVE(obj);
450 if (OBJ_IS_STRING(obj))
451 {
452 if (sizep)
453 *sizep = STRING(obj)->len;
454 return STRING(obj)->buf;
455 }
456 if (sizep)
457 *sizep = 0;
458 return "";
459 }
460
461 const char *pdf_to_text_string(fz_context *ctx, pdf_obj *obj)
462 {
463 RESOLVE(obj);
464 if (OBJ_IS_STRING(obj))
465 {
466 if (!STRING(obj)->text)
467 STRING(obj)->text = pdf_new_utf8_from_pdf_string(ctx, STRING(obj)->buf, STRING(obj)->len);
468 return STRING(obj)->text;
469 }
470 return "";
471 }
472
473 void pdf_set_int(fz_context *ctx, pdf_obj *obj, int64_t i)
474 {
475 if (OBJ_IS_INT(obj))
476 NUM(obj)->u.i = i;
477 }
478
479 void pdf_set_str_len(fz_context *ctx, pdf_obj *obj, size_t newlen)
480 {
481 RESOLVE(obj);
482 if (!OBJ_IS_STRING(obj))
483 return; /* This should never happen */
484 if (newlen > STRING(obj)->len)
485 return; /* This should never happen */
486 STRING(obj)->buf[newlen] = 0;
487 STRING(obj)->len = newlen;
488 }
489
490 int pdf_to_num(fz_context *ctx, pdf_obj *obj)
491 {
492 if (OBJ_IS_INDIRECT(obj))
493 return REF(obj)->num;
494 return 0;
495 }
496
497 int pdf_to_gen(fz_context *ctx, pdf_obj *obj)
498 {
499 if (OBJ_IS_INDIRECT(obj))
500 return REF(obj)->gen;
501 return 0;
502 }
503
504 /*
505 DEPRECATED: Do not use in new code.
506 */
507 pdf_document *pdf_get_indirect_document(fz_context *ctx, pdf_obj *obj)
508 {
509 if (OBJ_IS_INDIRECT(obj))
510 return REF(obj)->doc;
511 return NULL;
512 }
513
514 /*
515 DEPRECATED: Do not use in new code.
516 */
517 pdf_document *pdf_get_bound_document(fz_context *ctx, pdf_obj *obj)
518 {
519 if (obj < PDF_LIMIT)
520 return NULL;
521 if (obj->kind == PDF_INDIRECT)
522 return REF(obj)->doc;
523 if (obj->kind == PDF_ARRAY)
524 return ARRAY(obj)->doc;
525 if (obj->kind == PDF_DICT)
526 return DICT(obj)->doc;
527 return NULL;
528 }
529
530 /*
531 This implementation will do to provide the required
532 API change in advance of the rewrite to use weak references
533 in the next version.
534 */
535 pdf_document *pdf_pin_document(fz_context *ctx, pdf_obj *obj)
536 {
537 return pdf_keep_document(ctx, pdf_get_bound_document(ctx, obj));
538 }
539
540 int pdf_objcmp_resolve(fz_context *ctx, pdf_obj *a, pdf_obj *b)
541 {
542 RESOLVE(a);
543 RESOLVE(b);
544 return pdf_objcmp(ctx, a, b);
545 }
546
547 static int
548 do_objcmp(fz_context *ctx, pdf_obj *a, pdf_obj *b, int check_streams)
549 {
550 int i, j;
551
552 if (a == b)
553 return 0;
554
555 /* a or b is null, true, or false */
556 if (a <= PDF_FALSE || b <= PDF_FALSE)
557 return 1;
558
559 /* a is a constant name */
560 if (a < PDF_LIMIT)
561 {
562 if (b < PDF_LIMIT)
563 return a != b;
564 if (b->kind != PDF_NAME)
565 return 1;
566 return strcmp(PDF_NAME_LIST[(intptr_t)a], NAME(b)->n);
567 }
568
569 /* b is a constant name */
570 if (b < PDF_LIMIT)
571 {
572 if (a->kind != PDF_NAME)
573 return 1;
574 return strcmp(NAME(a)->n, PDF_NAME_LIST[(intptr_t)b]);
575 }
576
577 /* both a and b are allocated objects */
578 if (a->kind != b->kind)
579 return 1;
580
581 switch (a->kind)
582 {
583 case PDF_INT:
584 return NUM(a)->u.i - NUM(b)->u.i;
585
586 case PDF_REAL:
587 if (NUM(a)->u.f < NUM(b)->u.f)
588 return -1;
589 if (NUM(a)->u.f > NUM(b)->u.f)
590 return 1;
591 return 0;
592
593 case PDF_STRING:
594 if (STRING(a)->len < STRING(b)->len)
595 {
596 if (memcmp(STRING(a)->buf, STRING(b)->buf, STRING(a)->len) <= 0)
597 return -1;
598 return 1;
599 }
600 if (STRING(a)->len > STRING(b)->len)
601 {
602 if (memcmp(STRING(a)->buf, STRING(b)->buf, STRING(b)->len) >= 0)
603 return 1;
604 return -1;
605 }
606 return memcmp(STRING(a)->buf, STRING(b)->buf, STRING(a)->len);
607
608 case PDF_NAME:
609 return strcmp(NAME(a)->n, NAME(b)->n);
610
611 case PDF_INDIRECT:
612 if (REF(a)->num == REF(b)->num)
613 return REF(a)->gen - REF(b)->gen;
614 return REF(a)->num - REF(b)->num;
615
616 case PDF_ARRAY:
617 if (ARRAY(a)->len != ARRAY(b)->len)
618 return ARRAY(a)->len - ARRAY(b)->len;
619 for (i = 0; i < ARRAY(a)->len; i++)
620 if (pdf_objcmp(ctx, ARRAY(a)->items[i], ARRAY(b)->items[i]))
621 return 1;
622 return 0;
623
624 case PDF_DICT:
625 if (DICT(a)->len != DICT(b)->len)
626 return DICT(a)->len - DICT(b)->len;
627 if ((a->flags & b->flags) & PDF_FLAGS_SORTED)
628 {
629 /* Both a and b are sorted. Easy. */
630 for (i = 0; i < DICT(a)->len; i++)
631 {
632 if (pdf_objcmp(ctx, DICT(a)->items[i].k, DICT(b)->items[i].k))
633 return 1;
634 if (pdf_objcmp(ctx, DICT(a)->items[i].v, DICT(b)->items[i].v))
635 return 1;
636 }
637 }
638 else
639 {
640 /* Either a or b is not sorted. We need to work harder. */
641 int len = DICT(a)->len;
642 for (i = 0; i < len; i++)
643 {
644 pdf_obj *key = DICT(a)->items[i].k;
645 pdf_obj *val = DICT(a)->items[i].v;
646 for (j = 0; j < len; j++)
647 {
648 if (pdf_objcmp(ctx, key, DICT(b)->items[j].k) == 0 &&
649 pdf_objcmp(ctx, val, DICT(b)->items[j].v) == 0)
650 break; /* Match */
651 }
652 if (j == len)
653 return 1;
654 }
655 }
656 /* Dicts are identical, but if they are streams, we can only be sure
657 * they are identical if the stream contents match. If '!check_streams',
658 * then don't test for identical stream contents - only match if a == b.
659 * Otherwise, do the full, painful, comparison. */
660 {
661 /* Slightly convoluted to know if something is a stream. */
662 pdf_document *doc = DICT(a)->doc;
663 int ap = pdf_obj_parent_num(ctx, a);
664 int bp;
665 int a_is_stream = 0;
666 pdf_xref_entry *entrya = pdf_get_xref_entry_no_change(ctx, doc, ap);
667 pdf_xref_entry *entryb;
668 if (entrya != NULL && entrya->obj == a && pdf_obj_num_is_stream(ctx, doc, ap))
669 {
670 /* It's a stream, and we know a != b from above. */
671 if (!check_streams)
672 return 1; /* mismatch */
673 a_is_stream = 1;
674 }
675 bp = pdf_obj_parent_num(ctx, b);
676 entryb = pdf_get_xref_entry_no_change(ctx, doc, bp);
677 if (entryb != NULL && entryb->obj == b && pdf_obj_num_is_stream(ctx, doc, bp))
678 {
679 /* It's a stream, and we know a != b from above. So mismatch. */
680 if (!check_streams || !a_is_stream)
681 return 1; /* mismatch */
682 }
683 else
684 {
685 /* b is not a stream. We match, iff a is not a stream. */
686 return a_is_stream;
687 }
688 /* So, if we get here, we know check_streams is true, and that both
689 * a and b are streams. */
690 {
691 fz_buffer *sa = NULL;
692 fz_buffer *sb = NULL;
693 int differ = 1;
694
695 fz_var(sa);
696 fz_var(sb);
697
698 fz_try(ctx)
699 {
700 unsigned char *dataa, *datab;
701 size_t lena, lenb;
702 sa = pdf_load_raw_stream_number(ctx, doc, ap);
703 sb = pdf_load_raw_stream_number(ctx, doc, bp);
704 lena = fz_buffer_storage(ctx, sa, &dataa);
705 lenb = fz_buffer_storage(ctx, sb, &datab);
706 if (lena == lenb && memcmp(dataa, datab, lena) == 0)
707 differ = 0;
708 }
709 fz_always(ctx)
710 {
711 fz_drop_buffer(ctx, sa);
712 fz_drop_buffer(ctx, sb);
713 }
714 fz_catch(ctx)
715 {
716 fz_rethrow(ctx);
717 }
718 return differ;
719 }
720 }
721 }
722 return 1;
723 }
724
725 int
726 pdf_objcmp(fz_context *ctx, pdf_obj *a, pdf_obj *b)
727 {
728 return do_objcmp(ctx, a, b, 0);
729 }
730
731 int
732 pdf_objcmp_deep(fz_context *ctx, pdf_obj *a, pdf_obj *b)
733 {
734 return do_objcmp(ctx, a, b, 1);
735 }
736
737 int pdf_name_eq(fz_context *ctx, pdf_obj *a, pdf_obj *b)
738 {
739 RESOLVE(a);
740 RESOLVE(b);
741 if (a <= PDF_FALSE || b <= PDF_FALSE)
742 return 0;
743 if (a < PDF_LIMIT || b < PDF_LIMIT)
744 return (a == b);
745 if (a->kind == PDF_NAME && b->kind == PDF_NAME)
746 return !strcmp(NAME(a)->n, NAME(b)->n);
747 return 0;
748 }
749
750 static char *
751 pdf_objkindstr(pdf_obj *obj)
752 {
753 if (obj == PDF_NULL)
754 return "null";
755 if (obj == PDF_TRUE || obj == PDF_FALSE)
756 return "boolean";
757 if (obj < PDF_LIMIT)
758 return "name";
759 switch (obj->kind)
760 {
761 case PDF_INT: return "integer";
762 case PDF_REAL: return "real";
763 case PDF_STRING: return "string";
764 case PDF_NAME: return "name";
765 case PDF_ARRAY: return "array";
766 case PDF_DICT: return "dictionary";
767 case PDF_INDIRECT: return "reference";
768 }
769 return "<unknown>";
770 }
771
772 pdf_obj *
773 pdf_new_array(fz_context *ctx, pdf_document *doc, int initialcap)
774 {
775 pdf_obj_array *obj;
776 int i;
777
778 if (doc == NULL)
779 fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot create array without a document");
780
781 obj = Memento_label(fz_malloc(ctx, sizeof(pdf_obj_array)), "pdf_obj(array)");
782 obj->super.refs = 1;
783 obj->super.kind = PDF_ARRAY;
784 obj->super.flags = 0;
785 obj->doc = doc;
786 obj->parent_num = 0;
787
788 obj->len = 0;
789 obj->cap = initialcap > 1 ? initialcap : 6;
790
791 fz_try(ctx)
792 {
793 obj->items = Memento_label(fz_malloc_array(ctx, obj->cap, pdf_obj*), "pdf_array_items");
794 }
795 fz_catch(ctx)
796 {
797 fz_free(ctx, obj);
798 fz_rethrow(ctx);
799 }
800 for (i = 0; i < obj->cap; i++)
801 obj->items[i] = NULL;
802
803 return &obj->super;
804 }
805
806 static void
807 pdf_array_grow(fz_context *ctx, pdf_obj_array *obj)
808 {
809 int i;
810 int new_cap = (obj->cap * 3) / 2;
811
812 obj->items = fz_realloc_array(ctx, obj->items, new_cap, pdf_obj*);
813 obj->cap = new_cap;
814
815 for (i = obj->len ; i < obj->cap; i++)
816 obj->items[i] = NULL;
817 }
818
819 pdf_obj *
820 pdf_copy_array(fz_context *ctx, pdf_obj *obj)
821 {
822 pdf_document *doc;
823 pdf_obj *arr;
824 int i;
825 int n;
826
827 RESOLVE(obj);
828 if (!OBJ_IS_ARRAY(obj))
829 fz_throw(ctx, FZ_ERROR_ARGUMENT, "not an array (%s)", pdf_objkindstr(obj));
830
831 doc = ARRAY(obj)->doc;
832
833 n = pdf_array_len(ctx, obj);
834 arr = pdf_new_array(ctx, doc, n);
835 fz_try(ctx)
836 for (i = 0; i < n; i++)
837 pdf_array_push(ctx, arr, pdf_array_get(ctx, obj, i));
838 fz_catch(ctx)
839 {
840 pdf_drop_obj(ctx, arr);
841 fz_rethrow(ctx);
842 }
843
844 return arr;
845 }
846
847 int
848 pdf_array_len(fz_context *ctx, pdf_obj *obj)
849 {
850 RESOLVE(obj);
851 if (!OBJ_IS_ARRAY(obj))
852 return 0;
853 return ARRAY(obj)->len;
854 }
855
856 pdf_obj *
857 pdf_array_get(fz_context *ctx, pdf_obj *obj, int i)
858 {
859 RESOLVE(obj);
860 if (!OBJ_IS_ARRAY(obj))
861 return NULL;
862 if (i < 0 || i >= ARRAY(obj)->len)
863 return NULL;
864 return ARRAY(obj)->items[i];
865 }
866
867 /* Call this to enable journalling on a given document. */
868 void pdf_enable_journal(fz_context *ctx, pdf_document *doc)
869 {
870 if (ctx == NULL || doc == NULL)
871 return;
872
873 if (doc->journal == NULL)
874 doc->journal = fz_malloc_struct(ctx, pdf_journal);
875 }
876
877 static void
878 discard_fragments(fz_context *ctx, pdf_journal_fragment *head)
879 {
880 while (head)
881 {
882 pdf_journal_fragment *next = head->next;
883
884 pdf_drop_obj(ctx, head->inactive);
885 fz_drop_buffer(ctx, head->stream);
886 fz_free(ctx, head);
887 head = next;
888 }
889 }
890
891 static void
892 discard_journal_entries(fz_context *ctx, pdf_journal_entry **journal_entry)
893 {
894 pdf_journal_entry *entry = *journal_entry;
895
896 if (entry == NULL)
897 return;
898
899 *journal_entry = NULL;
900 while (entry)
901 {
902 pdf_journal_entry *next = entry->next;
903
904 discard_fragments(ctx, entry->head);
905 fz_free(ctx, entry->title);
906 fz_free(ctx, entry);
907 entry = next;
908 }
909 }
910
911 static void
912 new_entry(fz_context *ctx, pdf_document *doc, char *operation)
913 {
914 fz_try(ctx)
915 {
916 pdf_journal_entry *entry;
917
918 /* We create a new entry, and link it into the middle of
919 * the chain. If we actually come to put anything into
920 * it later, then the call to pdf_add_journal_fragment
921 * during that addition will discard everything in the
922 * history that follows it. */
923 entry = fz_malloc_struct(ctx, pdf_journal_entry);
924
925 if (doc->journal->current == NULL)
926 {
927 entry->prev = NULL;
928 entry->next = doc->journal->head;
929 doc->journal->head = entry;
930 }
931 else
932 {
933 entry->prev = doc->journal->current;
934 entry->next = doc->journal->current->next;
935 if (doc->journal->current->next)
936 doc->journal->current->next->prev = entry;
937 doc->journal->current->next = entry;
938 }
939 doc->journal->current = entry;
940 entry->title = operation;
941 }
942 fz_catch(ctx)
943 {
944 fz_free(ctx, operation);
945 fz_rethrow(ctx);
946 }
947 }
948
949 /* Call this to start an operation. Undo/redo works at 'operation'
950 * granularity. Nested operations are all counted within the outermost
951 * operation. Any modification performed on a journalled PDF without an
952 * operation having been started will throw an error. */
953 static void
954 do_begin_operation(fz_context *ctx, pdf_document *doc, const char *operation_)
955 {
956 char *operation;
957
958 /* If we aren't journalling this doc, just give up now. */
959 if (ctx == NULL || doc == NULL || doc->journal == NULL)
960 return;
961
962 /* Always increment nesting. */
963 doc->journal->nesting++;
964
965 operation = operation_ ? fz_strdup(ctx, operation_) : NULL;
966
967 #ifdef PDF_DEBUG_JOURNAL
968 fz_write_printf(ctx, fz_stddbg(ctx), "Beginning: (->%d) %s\n", doc->journal->nesting, operation ? operation : "<implicit>");
969 #endif
970
971 fz_try(ctx)
972 {
973 pdf_journal_entry *entry;
974
975 /* We create a new entry, and link it into the middle of
976 * the chain. If we actually come to put anything into
977 * it later, then the call to pdf_add_journal_fragment
978 * during that addition will discard everything in the
979 * history that follows it. */
980 entry = fz_malloc_struct(ctx, pdf_journal_entry);
981
982 if (doc->journal->pending_tail == NULL)
983 {
984 entry->prev = NULL;
985 entry->next = doc->journal->pending;
986 doc->journal->pending = entry;
987 }
988 else
989 {
990 entry->prev = doc->journal->pending_tail;
991 entry->next = doc->journal->pending_tail->next;
992 if (doc->journal->pending_tail->next)
993 doc->journal->pending_tail->next->prev = entry;
994 doc->journal->pending_tail->next = entry;
995 }
996 doc->journal->pending_tail = entry;
997 entry->title = operation;
998 }
999 fz_catch(ctx)
1000 {
1001 doc->journal->nesting--;
1002 fz_free(ctx, operation);
1003 fz_rethrow(ctx);
1004 }
1005 }
1006
1007 void pdf_begin_operation(fz_context *ctx, pdf_document *doc, const char *operation)
1008 {
1009 if (operation == NULL)
1010 fz_throw(ctx, FZ_ERROR_ARGUMENT, "All operations must be named");
1011
1012 do_begin_operation(ctx, doc, operation);
1013 }
1014
1015 void pdf_begin_implicit_operation(fz_context *ctx, pdf_document *doc)
1016 {
1017 do_begin_operation(ctx, doc, NULL);
1018 }
1019
1020 void pdf_drop_journal(fz_context *ctx, pdf_journal *journal)
1021 {
1022 if (ctx == NULL || journal == NULL)
1023 return;
1024
1025 discard_journal_entries(ctx, &journal->head);
1026 /* Shouldn't be any pending ones, but be safe. */
1027 discard_journal_entries(ctx, &journal->pending);
1028
1029 fz_free(ctx, journal);
1030 }
1031
1032 #ifdef PDF_DEBUG_JOURNAL
1033 static void
1034 dump_changes(fz_context *ctx, pdf_document *doc, pdf_journal_entry *entry)
1035 {
1036 pdf_journal_fragment *frag;
1037
1038 if (entry == NULL || entry->changed_since_last_dumped == 0)
1039 return;
1040
1041 for (frag = entry->head; frag; frag = frag->next)
1042 {
1043 pdf_obj *obj;
1044 fz_write_printf(ctx, fz_stddbg(ctx), "Changing obj %d:\n", frag->obj_num);
1045 pdf_debug_obj(ctx, frag->inactive);
1046 fz_write_printf(ctx, fz_stddbg(ctx), " To:\n");
1047 obj = pdf_load_object(ctx, doc, frag->obj_num);
1048 pdf_debug_obj(ctx, obj);
1049 pdf_drop_obj(ctx, obj);
1050 }
1051
1052 entry->changed_since_last_dumped = 0;
1053 }
1054 #endif
1055
1056 /* We build up journal entries as being a list of changes (fragments) that
1057 * happen all together as part of a single step. When we reach pdf_end_operation
1058 * we have all the changes that have happened during this operation in a list
1059 * that basically boils down to being:
1060 *
1061 * change object x from being A to the value in the xref.
1062 * change object y from being B to the value in the xref.
1063 * change object z from being C to the value in the xref.
1064 * etc.
1065 *
1066 * The idea is that we can undo, or redo by stepping through that list.
1067 * Every object can only be mentioned once in a fragment (otherwise we
1068 * get very confused when undoing and redoing).
1069 *
1070 * When we come to glue 2 entries together (as happens when we end a
1071 * nested or implicit operation), we need to be sure that the 2 entries
1072 * don't both mention the same object.
1073 *
1074 * Imagine we've edited a text field from being empty to containing
1075 * 'he' by typing each char at a time:
1076 *
1077 * Entry 1:
1078 * change object x from being ''.
1079 * Entry 2 (implicit):
1080 * change object x from being 'h'.
1081 *
1082 * with current xref entry for x being 'he'.
1083 *
1084 * When we come to combine the two, we can't simply go to:
1085 *
1086 * change object x from being ''.
1087 * change object x from being 'h'.
1088 *
1089 * If we 'undo' that, however, because we run forwards through the list for
1090 * both undo and redo, we get it wrong.
1091 *
1092 * First we replace 'he' by ''.
1093 * Then we replace '' by 'h'.
1094 *
1095 * i.e. leaving us only partly undone.
1096 *
1097 * Either we need to run in different directions for undo and redo, or we need to
1098 * resolve the changes down to a single change for each object. Given that we don't
1099 * really want more than one change for each object in each changeset (needless memory
1100 * etc), let's resolve the changesets.
1101 **/
1102 static void resolve_undo(fz_context *ctx, pdf_journal_entry *entry)
1103 {
1104 pdf_journal_fragment *start, *current;
1105 pdf_journal_fragment *tail = NULL;
1106
1107 /* Slightly nasty that this is n^2, but any alternative involves
1108 * sorting. Shouldn't be huge lists anyway. */
1109 for (start = entry->head; start; start = start->next)
1110 {
1111 pdf_journal_fragment *next;
1112 tail = start;
1113
1114 for (current = start->next; current; current = next)
1115 {
1116 next = current->next;
1117
1118 if (start->obj_num == current->obj_num)
1119 {
1120 pdf_drop_obj(ctx, current->inactive);
1121 fz_drop_buffer(ctx, current->stream);
1122 /* start->newobj should not change */
1123 /* Now drop current */
1124 if (next)
1125 next->prev = current->prev;
1126 current->prev->next = next;
1127 fz_free(ctx, current);
1128 }
1129 }
1130 }
1131 entry->tail = tail;
1132 }
1133
1134 /* Call this to end an operation. */
1135 void pdf_end_operation(fz_context *ctx, pdf_document *doc)
1136 {
1137 pdf_journal_entry *entry;
1138
1139 if (ctx == NULL || doc == NULL || doc->journal == NULL)
1140 return;
1141
1142 /* Decrement the operation nesting count. */
1143 if (--doc->journal->nesting > 0)
1144 {
1145 /* We need to move the contents of doc->pending_tail down to
1146 * be on doc->pending_tail->prev. i.e. we combine fragments
1147 * as these operations become one. */
1148 entry = doc->journal->pending_tail;
1149
1150 /* An implicit operation before we start the file can result in us getting here
1151 * with no entry at all! */
1152 if (entry && entry->prev)
1153 {
1154 if (entry->tail == NULL)
1155 {
1156 /* Nothing to move. */
1157 }
1158 else if (entry->prev->tail == NULL)
1159 {
1160 /* Nothing where we want to move it. */
1161 entry->prev->head = entry->head;
1162 entry->prev->tail = entry->tail;
1163 }
1164 else
1165 {
1166 /* Append one list to the other. */
1167 entry->prev->tail->next = entry->head;
1168 entry->head->prev = entry->prev->tail;
1169 entry->prev->tail = entry->tail;
1170 /* And resolve any clashing objects */
1171 resolve_undo(ctx, entry->prev);
1172 }
1173 #ifdef PDF_DEBUG_JOURNAL
1174 fz_write_printf(ctx, fz_stddbg(ctx), "Ending! (->%d) \"%s\" <= \"%s\"\n", doc->journal->nesting,
1175 entry->prev->title ? entry->prev->title : "<implicit>",
1176 entry->title ? entry->title : "<implicit>");
1177 #endif
1178 doc->journal->pending_tail = entry->prev;
1179 entry->prev->next = NULL;
1180 fz_free(ctx, entry->title);
1181 fz_free(ctx, entry);
1182 }
1183 else
1184 {
1185 #ifdef PDF_DEBUG_JOURNAL
1186 fz_write_printf(ctx, fz_stddbg(ctx), "Ending! (->%d) no entry\n", doc->journal->nesting);
1187 #endif
1188 }
1189 return;
1190 }
1191
1192 /* Now, check to see whether we have actually stored any changes
1193 * (fragments) into our entry. If we have, we need to move these
1194 * changes from pending onto current. */
1195 entry = doc->journal->pending;
1196 assert(entry);
1197
1198 /* We really ought to have just a single pending entry at this point,
1199 * but implicit operations when we've just loaded a file can mean
1200 * that we don't have an entry at all. */
1201 if (entry == NULL)
1202 {
1203 /* Never happens! */
1204 }
1205 else if (entry->head == NULL)
1206 {
1207 /* Didn't actually change anything! Remove the empty entry. */
1208 #ifdef PDF_DEBUG_JOURNAL
1209 fz_write_printf(ctx, fz_stddbg(ctx), "Ending Empty!\n");
1210 #endif
1211 discard_journal_entries(ctx, &doc->journal->pending);
1212 }
1213 else if (entry->title != NULL)
1214 {
1215 /* Explicit operation. Move the entry off the pending list. */
1216 assert(entry->next == NULL);
1217 if (doc->journal->current)
1218 {
1219 doc->journal->current->next = entry;
1220 entry->prev = doc->journal->current;
1221 doc->journal->current = entry;
1222 }
1223 else
1224 {
1225 doc->journal->head = entry;
1226 doc->journal->current = entry;
1227 }
1228 #ifdef PDF_DEBUG_JOURNAL
1229 fz_write_printf(ctx, fz_stddbg(ctx), "Ending!\n");
1230 #endif
1231 }
1232 else if (doc->journal->current == NULL)
1233 {
1234 /* Implicit operation, with no previous one. */
1235 #ifdef PDF_DEBUG_JOURNAL
1236 fz_write_printf(ctx, fz_stddbg(ctx), "Ending implicit with no previous!\n");
1237 #endif
1238 /* Just drop the record of the changes. */
1239 discard_journal_entries(ctx, &doc->journal->pending);
1240 }
1241 else
1242 {
1243 /* Implicit operation. Roll these changes into the previous one.*/
1244 #ifdef PDF_DEBUG_JOURNAL
1245 fz_write_printf(ctx, fz_stddbg(ctx), "Ending implicit!\n");
1246 #endif
1247 doc->journal->current->tail->next = entry->head;
1248 entry->head->prev = doc->journal->current->tail;
1249 doc->journal->current->tail = entry->tail;
1250 entry->head = NULL;
1251 entry->tail = NULL;
1252 fz_free(ctx, entry->title);
1253 fz_free(ctx, entry);
1254 /* And resolve any clashing objects */
1255 resolve_undo(ctx, doc->journal->current);
1256 }
1257 doc->journal->pending = NULL;
1258 doc->journal->pending_tail = NULL;
1259 }
1260
1261 /* Call this to find out how many undo/redo steps there are, and the
1262 * current position we are within those. 0 = original document,
1263 * *steps = final edited version. */
1264 int pdf_undoredo_state(fz_context *ctx, pdf_document *doc, int *steps)
1265 {
1266 int i, c;
1267 pdf_journal_entry *entry;
1268
1269 if (ctx == NULL || doc == NULL || doc->journal == NULL)
1270 {
1271 *steps = 0;
1272 return 0;
1273 }
1274
1275 if (doc->journal->pending != NULL || doc->journal->nesting > 0)
1276 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Can't undo/redo during an operation");
1277
1278 i = 0;
1279 c = 0;
1280 for (entry = doc->journal->head; entry != NULL; entry = entry->next)
1281 {
1282 i++;
1283 if (entry == doc->journal->current)
1284 c = i;
1285 }
1286
1287 *steps = i;
1288
1289 return c;
1290 }
1291
1292 int pdf_can_undo(fz_context *ctx, pdf_document *doc)
1293 {
1294 int steps, step;
1295
1296 step = pdf_undoredo_state(ctx, doc, &steps);
1297
1298 return step > 0;
1299 }
1300
1301 int pdf_can_redo(fz_context *ctx, pdf_document *doc)
1302 {
1303 int steps, step;
1304
1305 step = pdf_undoredo_state(ctx, doc, &steps);
1306
1307 return step != steps;
1308 }
1309
1310 /* Call this to find the title of the operation within the undo state. */
1311 const char *pdf_undoredo_step(fz_context *ctx, pdf_document *doc, int step)
1312 {
1313 pdf_journal_entry *entry;
1314
1315 if (ctx == NULL || doc == NULL || doc->journal == NULL)
1316 return NULL;
1317
1318 if (doc->journal->pending != NULL || doc->journal->nesting > 0)
1319 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Can't undo/redo during an operation");
1320
1321 for (entry = doc->journal->head; step > 0 && entry != NULL; step--, entry = entry->next);
1322
1323 if (step != 0 || entry == NULL)
1324 return NULL;
1325
1326 return entry->title;
1327 }
1328
1329 static void
1330 swap_fragments(fz_context *ctx, pdf_document *doc, pdf_journal_entry *entry)
1331 {
1332 pdf_journal_fragment *frag;
1333
1334 #ifdef PDF_DEBUG_JOURNAL
1335 entry->changed_since_last_dumped = 1;
1336 #endif
1337 if (doc->local_xref_nesting != 0)
1338 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Can't undo/redo within an operation");
1339
1340 pdf_drop_local_xref_and_resources(ctx, doc);
1341
1342 for (frag = entry->head; frag != NULL; frag = frag->next)
1343 {
1344 pdf_xref_entry *xre;
1345 pdf_obj *old;
1346 fz_buffer *obuf;
1347 int type;
1348 xre = pdf_get_incremental_xref_entry(ctx, doc, frag->obj_num);
1349 old = xre->obj;
1350 obuf = xre->stm_buf;
1351 xre->obj = frag->inactive;
1352 type = xre->type;
1353 xre->type = frag->newobj ? 0 : 'o';
1354 frag->newobj = type == 0;
1355 xre->stm_buf = frag->stream;
1356 frag->inactive = old;
1357 frag->stream = obuf;
1358 }
1359 }
1360
1361 /* Abandon an operation - unwind back to the previous begin. */
1362 void pdf_abandon_operation(fz_context *ctx, pdf_document *doc)
1363 {
1364 pdf_journal_entry *entry;
1365
1366 if (ctx == NULL || doc == NULL || doc->journal == NULL)
1367 return;
1368
1369 if (doc->journal->nesting == 0)
1370 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Can't abandon a non-existent operation!");
1371
1372 doc->journal->nesting--;
1373
1374 entry = doc->journal->pending_tail;
1375 assert(entry);
1376
1377 /* Undo the changes we are about the discard. */
1378 swap_fragments(ctx, doc, entry);
1379
1380 /* And discard entry. */
1381 if (entry->prev == NULL)
1382 {
1383 doc->journal->pending = NULL;
1384 doc->journal->pending_tail = NULL;
1385 }
1386 else
1387 {
1388 doc->journal->pending_tail = entry->prev;
1389 entry->prev->next = NULL;
1390 entry->prev = NULL;
1391 }
1392 #ifdef PDF_DEBUG_JOURNAL
1393 fz_write_printf(ctx, fz_stddbg(ctx), "Abandoning!\n");
1394 #endif
1395 discard_journal_entries(ctx, &entry);
1396 }
1397
1398 /* Move backwards in the undo history. Throws an error if we are at the
1399 * start. Any edits to the document at this point will discard all
1400 * subsequent history. */
1401 void pdf_undo(fz_context *ctx, pdf_document *doc)
1402 {
1403 pdf_journal_entry *entry;
1404 pdf_journal_fragment *frag;
1405
1406 if (ctx == NULL || doc == NULL)
1407 return;
1408
1409 if (doc->journal == NULL)
1410 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot undo on unjournaled PDF");
1411
1412 if (doc->journal->nesting != 0)
1413 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Can't undo during an operation!");
1414
1415 entry = doc->journal->current;
1416 if (entry == NULL)
1417 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Already at start of history");
1418
1419 #ifdef PDF_DEBUG_JOURNAL
1420 fz_write_printf(ctx, fz_stddbg(ctx), "Undo!\n");
1421 #endif
1422
1423 doc->journal->current = entry->prev;
1424
1425 swap_fragments(ctx, doc, entry);
1426
1427 // nuke all caches
1428 pdf_drop_page_tree_internal(ctx, doc);
1429 pdf_sync_open_pages(ctx, doc);
1430 for (frag = entry->head; frag; frag = frag->next)
1431 pdf_purge_object_from_store(ctx, doc, frag->obj_num);
1432 }
1433
1434 /* Move forwards in the undo history. Throws an error if we are at the
1435 * end. */
1436 void pdf_redo(fz_context *ctx, pdf_document *doc)
1437 {
1438 pdf_journal_entry *entry;
1439 pdf_journal_fragment *frag;
1440
1441 if (ctx == NULL || doc == NULL)
1442 return;
1443
1444 if (doc->journal == NULL)
1445 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Cannot redo on unjournaled PDF");
1446
1447 if (doc->journal->nesting != 0)
1448 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Can't redo during an operation!");
1449
1450 #ifdef PDF_DEBUG_JOURNAL
1451 fz_write_printf(ctx, fz_stddbg(ctx), "Redo!\n");
1452 #endif
1453
1454 entry = doc->journal->current;
1455 if (entry == NULL)
1456 {
1457 /* If journal->current is null then everything has been undone. */
1458 /* Go to the first change in journal->head if it exists. */
1459 entry = doc->journal->head;
1460 }
1461 else
1462 {
1463 entry = entry->next;
1464 }
1465
1466 if (entry == NULL)
1467 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Already at end of history");
1468
1469 // nuke all caches
1470 pdf_drop_page_tree_internal(ctx, doc);
1471 pdf_sync_open_pages(ctx, doc);
1472 for (frag = entry->head; frag; frag = frag->next)
1473 pdf_purge_object_from_store(ctx, doc, frag->obj_num);
1474
1475 doc->journal->current = entry;
1476
1477 swap_fragments(ctx, doc, entry);
1478 }
1479
1480 void pdf_discard_journal(fz_context *ctx, pdf_journal *journal)
1481 {
1482 if (ctx == NULL || journal == NULL)
1483 return;
1484
1485 discard_journal_entries(ctx, &journal->head);
1486 /* Should be NULL, but belt and braces. */
1487 discard_journal_entries(ctx, &journal->pending);
1488 journal->head = NULL;
1489 journal->current = NULL;
1490 journal->pending = NULL;
1491 journal->pending_tail = NULL;
1492 }
1493
1494 static void
1495 pdf_fingerprint_file(fz_context *ctx, pdf_document *doc, unsigned char digest[16], int i)
1496 {
1497 fz_md5 state;
1498
1499 fz_md5_init(&state);
1500 fz_md5_update_int64(&state, doc->num_xref_sections-i);
1501 for (; i < doc->num_xref_sections; i++)
1502 {
1503 pdf_xref_subsec *subsec = doc->xref_sections[i].subsec;
1504 fz_md5_update_int64(&state, doc->xref_sections[i].num_objects);
1505 while (subsec)
1506 {
1507 fz_md5_update_int64(&state, subsec->start);
1508 fz_md5_update_int64(&state, subsec->len);
1509 subsec = subsec->next;
1510 }
1511 }
1512 fz_md5_final(&state, digest);
1513 }
1514
1515 void
1516 pdf_serialise_journal(fz_context *ctx, pdf_document *doc, fz_output *out)
1517 {
1518 pdf_journal_entry *entry;
1519 int currentpos = 0;
1520 unsigned char digest[16];
1521 int i;
1522 int nis = doc->num_incremental_sections;
1523
1524 pdf_fingerprint_file(ctx, doc, digest, nis);
1525
1526 if (!pdf_has_unsaved_changes(ctx, doc))
1527 nis = 0;
1528
1529 fz_write_printf(ctx, out, "%!MuPDF-Journal-100\n");
1530 fz_write_string(ctx, out, "\njournal\n<<\n");
1531 fz_write_printf(ctx, out, "/NumSections %d\n", nis);
1532 fz_write_printf(ctx, out, "/FileSize %ld\n", doc->file_size);
1533 fz_write_printf(ctx, out, "/Fingerprint <");
1534 for (i = 0; i < 16; i++)
1535 fz_write_printf(ctx, out, "%02x", digest[i]);
1536 fz_write_printf(ctx, out, ">\n");
1537
1538 if (doc->journal->current != NULL)
1539 for (entry = doc->journal->head; entry != NULL; entry = entry->next)
1540 {
1541 currentpos++;
1542 if (entry == doc->journal->current)
1543 break;
1544 }
1545 fz_write_printf(ctx, out, "/HistoryPos %d\n", currentpos);
1546 fz_write_string(ctx, out, ">>\n");
1547
1548 for (entry = doc->journal->head; entry != NULL; entry = entry->next)
1549 {
1550 pdf_journal_fragment *frag;
1551 fz_write_printf(ctx, out, "entry\n%(\n", entry->title);
1552 for (frag = entry->head; frag != NULL; frag = frag->next)
1553 {
1554 if (frag->newobj)
1555 {
1556 fz_write_printf(ctx, out, "%d 0 newobj\n", frag->obj_num);
1557 continue;
1558 }
1559 fz_write_printf(ctx, out, "%d 0 obj\n", frag->obj_num);
1560 pdf_print_encrypted_obj(ctx, out, frag->inactive, 1, 0, NULL, frag->obj_num, 0, NULL);
1561 if (frag->stream)
1562 {
1563 fz_write_printf(ctx, out, "\nstream\n");
1564 fz_write_data(ctx, out, frag->stream->data, frag->stream->len);
1565 fz_write_string(ctx, out, "\nendstream");
1566 }
1567 fz_write_string(ctx, out, "\nendobj\n");
1568 }
1569 }
1570 fz_write_printf(ctx, out, "endjournal\n");
1571 }
1572
1573 void
1574 pdf_add_journal_fragment(fz_context *ctx, pdf_document *doc, int parent, pdf_obj *copy, fz_buffer *copy_stream, int newobj)
1575 {
1576 pdf_journal_entry *entry;
1577 pdf_journal_fragment *frag;
1578
1579 if (doc->journal == NULL)
1580 return;
1581
1582 entry = doc->journal->pending_tail;
1583 /* We must be in an operation. */
1584 assert(entry != NULL);
1585 if (entry == NULL)
1586 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Can't add a journal fragment absent an operation");
1587
1588 /* This should never happen, as we should always be appending to the end of
1589 * the pending list. */
1590 assert(entry->next == NULL);
1591 if (entry->next)
1592 {
1593 discard_journal_entries(ctx, &entry->next);
1594 doc->journal->pending_tail = NULL;
1595 }
1596
1597 #ifdef PDF_DEBUG_JOURNAL
1598 entry->changed_since_last_dumped = 1;
1599 #endif
1600
1601 fz_try(ctx)
1602 {
1603 frag = fz_malloc_struct(ctx, pdf_journal_fragment);
1604 frag->obj_num = parent;
1605 if (entry->tail == NULL)
1606 {
1607 frag->prev = NULL;
1608 entry->head = frag;
1609 }
1610 else
1611 {
1612 frag->prev = entry->tail;
1613 entry->tail->next = frag;
1614 }
1615 entry->tail = frag;
1616 frag->newobj = newobj;
1617 frag->inactive = copy;
1618 frag->stream = copy_stream;
1619 }
1620 fz_catch(ctx)
1621 fz_rethrow(ctx);
1622 }
1623
1624 void pdf_deserialise_journal(fz_context *ctx, pdf_document *doc, fz_stream *stm)
1625 {
1626 int num, version, c, nis, pos;
1627 pdf_obj *obj = NULL, *fingerprint_obj;
1628 fz_buffer *buffer;
1629 unsigned char digest[16];
1630 int64_t file_size;
1631 int digests_match = 0;
1632 pdf_token tok;
1633
1634 if (!doc || !stm)
1635 return;
1636
1637 if (doc->journal)
1638 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Can't load a journal over another one");
1639
1640 if (fz_skip_string(ctx, stm, "%!MuPDF-Journal-"))
1641 fz_throw(ctx, FZ_ERROR_FORMAT, "Bad journal format");
1642
1643 fz_var(obj);
1644 fz_var(digests_match);
1645
1646 fz_try(ctx)
1647 {
1648 version = 0;
1649 while (1)
1650 {
1651 c = fz_peek_byte(ctx, stm);
1652 if (c < '0' || c > '9')
1653 break;
1654 version = (version*10) + c - '0';
1655 (void)fz_read_byte(ctx, stm);
1656 }
1657 if (version != 100)
1658 fz_throw(ctx, FZ_ERROR_FORMAT, "Bad journal format");
1659
1660 fz_skip_space(ctx, stm);
1661 if (fz_skip_string(ctx, stm, "journal\n"))
1662 fz_throw(ctx, FZ_ERROR_FORMAT, "Bad journal format");
1663
1664 tok = pdf_lex(ctx, stm, &doc->lexbuf.base);
1665 if (tok != PDF_TOK_OPEN_DICT)
1666 fz_throw(ctx, FZ_ERROR_FORMAT, "Bad journal format");
1667 obj = pdf_parse_dict(ctx, doc, stm, &doc->lexbuf.base);
1668
1669 nis = pdf_dict_get_int(ctx, obj, PDF_NAME(NumSections));
1670 if (nis < 0 || nis > doc->num_xref_sections)
1671 fz_throw(ctx, FZ_ERROR_FORMAT, "Bad journal format");
1672 pdf_fingerprint_file(ctx, doc, digest, nis);
1673
1674 file_size = pdf_dict_get_int(ctx, obj, PDF_NAME(FileSize));
1675
1676 fingerprint_obj = pdf_dict_get(ctx, obj, PDF_NAME(Fingerprint));
1677 if (pdf_to_str_len(ctx, fingerprint_obj) != 16)
1678 fz_throw(ctx, FZ_ERROR_FORMAT, "Bad journal fingerprint");
1679
1680 digests_match = (memcmp(pdf_to_str_buf(ctx, fingerprint_obj), digest, 16) == 0);
1681
1682 pos = pdf_dict_get_int(ctx, obj, PDF_NAME(HistoryPos));
1683 }
1684 fz_always(ctx)
1685 {
1686 pdf_drop_obj(ctx, obj);
1687 }
1688 fz_catch(ctx)
1689 {
1690 fz_rethrow(ctx);
1691 }
1692
1693 if (!digests_match)
1694 return;
1695
1696 if (doc->file_size < file_size)
1697 return;
1698
1699 doc->journal = fz_malloc_struct(ctx, pdf_journal);
1700
1701 while (1)
1702 {
1703 int newobj;
1704 fz_skip_space(ctx, stm);
1705
1706 if (fz_skip_string(ctx, stm, "entry\n") == 0)
1707 {
1708 /* Read the fragment title. */
1709 char *title;
1710 tok = pdf_lex(ctx, stm, &doc->lexbuf.base);
1711
1712 if (tok != PDF_TOK_STRING)
1713 fz_throw(ctx, FZ_ERROR_FORMAT, "Bad string in journal");
1714 title = fz_malloc(ctx, doc->lexbuf.base.len+1);
1715 memcpy(title, doc->lexbuf.base.buffer, doc->lexbuf.base.len);
1716 title[doc->lexbuf.base.len] = 0;
1717
1718 new_entry(ctx, doc, title);
1719 continue;
1720 }
1721 if (fz_skip_string(ctx, stm, /*en*/"djournal") == 0)
1722 break;
1723
1724 if (doc->journal->current == NULL)
1725 fz_throw(ctx, FZ_ERROR_FORMAT, "Badly formed journal");
1726
1727 /* Read the object/stream for the next fragment. */
1728 obj = pdf_parse_journal_obj(ctx, doc, stm, &num, &buffer, &newobj);
1729
1730 pdf_add_journal_fragment(ctx, doc, num, obj, buffer, newobj);
1731 }
1732
1733 fz_skip_space(ctx, stm);
1734
1735 doc->journal->current = NULL;
1736 if (pos > 0)
1737 {
1738 if (doc->journal->head == NULL)
1739 fz_throw(ctx, FZ_ERROR_FORMAT, "Badly formed journal");
1740
1741 doc->journal->current = doc->journal->head;
1742 while (--pos)
1743 {
1744 doc->journal->current = doc->journal->current->next;
1745 if (doc->journal->current == NULL)
1746 break;
1747 }
1748 }
1749
1750 doc->file_size = file_size;
1751 /* We're about to make the last xref an incremental one. All incremental
1752 * ones MUST be solid, but the snapshot might not have saved it as such,
1753 * so solidify it now. */
1754 pdf_ensure_solid_xref(ctx, doc, pdf_xref_len(ctx, doc));
1755 doc->num_incremental_sections = nis;
1756
1757 if (nis > 0)
1758 {
1759 /* Ditch the trailer object out of the xref. Keep the direct
1760 * trailer reference. */
1761 pdf_delete_object(ctx, doc, pdf_obj_parent_num(ctx, doc->xref_sections[0].trailer));
1762 pdf_set_obj_parent(ctx, doc->xref_sections[0].trailer, 0);
1763 }
1764 }
1765
1766 static void prepare_object_for_alteration(fz_context *ctx, pdf_obj *obj, pdf_obj *val)
1767 {
1768 pdf_document *doc, *val_doc;
1769 int parent;
1770 pdf_journal_fragment *frag;
1771 pdf_journal_entry *entry;
1772 pdf_obj *copy = NULL;
1773 pdf_obj *orig;
1774 fz_buffer *copy_stream = NULL;
1775 int was_empty;
1776
1777 /*
1778 obj should be a dict or an array. We don't care about
1779 any other types, as they aren't 'containers'.
1780 */
1781 if (obj < PDF_LIMIT)
1782 return;
1783
1784 switch (obj->kind)
1785 {
1786 case PDF_DICT:
1787 doc = DICT(obj)->doc;
1788 parent = DICT(obj)->parent_num;
1789 break;
1790 case PDF_ARRAY:
1791 doc = ARRAY(obj)->doc;
1792 parent = ARRAY(obj)->parent_num;
1793 break;
1794 default:
1795 return;
1796 }
1797
1798 assert(doc != NULL);
1799
1800 /* Do we need to drop the page maps? */
1801 if (doc->rev_page_map || doc->fwd_page_map)
1802 {
1803 if (doc->non_structural_change)
1804 {
1805 /* No need to drop the reverse page map on a non-structural change. */
1806 }
1807 else if (parent == 0)
1808 {
1809 /* This object isn't linked into the document - can't change the
1810 * pagemap. */
1811 }
1812 else if (doc->local_xref && doc->local_xref_nesting > 0)
1813 {
1814 /* We have a local_xref and it's in force. By convention, we
1815 * never do structural changes in local_xrefs. */
1816 }
1817 else
1818 pdf_drop_page_tree_internal(ctx, doc);
1819 }
1820
1821 if (val)
1822 {
1823 val_doc = pdf_get_bound_document(ctx, val);
1824 if (val_doc && val_doc != doc)
1825 fz_throw(ctx, FZ_ERROR_ARGUMENT, "container and item belong to different documents");
1826 }
1827
1828 /*
1829 The newly linked object needs to record the parent_num.
1830 */
1831 if (parent != 0)
1832 pdf_set_obj_parent(ctx, val, parent);
1833
1834 /*
1835 parent_num == 0 while an object is being parsed from the file.
1836 No further action is necessary.
1837 */
1838 if (parent == 0 || doc->save_in_progress || doc->repair_in_progress)
1839 return;
1840
1841 if (doc->journal && doc->journal->nesting == 0)
1842 fz_throw(ctx, FZ_ERROR_ARGUMENT, "Can't alter an object other than in an operation");
1843
1844 if (doc->local_xref)
1845 {
1846 /* We have a local_xref. If it's in force, then we're
1847 * ready for alteration already. */
1848 if (doc->local_xref_nesting > 0)
1849 {
1850 pdf_xref_ensure_local_object(ctx, doc, parent);
1851 return;
1852 }
1853 else
1854 {
1855 /* The local xref isn't in force, and we're about
1856 * to edit the document. This invalidates it, so
1857 * throw it away. */
1858 pdf_drop_local_xref_and_resources(ctx, doc);
1859 }
1860 }
1861
1862 // Empty store of items keyed on the object being changed.
1863 if (parent != 0)
1864 pdf_purge_object_from_store(ctx, doc, parent);
1865
1866 entry = NULL;
1867 if (doc->journal)
1868 {
1869 /* We are about to add a fragment. Everything after 'current' in the
1870 * history must be thrown away. If current is NULL, then *everything*
1871 * must be thrown away. */
1872 discard_journal_entries(ctx, doc->journal->current ? &doc->journal->current->next : &doc->journal->head);
1873
1874 /* We should be collating into a pending block. */
1875 entry = doc->journal->pending_tail;
1876 assert(entry);
1877
1878 /* If we've already stashed a value for this object in this fragment,
1879 * we don't need to stash another one. It'll only confuse us later. */
1880 for (frag = entry->head; frag != NULL; frag = frag->next)
1881 if (frag->obj_num == parent)
1882 {
1883 entry = NULL;
1884 break; /* Already stashed this one! */
1885 }
1886 }
1887
1888 /*
1889 We need to ensure that the containing hierarchy of objects
1890 has been moved to the incremental xref section.
1891 */
1892 was_empty = pdf_xref_ensure_incremental_object(ctx, doc, parent);
1893
1894 /* If we're not journalling, or we've already stashed an 'old' value for this
1895 * object, just exit now. */
1896 if (entry == NULL)
1897 return;
1898
1899 /* Load the 'old' value and store it in a fragment. */
1900 orig = pdf_load_object(ctx, doc, parent);
1901
1902 fz_var(copy);
1903 fz_var(copy_stream);
1904
1905 fz_try(ctx)
1906 {
1907 if (was_empty)
1908 {
1909 /* was_empty = 1 iff, the the entry in the incremental xref was empty,
1910 * and we copied any older value for that object forwards from an old xref.
1911 * When we undo, we just want to blank the one in the incremental section.
1912 * Effectively this is a "new object". */
1913 copy = NULL;
1914 copy_stream = NULL;
1915 }
1916 else
1917 {
1918 copy = pdf_deep_copy_obj(ctx, orig);
1919 pdf_set_obj_parent(ctx, copy, parent);
1920 if (pdf_obj_num_is_stream(ctx, doc, parent))
1921 copy_stream = pdf_load_raw_stream_number(ctx, doc, parent);
1922 }
1923 pdf_add_journal_fragment(ctx, doc, parent, copy, copy_stream, was_empty);
1924 }
1925 fz_always(ctx)
1926 {
1927 pdf_drop_obj(ctx, orig);
1928 }
1929 fz_catch(ctx)
1930 {
1931 fz_drop_buffer(ctx, copy_stream);
1932 pdf_drop_obj(ctx, copy);
1933 fz_rethrow(ctx);
1934 }
1935 }
1936
1937 void
1938 pdf_array_put(fz_context *ctx, pdf_obj *obj, int i, pdf_obj *item)
1939 {
1940 RESOLVE(obj);
1941 if (!OBJ_IS_ARRAY(obj))
1942 fz_throw(ctx, FZ_ERROR_ARGUMENT, "not an array (%s)", pdf_objkindstr(obj));
1943 if (i == ARRAY(obj)->len)
1944 {
1945 pdf_array_push(ctx, obj, item);
1946 return;
1947 }
1948 if (i < 0 || i > ARRAY(obj)->len)
1949 fz_throw(ctx, FZ_ERROR_ARGUMENT, "index out of bounds");
1950 prepare_object_for_alteration(ctx, obj, item);
1951 pdf_drop_obj(ctx, ARRAY(obj)->items[i]);
1952 ARRAY(obj)->items[i] = pdf_keep_obj(ctx, item);
1953 }
1954
1955 void
1956 pdf_array_put_drop(fz_context *ctx, pdf_obj *obj, int i, pdf_obj *item)
1957 {
1958 fz_try(ctx)
1959 pdf_array_put(ctx, obj, i, item);
1960 fz_always(ctx)
1961 pdf_drop_obj(ctx, item);
1962 fz_catch(ctx)
1963 fz_rethrow(ctx);
1964 }
1965
1966 void
1967 pdf_array_push(fz_context *ctx, pdf_obj *obj, pdf_obj *item)
1968 {
1969 RESOLVE(obj);
1970 if (!OBJ_IS_ARRAY(obj))
1971 fz_throw(ctx, FZ_ERROR_ARGUMENT, "not an array (%s)", pdf_objkindstr(obj));
1972 prepare_object_for_alteration(ctx, obj, item);
1973 if (ARRAY(obj)->len + 1 > ARRAY(obj)->cap)
1974 pdf_array_grow(ctx, ARRAY(obj));
1975 ARRAY(obj)->items[ARRAY(obj)->len] = pdf_keep_obj(ctx, item);
1976 ARRAY(obj)->len++;
1977 }
1978
1979 void
1980 pdf_array_push_drop(fz_context *ctx, pdf_obj *obj, pdf_obj *item)
1981 {
1982 fz_try(ctx)
1983 pdf_array_push(ctx, obj, item);
1984 fz_always(ctx)
1985 pdf_drop_obj(ctx, item);
1986 fz_catch(ctx)
1987 fz_rethrow(ctx);
1988 }
1989
1990 void
1991 pdf_array_insert(fz_context *ctx, pdf_obj *obj, pdf_obj *item, int i)
1992 {
1993 RESOLVE(obj);
1994 if (!OBJ_IS_ARRAY(obj))
1995 fz_throw(ctx, FZ_ERROR_ARGUMENT, "not an array (%s)", pdf_objkindstr(obj));
1996 if (i < 0 || i > ARRAY(obj)->len)
1997 fz_throw(ctx, FZ_ERROR_ARGUMENT, "index out of bounds");
1998 prepare_object_for_alteration(ctx, obj, item);
1999 if (ARRAY(obj)->len + 1 > ARRAY(obj)->cap)
2000 pdf_array_grow(ctx, ARRAY(obj));
2001 memmove(ARRAY(obj)->items + i + 1, ARRAY(obj)->items + i, (ARRAY(obj)->len - i) * sizeof(pdf_obj*));
2002 ARRAY(obj)->items[i] = pdf_keep_obj(ctx, item);
2003 ARRAY(obj)->len++;
2004 }
2005
2006 void
2007 pdf_array_insert_drop(fz_context *ctx, pdf_obj *obj, pdf_obj *item, int i)
2008 {
2009 fz_try(ctx)
2010 pdf_array_insert(ctx, obj, item, i);
2011 fz_always(ctx)
2012 pdf_drop_obj(ctx, item);
2013 fz_catch(ctx)
2014 fz_rethrow(ctx);
2015 }
2016
2017 void
2018 pdf_array_delete(fz_context *ctx, pdf_obj *obj, int i)
2019 {
2020 RESOLVE(obj);
2021 if (!OBJ_IS_ARRAY(obj))
2022 fz_throw(ctx, FZ_ERROR_ARGUMENT, "not an array (%s)", pdf_objkindstr(obj));
2023 if (i < 0 || i >= ARRAY(obj)->len)
2024 fz_throw(ctx, FZ_ERROR_ARGUMENT, "index out of bounds");
2025 prepare_object_for_alteration(ctx, obj, NULL);
2026 pdf_drop_obj(ctx, ARRAY(obj)->items[i]);
2027 ARRAY(obj)->items[i] = 0;
2028 ARRAY(obj)->len--;
2029 memmove(ARRAY(obj)->items + i, ARRAY(obj)->items + i + 1, (ARRAY(obj)->len - i) * sizeof(pdf_obj*));
2030 }
2031
2032 int
2033 pdf_array_contains(fz_context *ctx, pdf_obj *arr, pdf_obj *obj)
2034 {
2035 int i, len;
2036
2037 len = pdf_array_len(ctx, arr);
2038 for (i = 0; i < len; i++)
2039 if (!pdf_objcmp(ctx, pdf_array_get(ctx, arr, i), obj))
2040 return 1;
2041
2042 return 0;
2043 }
2044
2045 int
2046 pdf_array_find(fz_context *ctx, pdf_obj *arr, pdf_obj *obj)
2047 {
2048 int i, len;
2049
2050 len = pdf_array_len(ctx, arr);
2051 for (i = 0; i < len; i++)
2052 if (!pdf_objcmp(ctx, pdf_array_get(ctx, arr, i), obj))
2053 return i;
2054
2055 return -1;
2056 }
2057
2058 pdf_obj *pdf_new_point(fz_context *ctx, pdf_document *doc, fz_point point)
2059 {
2060 pdf_obj *arr = pdf_new_array(ctx, doc, 2);
2061 fz_try(ctx)
2062 {
2063 pdf_array_push_real(ctx, arr, point.x);
2064 pdf_array_push_real(ctx, arr, point.y);
2065 }
2066 fz_catch(ctx)
2067 {
2068 pdf_drop_obj(ctx, arr);
2069 fz_rethrow(ctx);
2070 }
2071 return arr;
2072 }
2073
2074 pdf_obj *pdf_new_rect(fz_context *ctx, pdf_document *doc, fz_rect rect)
2075 {
2076 pdf_obj *arr = pdf_new_array(ctx, doc, 4);
2077 fz_try(ctx)
2078 {
2079 pdf_array_push_real(ctx, arr, rect.x0);
2080 pdf_array_push_real(ctx, arr, rect.y0);
2081 pdf_array_push_real(ctx, arr, rect.x1);
2082 pdf_array_push_real(ctx, arr, rect.y1);
2083 }
2084 fz_catch(ctx)
2085 {
2086 pdf_drop_obj(ctx, arr);
2087 fz_rethrow(ctx);
2088 }
2089 return arr;
2090 }
2091
2092 pdf_obj *pdf_new_matrix(fz_context *ctx, pdf_document *doc, fz_matrix mtx)
2093 {
2094 pdf_obj *arr = pdf_new_array(ctx, doc, 6);
2095 fz_try(ctx)
2096 {
2097 pdf_array_push_real(ctx, arr, mtx.a);
2098 pdf_array_push_real(ctx, arr, mtx.b);
2099 pdf_array_push_real(ctx, arr, mtx.c);
2100 pdf_array_push_real(ctx, arr, mtx.d);
2101 pdf_array_push_real(ctx, arr, mtx.e);
2102 pdf_array_push_real(ctx, arr, mtx.f);
2103 }
2104 fz_catch(ctx)
2105 {
2106 pdf_drop_obj(ctx, arr);
2107 fz_rethrow(ctx);
2108 }
2109 return arr;
2110 }
2111
2112
2113 pdf_obj *pdf_new_date(fz_context *ctx, pdf_document *doc, int64_t time)
2114 {
2115 char s[40];
2116 if (!pdf_format_date(ctx, time, s, nelem(s)))
2117 return NULL;
2118 return pdf_new_string(ctx, s, strlen(s));
2119 }
2120
2121 /* dicts may only have names as keys! */
2122
2123 static int keyvalcmp(const void *ap, const void *bp)
2124 {
2125 const struct keyval *a = ap;
2126 const struct keyval *b = bp;
2127 const char *an;
2128 const char *bn;
2129
2130 /* We should never get a->k == NULL or b->k == NULL. If we
2131 * do, then they match. */
2132 if (a->k < PDF_LIMIT)
2133 an = PDF_NAME_LIST[(intptr_t)a->k];
2134 else if (a->k >= PDF_LIMIT && a->k->kind == PDF_NAME)
2135 an = NAME(a->k)->n;
2136 else
2137 return 0;
2138
2139 if (b->k < PDF_LIMIT)
2140 bn = PDF_NAME_LIST[(intptr_t)b->k];
2141 else if (b->k >= PDF_LIMIT && b->k->kind == PDF_NAME)
2142 bn = NAME(b->k)->n;
2143 else
2144 return 0;
2145
2146 return strcmp(an, bn);
2147 }
2148
2149 pdf_obj *
2150 pdf_new_dict(fz_context *ctx, pdf_document *doc, int initialcap)
2151 {
2152 pdf_obj_dict *obj;
2153 int i;
2154
2155 if (doc == NULL)
2156 fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot create dictionary without a document");
2157
2158 obj = Memento_label(fz_malloc(ctx, sizeof(pdf_obj_dict)), "pdf_obj(dict)");
2159 obj->super.refs = 1;
2160 obj->super.kind = PDF_DICT;
2161 obj->super.flags = 0;
2162 obj->doc = doc;
2163 obj->parent_num = 0;
2164
2165 obj->len = 0;
2166 obj->cap = initialcap > 1 ? initialcap : 10;
2167
2168 fz_try(ctx)
2169 {
2170 DICT(obj)->items = Memento_label(fz_malloc_array(ctx, DICT(obj)->cap, struct keyval), "dict_items");
2171 }
2172 fz_catch(ctx)
2173 {
2174 fz_free(ctx, obj);
2175 fz_rethrow(ctx);
2176 }
2177 for (i = 0; i < DICT(obj)->cap; i++)
2178 {
2179 DICT(obj)->items[i].k = NULL;
2180 DICT(obj)->items[i].v = NULL;
2181 }
2182
2183 return &obj->super;
2184 }
2185
2186 static void
2187 pdf_dict_grow(fz_context *ctx, pdf_obj *obj)
2188 {
2189 int i;
2190 int new_cap = (DICT(obj)->cap * 3) / 2;
2191
2192 DICT(obj)->items = fz_realloc_array(ctx, DICT(obj)->items, new_cap, struct keyval);
2193 DICT(obj)->cap = new_cap;
2194
2195 for (i = DICT(obj)->len; i < DICT(obj)->cap; i++)
2196 {
2197 DICT(obj)->items[i].k = NULL;
2198 DICT(obj)->items[i].v = NULL;
2199 }
2200 }
2201
2202 pdf_obj *
2203 pdf_copy_dict(fz_context *ctx, pdf_obj *obj)
2204 {
2205 pdf_document *doc;
2206 pdf_obj *dict;
2207 int i, n;
2208
2209 RESOLVE(obj);
2210 if (!OBJ_IS_DICT(obj))
2211 fz_throw(ctx, FZ_ERROR_ARGUMENT, "not a dict (%s)", pdf_objkindstr(obj));
2212
2213 doc = DICT(obj)->doc;
2214 n = pdf_dict_len(ctx, obj);
2215 dict = pdf_new_dict(ctx, doc, n);
2216 fz_try(ctx)
2217 for (i = 0; i < n; i++)
2218 pdf_dict_put(ctx, dict, pdf_dict_get_key(ctx, obj, i), pdf_dict_get_val(ctx, obj, i));
2219 fz_catch(ctx)
2220 {
2221 pdf_drop_obj(ctx, dict);
2222 fz_rethrow(ctx);
2223 }
2224
2225 return dict;
2226 }
2227
2228 int
2229 pdf_dict_len(fz_context *ctx, pdf_obj *obj)
2230 {
2231 RESOLVE(obj);
2232 if (!OBJ_IS_DICT(obj))
2233 return 0;
2234 return DICT(obj)->len;
2235 }
2236
2237 pdf_obj *
2238 pdf_dict_get_key(fz_context *ctx, pdf_obj *obj, int i)
2239 {
2240 RESOLVE(obj);
2241 if (!OBJ_IS_DICT(obj))
2242 return NULL;
2243 if (i < 0 || i >= DICT(obj)->len)
2244 return NULL;
2245 return DICT(obj)->items[i].k;
2246 }
2247
2248 pdf_obj *
2249 pdf_dict_get_val(fz_context *ctx, pdf_obj *obj, int i)
2250 {
2251 RESOLVE(obj);
2252 if (!OBJ_IS_DICT(obj))
2253 return NULL;
2254 if (i < 0 || i >= DICT(obj)->len)
2255 return NULL;
2256 return DICT(obj)->items[i].v;
2257 }
2258
2259 void
2260 pdf_dict_put_val_null(fz_context *ctx, pdf_obj *obj, int idx)
2261 {
2262 RESOLVE(obj);
2263 if (!OBJ_IS_DICT(obj))
2264 fz_throw(ctx, FZ_ERROR_ARGUMENT, "not a dict (%s)", pdf_objkindstr(obj));
2265 if (idx < 0 || idx >= DICT(obj)->len)
2266 fz_throw(ctx, FZ_ERROR_ARGUMENT, "index out of bounds");
2267
2268 prepare_object_for_alteration(ctx, obj, NULL);
2269 pdf_drop_obj(ctx, DICT(obj)->items[idx].v);
2270 DICT(obj)->items[idx].v = PDF_NULL;
2271 }
2272
2273 /* Returns 0 <= i < len for key found. Returns -1-len < i <= -1 for key
2274 * not found, but with insertion point -1-i. */
2275 static int
2276 pdf_dict_finds(fz_context *ctx, pdf_obj *obj, const char *key)
2277 {
2278 int len = DICT(obj)->len;
2279 if ((obj->flags & PDF_FLAGS_SORTED) && len > 0)
2280 {
2281 int l = 0;
2282 int r = len - 1;
2283
2284 if (strcmp(pdf_to_name(ctx, DICT(obj)->items[r].k), key) < 0)
2285 {
2286 return -1 - (r+1);
2287 }
2288
2289 while (l <= r)
2290 {
2291 int m = (l + r) >> 1;
2292 int c = -strcmp(pdf_to_name(ctx, DICT(obj)->items[m].k), key);
2293 if (c < 0)
2294 r = m - 1;
2295 else if (c > 0)
2296 l = m + 1;
2297 else
2298 return m;
2299 }
2300 return -1 - l;
2301 }
2302
2303 else
2304 {
2305 int i;
2306 for (i = 0; i < len; i++)
2307 if (strcmp(pdf_to_name(ctx, DICT(obj)->items[i].k), key) == 0)
2308 return i;
2309
2310 return -1 - len;
2311 }
2312 }
2313
2314 static int
2315 pdf_dict_find(fz_context *ctx, pdf_obj *obj, pdf_obj *key)
2316 {
2317 int len = DICT(obj)->len;
2318 if ((obj->flags & PDF_FLAGS_SORTED) && len > 0)
2319 {
2320 int l = 0;
2321 int r = len - 1;
2322 pdf_obj *k = DICT(obj)->items[r].k;
2323
2324 if (k == key || (k >= PDF_LIMIT && strcmp(NAME(k)->n, PDF_NAME_LIST[(intptr_t)key]) < 0))
2325 {
2326 return -1 - (r+1);
2327 }
2328
2329 while (l <= r)
2330 {
2331 int m = (l + r) >> 1;
2332 int c;
2333
2334 k = DICT(obj)->items[m].k;
2335 c = (k < PDF_LIMIT ? (char *)key-(char *)k : -strcmp(NAME(k)->n, PDF_NAME_LIST[(intptr_t)key]));
2336 if (c < 0)
2337 r = m - 1;
2338 else if (c > 0)
2339 l = m + 1;
2340 else
2341 return m;
2342 }
2343 return -1 - l;
2344 }
2345 else
2346 {
2347 int i;
2348 for (i = 0; i < len; i++)
2349 {
2350 pdf_obj *k = DICT(obj)->items[i].k;
2351 if (k < PDF_LIMIT)
2352 {
2353 if (k == key)
2354 return i;
2355 }
2356 else
2357 {
2358 if (!strcmp(PDF_NAME_LIST[(intptr_t)key], NAME(k)->n))
2359 return i;
2360 }
2361 }
2362
2363 return -1 - len;
2364 }
2365 }
2366
2367 pdf_obj *
2368 pdf_dict_gets(fz_context *ctx, pdf_obj *obj, const char *key)
2369 {
2370 int i;
2371
2372 RESOLVE(obj);
2373 if (!OBJ_IS_DICT(obj))
2374 return NULL;
2375 if (!key)
2376 return NULL;
2377
2378 i = pdf_dict_finds(ctx, obj, key);
2379 if (i >= 0)
2380 return DICT(obj)->items[i].v;
2381 return NULL;
2382 }
2383
2384 pdf_obj *
2385 pdf_dict_getp(fz_context *ctx, pdf_obj *obj, const char *keys)
2386 {
2387 char buf[256];
2388 char *k, *e;
2389
2390 RESOLVE(obj);
2391 if (!OBJ_IS_DICT(obj))
2392 return NULL;
2393 if (strlen(keys)+1 > 256)
2394 fz_throw(ctx, FZ_ERROR_ARGUMENT, "path too long");
2395
2396 strcpy(buf, keys);
2397
2398 e = buf;
2399 while (*e && obj)
2400 {
2401 k = e;
2402 while (*e != '/' && *e != '\0')
2403 e++;
2404
2405 if (*e == '/')
2406 {
2407 *e = '\0';
2408 e++;
2409 }
2410
2411 obj = pdf_dict_gets(ctx, obj, k);
2412 }
2413
2414 return obj;
2415 }
2416
2417 pdf_obj *
2418 pdf_dict_getl(fz_context *ctx, pdf_obj *obj, ...)
2419 {
2420 va_list keys;
2421 pdf_obj *key;
2422
2423 va_start(keys, obj);
2424
2425 while (obj != NULL && (key = va_arg(keys, pdf_obj *)) != NULL)
2426 {
2427 obj = pdf_dict_get(ctx, obj, key);
2428 }
2429
2430 va_end(keys);
2431 return obj;
2432 }
2433
2434 pdf_obj *
2435 pdf_dict_get(fz_context *ctx, pdf_obj *obj, pdf_obj *key)
2436 {
2437 int i;
2438
2439 RESOLVE(obj);
2440 if (!OBJ_IS_DICT(obj))
2441 return NULL;
2442 if (!OBJ_IS_NAME(key))
2443 return NULL;
2444
2445 if (key < PDF_LIMIT)
2446 i = pdf_dict_find(ctx, obj, key);
2447 else
2448 i = pdf_dict_finds(ctx, obj, pdf_to_name(ctx, key));
2449 if (i >= 0)
2450 return DICT(obj)->items[i].v;
2451 return NULL;
2452 }
2453
2454 pdf_obj *
2455 pdf_dict_getsa(fz_context *ctx, pdf_obj *obj, const char *key, const char *abbrev)
2456 {
2457 pdf_obj *v;
2458 v = pdf_dict_gets(ctx, obj, key);
2459 if (v)
2460 return v;
2461 return pdf_dict_gets(ctx, obj, abbrev);
2462 }
2463
2464 pdf_obj *
2465 pdf_dict_geta(fz_context *ctx, pdf_obj *obj, pdf_obj *key, pdf_obj *abbrev)
2466 {
2467 pdf_obj *v;
2468 /* ISO 32000-2:2020 (PDF 2.0) - abbreviated names take precedence. */
2469 v = pdf_dict_get(ctx, obj, abbrev);
2470 if (v)
2471 return v;
2472 return pdf_dict_get(ctx, obj, key);
2473 }
2474
2475 static void
2476 pdf_dict_get_put(fz_context *ctx, pdf_obj *obj, pdf_obj *key, pdf_obj *val, pdf_obj **old_val)
2477 {
2478 int i;
2479
2480 if (old_val)
2481 *old_val = NULL;
2482
2483 RESOLVE(obj);
2484 if (!OBJ_IS_DICT(obj))
2485 fz_throw(ctx, FZ_ERROR_ARGUMENT, "not a dict (%s)", pdf_objkindstr(obj));
2486 if (!OBJ_IS_NAME(key))
2487 fz_throw(ctx, FZ_ERROR_ARGUMENT, "key is not a name (%s)", pdf_objkindstr(obj));
2488
2489 if (DICT(obj)->len > 100 && !(obj->flags & PDF_FLAGS_SORTED))
2490 pdf_sort_dict(ctx, obj);
2491
2492 if (key < PDF_LIMIT)
2493 i = pdf_dict_find(ctx, obj, key);
2494 else
2495 i = pdf_dict_finds(ctx, obj, pdf_to_name(ctx, key));
2496
2497 prepare_object_for_alteration(ctx, obj, val);
2498
2499 if (i >= 0 && i < DICT(obj)->len)
2500 {
2501 if (DICT(obj)->items[i].v != val)
2502 {
2503 pdf_obj *d = DICT(obj)->items[i].v;
2504 DICT(obj)->items[i].v = pdf_keep_obj(ctx, val);
2505 if (old_val)
2506 *old_val = d;
2507 else
2508 pdf_drop_obj(ctx, d);
2509 }
2510 }
2511 else
2512 {
2513 if (DICT(obj)->len + 1 > DICT(obj)->cap)
2514 pdf_dict_grow(ctx, obj);
2515
2516 i = -1-i;
2517 if ((obj->flags & PDF_FLAGS_SORTED) && DICT(obj)->len > 0)
2518 memmove(&DICT(obj)->items[i + 1],
2519 &DICT(obj)->items[i],
2520 (DICT(obj)->len - i) * sizeof(struct keyval));
2521
2522 DICT(obj)->items[i].k = pdf_keep_obj(ctx, key);
2523 DICT(obj)->items[i].v = pdf_keep_obj(ctx, val);
2524 DICT(obj)->len ++;
2525 }
2526 }
2527
2528 void
2529 pdf_dict_put(fz_context *ctx, pdf_obj *obj, pdf_obj *key, pdf_obj *val)
2530 {
2531 pdf_dict_get_put(ctx, obj, key, val, NULL);
2532 }
2533
2534 void
2535 pdf_dict_put_drop(fz_context *ctx, pdf_obj *obj, pdf_obj *key, pdf_obj *val)
2536 {
2537 fz_try(ctx)
2538 pdf_dict_get_put(ctx, obj, key, val, NULL);
2539 fz_always(ctx)
2540 pdf_drop_obj(ctx, val);
2541 fz_catch(ctx)
2542 fz_rethrow(ctx);
2543 }
2544
2545 void
2546 pdf_dict_get_put_drop(fz_context *ctx, pdf_obj *obj, pdf_obj *key, pdf_obj *val, pdf_obj **old_val)
2547 {
2548 fz_try(ctx)
2549 pdf_dict_get_put(ctx, obj, key, val, old_val);
2550 fz_always(ctx)
2551 pdf_drop_obj(ctx, val);
2552 fz_catch(ctx)
2553 fz_rethrow(ctx);
2554 }
2555
2556 void
2557 pdf_dict_puts(fz_context *ctx, pdf_obj *obj, const char *key, pdf_obj *val)
2558 {
2559 pdf_obj *keyobj;
2560
2561 RESOLVE(obj);
2562 if (!OBJ_IS_DICT(obj))
2563 fz_throw(ctx, FZ_ERROR_ARGUMENT, "not a dict (%s)", pdf_objkindstr(obj));
2564
2565 keyobj = pdf_new_name(ctx, key);
2566
2567 fz_try(ctx)
2568 pdf_dict_put(ctx, obj, keyobj, val);
2569 fz_always(ctx)
2570 pdf_drop_obj(ctx, keyobj);
2571 fz_catch(ctx)
2572 fz_rethrow(ctx);
2573 }
2574
2575 void
2576 pdf_dict_puts_drop(fz_context *ctx, pdf_obj *obj, const char *key, pdf_obj *val)
2577 {
2578 pdf_obj *keyobj;
2579
2580 RESOLVE(obj);
2581 if (!OBJ_IS_DICT(obj))
2582 fz_throw(ctx, FZ_ERROR_ARGUMENT, "not a dict (%s)", pdf_objkindstr(obj));
2583
2584 keyobj = pdf_new_name(ctx, key);
2585
2586 fz_var(keyobj);
2587
2588 fz_try(ctx)
2589 pdf_dict_put(ctx, obj, keyobj, val);
2590 fz_always(ctx)
2591 {
2592 pdf_drop_obj(ctx, keyobj);
2593 pdf_drop_obj(ctx, val);
2594 }
2595 fz_catch(ctx)
2596 {
2597 fz_rethrow(ctx);
2598 }
2599 }
2600
2601 void
2602 pdf_dict_putp(fz_context *ctx, pdf_obj *obj, const char *keys, pdf_obj *val)
2603 {
2604 pdf_document *doc;
2605 char buf[256];
2606 char *k, *e;
2607 pdf_obj *cobj = NULL;
2608
2609 RESOLVE(obj);
2610 if (!OBJ_IS_DICT(obj))
2611 fz_throw(ctx, FZ_ERROR_ARGUMENT, "not a dict (%s)", pdf_objkindstr(obj));
2612 if (strlen(keys)+1 > 256)
2613 fz_throw(ctx, FZ_ERROR_ARGUMENT, "path too long");
2614
2615 doc = DICT(obj)->doc;
2616 strcpy(buf, keys);
2617
2618 e = buf;
2619 while (*e)
2620 {
2621 k = e;
2622 while (*e != '/' && *e != '\0')
2623 e++;
2624
2625 if (*e == '/')
2626 {
2627 *e = '\0';
2628 e++;
2629 }
2630
2631 if (*e)
2632 {
2633 /* Not the last key in the key path. Create subdict if not already there. */
2634 cobj = pdf_dict_gets(ctx, obj, k);
2635 if (!pdf_is_dict(ctx, cobj))
2636 {
2637 cobj = pdf_new_dict(ctx, doc, 1);
2638 fz_try(ctx)
2639 pdf_dict_puts(ctx, obj, k, cobj);
2640 fz_always(ctx)
2641 pdf_drop_obj(ctx, cobj);
2642 fz_catch(ctx)
2643 fz_rethrow(ctx);
2644 }
2645 /* Move to subdict */
2646 obj = cobj;
2647 }
2648 else
2649 {
2650 /* Last key. Use it to store the value */
2651 /* Use val = NULL to request delete */
2652 if (val)
2653 pdf_dict_puts(ctx, obj, k, val);
2654 else
2655 pdf_dict_dels(ctx, obj, k);
2656 }
2657 }
2658 }
2659
2660 void
2661 pdf_dict_putp_drop(fz_context *ctx, pdf_obj *obj, const char *keys, pdf_obj *val)
2662 {
2663 fz_try(ctx)
2664 pdf_dict_putp(ctx, obj, keys, val);
2665 fz_always(ctx)
2666 pdf_drop_obj(ctx, val);
2667 fz_catch(ctx)
2668 fz_rethrow(ctx);
2669 }
2670
2671 static void
2672 pdf_dict_vputl(fz_context *ctx, pdf_obj *obj, pdf_obj *val, va_list keys)
2673 {
2674 pdf_obj *key;
2675 pdf_obj *next_key;
2676 pdf_obj *next_obj;
2677 pdf_document *doc;
2678
2679 RESOLVE(obj);
2680 if (!OBJ_IS_DICT(obj))
2681 fz_throw(ctx, FZ_ERROR_ARGUMENT, "not a dict (%s)", pdf_objkindstr(obj));
2682
2683 doc = DICT(obj)->doc;
2684
2685 key = va_arg(keys, pdf_obj *);
2686 if (key == NULL)
2687 return;
2688
2689 while ((next_key = va_arg(keys, pdf_obj *)) != NULL)
2690 {
2691 next_obj = pdf_dict_get(ctx, obj, key);
2692 if (next_obj == NULL)
2693 goto new_obj;
2694 obj = next_obj;
2695 key = next_key;
2696 }
2697
2698 pdf_dict_put(ctx, obj, key, val);
2699 return;
2700
2701 new_obj:
2702 /* We have to create entries */
2703 do
2704 {
2705 next_obj = pdf_new_dict(ctx, doc, 1);
2706 pdf_dict_put_drop(ctx, obj, key, next_obj);
2707 obj = next_obj;
2708 key = next_key;
2709 }
2710 while ((next_key = va_arg(keys, pdf_obj *)) != NULL);
2711
2712 pdf_dict_put(ctx, obj, key, val);
2713 return;
2714 }
2715
2716 void
2717 pdf_dict_putl(fz_context *ctx, pdf_obj *obj, pdf_obj *val, ...)
2718 {
2719 va_list keys;
2720 va_start(keys, val);
2721
2722 fz_try(ctx)
2723 pdf_dict_vputl(ctx, obj, val, keys);
2724 fz_always(ctx)
2725 va_end(keys);
2726 fz_catch(ctx)
2727 fz_rethrow(ctx);
2728 }
2729
2730 void
2731 pdf_dict_putl_drop(fz_context *ctx, pdf_obj *obj, pdf_obj *val, ...)
2732 {
2733 va_list keys;
2734 va_start(keys, val);
2735
2736 fz_try(ctx)
2737 pdf_dict_vputl(ctx, obj, val, keys);
2738 fz_always(ctx)
2739 {
2740 pdf_drop_obj(ctx, val);
2741 va_end(keys);
2742 }
2743 fz_catch(ctx)
2744 fz_rethrow(ctx);
2745 }
2746
2747 void
2748 pdf_dict_dels(fz_context *ctx, pdf_obj *obj, const char *key)
2749 {
2750 int i;
2751
2752 RESOLVE(obj);
2753 if (!OBJ_IS_DICT(obj))
2754 fz_throw(ctx, FZ_ERROR_ARGUMENT, "not a dict (%s)", pdf_objkindstr(obj));
2755 if (!key)
2756 fz_throw(ctx, FZ_ERROR_ARGUMENT, "key is null");
2757
2758 prepare_object_for_alteration(ctx, obj, NULL);
2759 i = pdf_dict_finds(ctx, obj, key);
2760 if (i >= 0)
2761 {
2762 pdf_drop_obj(ctx, DICT(obj)->items[i].k);
2763 pdf_drop_obj(ctx, DICT(obj)->items[i].v);
2764 obj->flags &= ~PDF_FLAGS_SORTED;
2765 DICT(obj)->items[i] = DICT(obj)->items[DICT(obj)->len-1];
2766 DICT(obj)->len --;
2767 }
2768 }
2769
2770 void
2771 pdf_dict_del(fz_context *ctx, pdf_obj *obj, pdf_obj *key)
2772 {
2773 if (!OBJ_IS_NAME(key))
2774 fz_throw(ctx, FZ_ERROR_ARGUMENT, "key is not a name (%s)", pdf_objkindstr(key));
2775
2776 if (key < PDF_LIMIT)
2777 pdf_dict_dels(ctx, obj, PDF_NAME_LIST[(intptr_t)key]);
2778 else
2779 pdf_dict_dels(ctx, obj, NAME(key)->n);
2780 }
2781
2782 void
2783 pdf_sort_dict(fz_context *ctx, pdf_obj *obj)
2784 {
2785 RESOLVE(obj);
2786 if (!OBJ_IS_DICT(obj))
2787 return;
2788 if (!(obj->flags & PDF_FLAGS_SORTED))
2789 {
2790 qsort(DICT(obj)->items, DICT(obj)->len, sizeof(struct keyval), keyvalcmp);
2791 obj->flags |= PDF_FLAGS_SORTED;
2792 }
2793 }
2794
2795 pdf_obj *
2796 pdf_deep_copy_obj(fz_context *ctx, pdf_obj *obj)
2797 {
2798 if (obj < PDF_LIMIT)
2799 {
2800 return obj;
2801 }
2802 if (obj->kind == PDF_DICT)
2803 {
2804 pdf_document *doc = DICT(obj)->doc;
2805 int n = pdf_dict_len(ctx, obj);
2806 pdf_obj *dict = pdf_new_dict(ctx, doc, n);
2807 int i;
2808
2809 fz_try(ctx)
2810 for (i = 0; i < n; i++)
2811 {
2812 pdf_obj *obj_copy = pdf_deep_copy_obj(ctx, pdf_dict_get_val(ctx, obj, i));
2813 pdf_dict_put_drop(ctx, dict, pdf_dict_get_key(ctx, obj, i), obj_copy);
2814 }
2815 fz_catch(ctx)
2816 {
2817 pdf_drop_obj(ctx, dict);
2818 fz_rethrow(ctx);
2819 }
2820
2821 DICT(dict)->parent_num = DICT(obj)->parent_num;
2822 return dict;
2823 }
2824 else if (obj->kind == PDF_ARRAY)
2825 {
2826 pdf_document *doc = ARRAY(obj)->doc;
2827 int n = pdf_array_len(ctx, obj);
2828 pdf_obj *arr = pdf_new_array(ctx, doc, n);
2829 int i;
2830
2831 fz_try(ctx)
2832 for (i = 0; i < n; i++)
2833 {
2834 pdf_obj *obj_copy = pdf_deep_copy_obj(ctx, pdf_array_get(ctx, obj, i));
2835 pdf_array_push_drop(ctx, arr, obj_copy);
2836 }
2837 fz_catch(ctx)
2838 {
2839 pdf_drop_obj(ctx, arr);
2840 fz_rethrow(ctx);
2841 }
2842
2843 ARRAY(arr)->parent_num = ARRAY(obj)->parent_num;
2844 return arr;
2845 }
2846 else
2847 {
2848 return pdf_keep_obj(ctx, obj);
2849 }
2850 }
2851
2852 /* obj marking and unmarking functions - to avoid infinite recursions. */
2853 int
2854 pdf_obj_marked(fz_context *ctx, pdf_obj *obj)
2855 {
2856 RESOLVE(obj);
2857 if (obj < PDF_LIMIT)
2858 return 0;
2859 return !!(obj->flags & PDF_FLAGS_MARKED);
2860 }
2861
2862 int
2863 pdf_mark_obj(fz_context *ctx, pdf_obj *obj)
2864 {
2865 int marked;
2866 RESOLVE(obj);
2867 if (obj < PDF_LIMIT)
2868 return 0;
2869 marked = !!(obj->flags & PDF_FLAGS_MARKED);
2870 obj->flags |= PDF_FLAGS_MARKED;
2871 return marked;
2872 }
2873
2874 void
2875 pdf_unmark_obj(fz_context *ctx, pdf_obj *obj)
2876 {
2877 RESOLVE(obj);
2878 if (obj < PDF_LIMIT)
2879 return;
2880 obj->flags &= ~PDF_FLAGS_MARKED;
2881 }
2882
2883 int
2884 pdf_cycle(fz_context *ctx, pdf_cycle_list *here, pdf_cycle_list *up, pdf_obj *obj)
2885 {
2886 int num = pdf_to_num(ctx, obj);
2887 if (num > 0)
2888 {
2889 pdf_cycle_list *x = up;
2890 while (x)
2891 {
2892 if (x->num == num)
2893 return 1;
2894 x = x->up;
2895 }
2896 }
2897 here->up = up;
2898 here->num = num;
2899 return 0;
2900 }
2901
2902 pdf_mark_bits *
2903 pdf_new_mark_bits(fz_context *ctx, pdf_document *doc)
2904 {
2905 int n = pdf_xref_len(ctx, doc);
2906 int nb = (n + 7) >> 3;
2907 pdf_mark_bits *marks = fz_calloc(ctx, offsetof(pdf_mark_bits, bits) + nb, 1);
2908 marks->len = n;
2909 return marks;
2910 }
2911
2912 void
2913 pdf_drop_mark_bits(fz_context *ctx, pdf_mark_bits *marks)
2914 {
2915 fz_free(ctx, marks);
2916 }
2917
2918 void pdf_mark_bits_reset(fz_context *ctx, pdf_mark_bits *marks)
2919 {
2920 memset(marks->bits, 0, (marks->len + 7) >> 3);
2921 }
2922
2923 int pdf_mark_bits_set(fz_context *ctx, pdf_mark_bits *marks, pdf_obj *obj)
2924 {
2925 int num = pdf_to_num(ctx, obj);
2926 if (num > 0 && num < marks->len)
2927 {
2928 int x = num >> 3;
2929 int m = 1 << (num & 7);
2930 if (marks->bits[x] & m)
2931 return 1;
2932 marks->bits[x] |= m;
2933 }
2934 return 0;
2935 }
2936
2937 void pdf_mark_bits_clear(fz_context *ctx, pdf_mark_bits *marks, pdf_obj *obj)
2938 {
2939 int num = pdf_to_num(ctx, obj);
2940 if (num > 0 && num < marks->len)
2941 {
2942 int x = num >> 3;
2943 int m = 0xff ^ (1 << (num & 7));
2944 marks->bits[x] &= m;
2945 }
2946 }
2947
2948 int
2949 pdf_mark_list_push(fz_context *ctx, pdf_mark_list *marks, pdf_obj *obj)
2950 {
2951 int num = pdf_to_num(ctx, obj);
2952 int i;
2953
2954 /* If object is not an indirection, then no record to check.
2955 * We must still push it to allow pops to stay in sync. */
2956 if (num > 0)
2957 {
2958 /* Note: this is slow, if the mark list is expected to be big use pdf_mark_bits instead! */
2959 for (i = 0; i < marks->len; ++i)
2960 if (marks->list[i] == num)
2961 return 1;
2962 }
2963
2964 if (marks->len == marks->max)
2965 {
2966 int newsize = marks->max << 1;
2967 if (marks->list == marks->local_list)
2968 {
2969 marks->list = fz_malloc_array(ctx, newsize, int);
2970 memcpy(marks->list, marks->local_list, sizeof(marks->local_list));
2971 }
2972 else
2973 marks->list = fz_realloc_array(ctx, marks->list, newsize, int);
2974 marks->max = newsize;
2975 }
2976
2977 marks->list[marks->len++] = num;
2978 return 0;
2979 }
2980
2981 void
2982 pdf_mark_list_pop(fz_context *ctx, pdf_mark_list *marks)
2983 {
2984 --marks->len;
2985 }
2986
2987 int
2988 pdf_mark_list_check(fz_context *ctx, pdf_mark_list *marks, pdf_obj *obj)
2989 {
2990 if (pdf_mark_list_push(ctx, marks, obj))
2991 return 1;
2992 pdf_mark_list_pop(ctx, marks);
2993
2994 return 0;
2995 }
2996
2997 void
2998 pdf_mark_list_init(fz_context *ctx, pdf_mark_list *marks)
2999 {
3000 marks->len = 0;
3001 marks->max = nelem(marks->local_list);
3002 marks->list = marks->local_list;
3003 }
3004
3005 void
3006 pdf_mark_list_free(fz_context *ctx, pdf_mark_list *marks)
3007 {
3008 if (marks->list != marks->local_list)
3009 fz_free(ctx, marks->list);
3010 marks->len = 0;
3011 marks->max = 0;
3012 marks->list = NULL;
3013 }
3014
3015 void
3016 pdf_set_obj_memo(fz_context *ctx, pdf_obj *obj, int bit, int memo)
3017 {
3018 if (obj < PDF_LIMIT)
3019 return;
3020 bit <<= 1;
3021 obj->flags |= PDF_FLAGS_MEMO_BASE << bit;
3022 if (memo)
3023 obj->flags |= PDF_FLAGS_MEMO_BASE_BOOL << bit;
3024 else
3025 obj->flags &= ~(PDF_FLAGS_MEMO_BASE_BOOL << bit);
3026 }
3027
3028 int
3029 pdf_obj_memo(fz_context *ctx, pdf_obj *obj, int bit, int *memo)
3030 {
3031 if (obj < PDF_LIMIT)
3032 return 0;
3033 bit <<= 1;
3034 if (!(obj->flags & (PDF_FLAGS_MEMO_BASE<<bit)))
3035 return 0;
3036 *memo = !!(obj->flags & (PDF_FLAGS_MEMO_BASE_BOOL<<bit));
3037 return 1;
3038 }
3039
3040 /* obj dirty bit support. */
3041 int pdf_obj_is_dirty(fz_context *ctx, pdf_obj *obj)
3042 {
3043 RESOLVE(obj);
3044 if (obj < PDF_LIMIT)
3045 return 0;
3046 return !!(obj->flags & PDF_FLAGS_DIRTY);
3047 }
3048
3049 void pdf_dirty_obj(fz_context *ctx, pdf_obj *obj)
3050 {
3051 RESOLVE(obj);
3052 if (obj < PDF_LIMIT)
3053 return;
3054 obj->flags |= PDF_FLAGS_DIRTY;
3055 }
3056
3057 void pdf_clean_obj(fz_context *ctx, pdf_obj *obj)
3058 {
3059 RESOLVE(obj);
3060 if (obj < PDF_LIMIT)
3061 return;
3062 obj->flags &= ~PDF_FLAGS_DIRTY;
3063 }
3064
3065 static void
3066 pdf_drop_array(fz_context *ctx, pdf_obj *obj)
3067 {
3068 int i;
3069
3070 for (i = 0; i < DICT(obj)->len; i++)
3071 pdf_drop_obj(ctx, ARRAY(obj)->items[i]);
3072
3073 fz_free(ctx, DICT(obj)->items);
3074 fz_free(ctx, obj);
3075 }
3076
3077 static void
3078 pdf_drop_dict(fz_context *ctx, pdf_obj *obj)
3079 {
3080 int i;
3081
3082 for (i = 0; i < DICT(obj)->len; i++) {
3083 pdf_drop_obj(ctx, DICT(obj)->items[i].k);
3084 pdf_drop_obj(ctx, DICT(obj)->items[i].v);
3085 }
3086
3087 fz_free(ctx, DICT(obj)->items);
3088 fz_free(ctx, obj);
3089 }
3090
3091 pdf_obj *
3092 pdf_keep_obj(fz_context *ctx, pdf_obj *obj)
3093 {
3094 if (obj >= PDF_LIMIT)
3095 return fz_keep_imp16(ctx, obj, &obj->refs);
3096 return obj;
3097 }
3098
3099 void
3100 pdf_drop_obj(fz_context *ctx, pdf_obj *obj)
3101 {
3102 if (obj >= PDF_LIMIT)
3103 {
3104 if (fz_drop_imp16(ctx, obj, &obj->refs))
3105 {
3106 if (obj->kind == PDF_ARRAY)
3107 pdf_drop_array(ctx, obj);
3108 else if (obj->kind == PDF_DICT)
3109 pdf_drop_dict(ctx, obj);
3110 else if (obj->kind == PDF_STRING)
3111 {
3112 fz_free(ctx, STRING(obj)->text);
3113 fz_free(ctx, obj);
3114 }
3115 else
3116 fz_free(ctx, obj);
3117 }
3118 }
3119 }
3120
3121 pdf_obj *
3122 pdf_drop_singleton_obj(fz_context *ctx, pdf_obj *obj)
3123 {
3124 int drop;
3125
3126 /* If an object is < PDF_LIMIT, then it's a 'common' name or
3127 * true or false. No point in dropping these as it
3128 * won't save any memory. */
3129 if (obj < PDF_LIMIT)
3130 return obj;
3131
3132 /* See if it's a singleton object. We can only drop if
3133 * it's a singleton object. If not, just exit leaving
3134 * everything unchanged. */
3135 fz_lock(ctx, FZ_LOCK_ALLOC);
3136 drop = (obj->refs == 1);
3137 fz_unlock(ctx, FZ_LOCK_ALLOC);
3138 if (!drop)
3139 return obj;
3140
3141 /* So drop the object! */
3142 if (obj->kind == PDF_ARRAY)
3143 pdf_drop_array(ctx, obj);
3144 else if (obj->kind == PDF_DICT)
3145 pdf_drop_dict(ctx, obj);
3146 else if (obj->kind == PDF_STRING)
3147 {
3148 fz_free(ctx, STRING(obj)->text);
3149 fz_free(ctx, obj);
3150 }
3151 else
3152 fz_free(ctx, obj);
3153
3154 return NULL;
3155 }
3156
3157 /*
3158 Recurse through the object structure setting the node's parent_num to num.
3159 parent_num is used when a subobject is to be changed during a document edit.
3160 The whole containing hierarchy is moved to the incremental xref section, so
3161 to be later written out as an incremental file update.
3162 */
3163 void
3164 pdf_set_obj_parent(fz_context *ctx, pdf_obj *obj, int num)
3165 {
3166 int n, i;
3167
3168 if (obj < PDF_LIMIT)
3169 return;
3170
3171 switch (obj->kind)
3172 {
3173 case PDF_ARRAY:
3174 ARRAY(obj)->parent_num = num;
3175 n = pdf_array_len(ctx, obj);
3176 for (i = 0; i < n; i++)
3177 pdf_set_obj_parent(ctx, pdf_array_get(ctx, obj, i), num);
3178 break;
3179 case PDF_DICT:
3180 DICT(obj)->parent_num = num;
3181 n = pdf_dict_len(ctx, obj);
3182 for (i = 0; i < n; i++)
3183 pdf_set_obj_parent(ctx, pdf_dict_get_val(ctx, obj, i), num);
3184 break;
3185 }
3186 }
3187
3188 int pdf_obj_parent_num(fz_context *ctx, pdf_obj *obj)
3189 {
3190 if (obj < PDF_LIMIT)
3191 return 0;
3192
3193 switch (obj->kind)
3194 {
3195 case PDF_INDIRECT:
3196 return REF(obj)->num;
3197 case PDF_ARRAY:
3198 return ARRAY(obj)->parent_num;
3199 case PDF_DICT:
3200 return DICT(obj)->parent_num;
3201 default:
3202 return 0;
3203 }
3204 }
3205
3206 /* Pretty printing objects */
3207
3208 struct fmt
3209 {
3210 char *buf; /* original static buffer */
3211 char *ptr; /* buffer we're writing to, maybe dynamically reallocated */
3212 size_t cap;
3213 size_t len;
3214 int indent;
3215 int tight;
3216 int ascii;
3217 int col;
3218 int sep;
3219 int last;
3220 pdf_crypt *crypt;
3221 int num;
3222 int gen;
3223 };
3224
3225 static void fmt_obj(fz_context *ctx, struct fmt *fmt, pdf_obj *obj);
3226
3227 static inline int iswhite(int ch)
3228 {
3229 return
3230 ch == '\000' ||
3231 ch == '\011' ||
3232 ch == '\012' ||
3233 ch == '\014' ||
3234 ch == '\015' ||
3235 ch == '\040';
3236 }
3237
3238 static inline int isdelim(int ch)
3239 {
3240 return
3241 ch == '(' || ch == ')' ||
3242 ch == '<' || ch == '>' ||
3243 ch == '[' || ch == ']' ||
3244 ch == '{' || ch == '}' ||
3245 ch == '/' ||
3246 ch == '%';
3247 }
3248
3249 static inline void fmt_putc(fz_context *ctx, struct fmt *fmt, int c)
3250 {
3251 if (fmt->sep && !isdelim(fmt->last) && !iswhite(fmt->last) && !isdelim(c) && !iswhite(c)) {
3252 fmt->sep = 0;
3253 fmt_putc(ctx, fmt, ' ');
3254 }
3255 fmt->sep = 0;
3256
3257 if (fmt->len >= fmt->cap)
3258 {
3259 fmt->cap *= 2;
3260 if (fmt->buf == fmt->ptr)
3261 {
3262 fmt->ptr = Memento_label(fz_malloc(ctx, fmt->cap), "fmt_ptr");
3263 memcpy(fmt->ptr, fmt->buf, fmt->len);
3264 }
3265 else
3266 {
3267 fmt->ptr = fz_realloc(ctx, fmt->ptr, fmt->cap);
3268 }
3269 }
3270
3271 fmt->ptr[fmt->len] = c;
3272
3273 if (c == '\n')
3274 fmt->col = 0;
3275 else
3276 fmt->col ++;
3277
3278 fmt->len ++;
3279
3280 fmt->last = c;
3281 }
3282
3283 static inline void fmt_indent(fz_context *ctx, struct fmt *fmt)
3284 {
3285 int i = fmt->indent;
3286 while (i--) {
3287 fmt_putc(ctx, fmt, ' ');
3288 fmt_putc(ctx, fmt, ' ');
3289 }
3290 }
3291
3292 static inline void fmt_puts(fz_context *ctx, struct fmt *fmt, char *s)
3293 {
3294 while (*s)
3295 fmt_putc(ctx, fmt, *s++);
3296 }
3297
3298 static inline void fmt_sep(fz_context *ctx, struct fmt *fmt)
3299 {
3300 fmt->sep = 1;
3301 }
3302
3303 static int is_binary_string(fz_context *ctx, pdf_obj *obj)
3304 {
3305 unsigned char *s = (unsigned char *)pdf_to_str_buf(ctx, obj);
3306 size_t i, n = pdf_to_str_len(ctx, obj);
3307 for (i = 0; i < n; ++i)
3308 {
3309 if (s[i] > 126) return 1;
3310 if (s[i] < 32 && (s[i] != '\t' && s[i] != '\n' && s[i] != '\r')) return 1;
3311 }
3312 return 0;
3313 }
3314
3315 static int is_longer_than_hex(fz_context *ctx, pdf_obj *obj)
3316 {
3317 unsigned char *s = (unsigned char *)pdf_to_str_buf(ctx, obj);
3318 size_t i, n = pdf_to_str_len(ctx, obj);
3319 size_t m = 0;
3320 for (i = 0; i < n; ++i)
3321 {
3322 if (s[i] > 126)
3323 m += 4;
3324 else if (s[i] == 0)
3325 m += 4;
3326 else if (strchr("\n\r\t\b\f()\\", s[i]))
3327 m += 2;
3328 else if (s[i] < 32)
3329 m += 4;
3330 else
3331 m += 1;
3332 }
3333 return m > (n * 2);
3334 }
3335
3336 static void fmt_str_out(fz_context *ctx, void *fmt_, const unsigned char *s, size_t n)
3337 {
3338 struct fmt *fmt = (struct fmt *)fmt_;
3339 int c;
3340 size_t i;
3341
3342 for (i = 0; i < n; i++)
3343 {
3344 c = (unsigned char)s[i];
3345 if (c == '\n')
3346 fmt_puts(ctx, fmt, "\\n");
3347 else if (c == '\r')
3348 fmt_puts(ctx, fmt, "\\r");
3349 else if (c == '\t')
3350 fmt_puts(ctx, fmt, "\\t");
3351 else if (c == '\b')
3352 fmt_puts(ctx, fmt, "\\b");
3353 else if (c == '\f')
3354 fmt_puts(ctx, fmt, "\\f");
3355 else if (c == '(')
3356 fmt_puts(ctx, fmt, "\\(");
3357 else if (c == ')')
3358 fmt_puts(ctx, fmt, "\\)");
3359 else if (c == '\\')
3360 fmt_puts(ctx, fmt, "\\\\");
3361 else if (c < 32 || c >= 127) {
3362 fmt_putc(ctx, fmt, '\\');
3363 fmt_putc(ctx, fmt, '0' + ((c / 64) & 7));
3364 fmt_putc(ctx, fmt, '0' + ((c / 8) & 7));
3365 fmt_putc(ctx, fmt, '0' + ((c) & 7));
3366 }
3367 else
3368 fmt_putc(ctx, fmt, c);
3369 }
3370 }
3371
3372 static void fmt_str(fz_context *ctx, struct fmt *fmt, pdf_obj *obj)
3373 {
3374 unsigned char *s = (unsigned char *)pdf_to_str_buf(ctx, obj);
3375 size_t n = pdf_to_str_len(ctx, obj);
3376
3377 fmt_putc(ctx, fmt, '(');
3378 pdf_encrypt_data(ctx, fmt->crypt, fmt->num, fmt->gen, fmt_str_out, fmt, s, n);
3379 fmt_putc(ctx, fmt, ')');
3380 }
3381
3382 static void fmt_hex_out(fz_context *ctx, void *arg, const unsigned char *s, size_t n)
3383 {
3384 struct fmt *fmt = (struct fmt *)arg;
3385 size_t i;
3386 int b, c;
3387
3388 for (i = 0; i < n; i++) {
3389 b = (unsigned char) s[i];
3390 c = (b >> 4) & 0x0f;
3391 fmt_putc(ctx, fmt, c < 0xA ? c + '0' : c + 'A' - 0xA);
3392 c = (b) & 0x0f;
3393 fmt_putc(ctx, fmt, c < 0xA ? c + '0' : c + 'A' - 0xA);
3394 }
3395 }
3396
3397 static void fmt_hex(fz_context *ctx, struct fmt *fmt, pdf_obj *obj)
3398 {
3399 unsigned char *s = (unsigned char *)pdf_to_str_buf(ctx, obj);
3400 size_t n = pdf_to_str_len(ctx, obj);
3401
3402 fmt_putc(ctx, fmt, '<');
3403 pdf_encrypt_data(ctx, fmt->crypt, fmt->num, fmt->gen, fmt_hex_out, fmt, s, n);
3404 fmt_putc(ctx, fmt, '>');
3405 }
3406
3407 static void fmt_name(fz_context *ctx, struct fmt *fmt, pdf_obj *obj)
3408 {
3409 unsigned char *s = (unsigned char *) pdf_to_name(ctx, obj);
3410 int i, c;
3411
3412 fmt_putc(ctx, fmt, '/');
3413
3414 for (i = 0; s[i]; i++)
3415 {
3416 if (isdelim(s[i]) || iswhite(s[i]) ||
3417 s[i] == '#' || s[i] < 32 || s[i] >= 127)
3418 {
3419 fmt_putc(ctx, fmt, '#');
3420 c = (s[i] >> 4) & 0xf;
3421 fmt_putc(ctx, fmt, c < 0xA ? c + '0' : c + 'A' - 0xA);
3422 c = s[i] & 0xf;
3423 fmt_putc(ctx, fmt, c < 0xA ? c + '0' : c + 'A' - 0xA);
3424 }
3425 else
3426 {
3427 fmt_putc(ctx, fmt, s[i]);
3428 }
3429 }
3430
3431 fmt->sep = 1;
3432 }
3433
3434 static void fmt_array(fz_context *ctx, struct fmt *fmt, pdf_obj *obj)
3435 {
3436 int i, n;
3437
3438 n = pdf_array_len(ctx, obj);
3439 if (fmt->tight) {
3440 fmt_putc(ctx, fmt, '[');
3441 for (i = 0; i < n; i++) {
3442 fmt_obj(ctx, fmt, pdf_array_get(ctx, obj, i));
3443 }
3444 fmt_putc(ctx, fmt, ']');
3445 }
3446 else {
3447 fmt_putc(ctx, fmt, '[');
3448 fmt->indent ++;
3449 for (i = 0; i < n; i++) {
3450 if (fmt->col > 60) {
3451 fmt_putc(ctx, fmt, '\n');
3452 fmt_indent(ctx, fmt);
3453 } else {
3454 fmt_putc(ctx, fmt, ' ');
3455 }
3456 fmt_obj(ctx, fmt, pdf_array_get(ctx, obj, i));
3457 }
3458 fmt->indent --;
3459 fmt_putc(ctx, fmt, ' ');
3460 fmt_putc(ctx, fmt, ']');
3461 fmt_sep(ctx, fmt);
3462 }
3463 }
3464
3465 static int is_signature(fz_context *ctx, pdf_obj *obj)
3466 {
3467 if (pdf_dict_get(ctx, obj, PDF_NAME(Type)) == PDF_NAME(Sig))
3468 if (pdf_dict_get(ctx, obj, PDF_NAME(Contents)) && pdf_dict_get(ctx, obj, PDF_NAME(ByteRange)) && pdf_dict_get(ctx, obj, PDF_NAME(Filter)))
3469 return 1;
3470 return 0;
3471 }
3472
3473 static void fmt_dict(fz_context *ctx, struct fmt *fmt, pdf_obj *obj)
3474 {
3475 int i, n;
3476 pdf_obj *key, *val;
3477 int skip = 0;
3478 pdf_obj *type = pdf_dict_get(ctx, obj, PDF_NAME(Type));
3479
3480 n = pdf_dict_len(ctx, obj);
3481
3482 /* Open the dictionary.
3483 * We spot /Type and /Subtype here so we can sent those first,
3484 * in order. The hope is this will improve compression, because
3485 * we'll be consistently sending those first. */
3486 if (fmt->tight) {
3487 fmt_puts(ctx, fmt, "<<");
3488 if (type)
3489 {
3490 pdf_obj *subtype = pdf_dict_get(ctx, obj, PDF_NAME(Subtype));
3491 fmt_obj(ctx, fmt, PDF_NAME(Type));
3492 fmt_obj(ctx, fmt, type);
3493 if (subtype)
3494 {
3495 fmt_obj(ctx, fmt, PDF_NAME(Subtype));
3496 fmt_obj(ctx, fmt, subtype);
3497 skip |= 2; /* Skip Subtype */
3498 }
3499 skip |= 1; /* Skip Type */
3500 }
3501
3502 /* Now send all the key/value pairs except the ones we have decided to
3503 * skip. */
3504 for (i = 0; i < n; i++) {
3505 key = pdf_dict_get_key(ctx, obj, i);
3506 if (skip)
3507 {
3508 if ((skip & 1) != 0 && key == PDF_NAME(Type))
3509 continue;
3510 if ((skip & 2) != 0 && key == PDF_NAME(Subtype))
3511 continue;
3512 }
3513 val = pdf_dict_get_val(ctx, obj, i);
3514 fmt_obj(ctx, fmt, key);
3515 if (key == PDF_NAME(Contents) && is_signature(ctx, obj))
3516 {
3517 pdf_crypt *crypt = fmt->crypt;
3518 fz_try(ctx)
3519 {
3520 fmt->crypt = NULL;
3521 fmt_obj(ctx, fmt, val);
3522 }
3523 fz_always(ctx)
3524 fmt->crypt = crypt;
3525 fz_catch(ctx)
3526 fz_rethrow(ctx);
3527 }
3528 else
3529 fmt_obj(ctx, fmt, val);
3530 }
3531
3532 fmt_puts(ctx, fmt, ">>");
3533 }
3534 else /* Not tight, send it simply. */
3535 {
3536 fmt_puts(ctx, fmt, "<<\n");
3537 fmt->indent ++;
3538 for (i = 0; i < n; i++) {
3539 key = pdf_dict_get_key(ctx, obj, i);
3540 val = pdf_dict_get_val(ctx, obj, i);
3541 fmt_indent(ctx, fmt);
3542 fmt_obj(ctx, fmt, key);
3543 fmt_putc(ctx, fmt, ' ');
3544 if (!pdf_is_indirect(ctx, val) && pdf_is_array(ctx, val))
3545 fmt->indent ++;
3546 if (key == PDF_NAME(Contents) && is_signature(ctx, obj))
3547 {
3548 pdf_crypt *crypt = fmt->crypt;
3549 fz_try(ctx)
3550 {
3551 fmt->crypt = NULL;
3552 fmt_obj(ctx, fmt, val);
3553 }
3554 fz_always(ctx)
3555 fmt->crypt = crypt;
3556 fz_catch(ctx)
3557 fz_rethrow(ctx);
3558 }
3559 else
3560 fmt_obj(ctx, fmt, val);
3561 fmt_putc(ctx, fmt, '\n');
3562 if (!pdf_is_indirect(ctx, val) && pdf_is_array(ctx, val))
3563 fmt->indent --;
3564 }
3565 fmt->indent --;
3566 fmt_indent(ctx, fmt);
3567 fmt_puts(ctx, fmt, ">>");
3568 }
3569 }
3570
3571 static void fmt_obj(fz_context *ctx, struct fmt *fmt, pdf_obj *obj)
3572 {
3573 char buf[256];
3574
3575 if (obj == PDF_NULL)
3576 {
3577 fmt_puts(ctx, fmt, "null");
3578 fmt->sep = 1;
3579 return;
3580 }
3581 else if (obj == PDF_TRUE)
3582 {
3583 fmt_puts(ctx, fmt, "true");
3584 fmt->sep = 1;
3585 return;
3586 }
3587 else if (obj == PDF_FALSE)
3588 {
3589 fmt_puts(ctx, fmt, "false");
3590 fmt->sep = 1;
3591 return;
3592 }
3593 else if (pdf_is_indirect(ctx, obj))
3594 {
3595 int n = pdf_to_num(ctx, obj);
3596 int g = pdf_to_gen(ctx, obj);
3597 fz_snprintf(buf, sizeof buf, "%d %d R", n, g);
3598 fmt_puts(ctx, fmt, buf);
3599 fmt->sep = 1;
3600 return;
3601 }
3602 else if (pdf_is_int(ctx, obj))
3603 {
3604 fz_snprintf(buf, sizeof buf, "%ld", pdf_to_int64(ctx, obj));
3605 fmt_puts(ctx, fmt, buf);
3606 fmt->sep = 1;
3607 return;
3608 }
3609 else if (pdf_is_real(ctx, obj))
3610 {
3611 float f = pdf_to_real(ctx, obj);
3612 if (f == (int)f)
3613 fz_snprintf(buf, sizeof buf, "%d", (int)f);
3614 else
3615 fz_snprintf(buf, sizeof buf, "%g", f);
3616 fmt_puts(ctx, fmt, buf);
3617 fmt->sep = 1;
3618 return;
3619 }
3620 else if (pdf_is_string(ctx, obj))
3621 {
3622 unsigned char *str = (unsigned char *)pdf_to_str_buf(ctx, obj);
3623 if (fmt->crypt
3624 || (fmt->ascii && is_binary_string(ctx, obj))
3625 || (str[0]==0xff && str[1]==0xfe)
3626 || (str[0]==0xfe && str[1] == 0xff)
3627 || is_longer_than_hex(ctx, obj)
3628 )
3629 fmt_hex(ctx, fmt, obj);
3630 else
3631 fmt_str(ctx, fmt, obj);
3632 }
3633 else if (pdf_is_name(ctx, obj))
3634 fmt_name(ctx, fmt, obj);
3635 else if (pdf_is_array(ctx, obj))
3636 fmt_array(ctx, fmt, obj);
3637 else if (pdf_is_dict(ctx, obj))
3638 fmt_dict(ctx, fmt, obj);
3639 else
3640 fmt_puts(ctx, fmt, "<unknown object>");
3641 }
3642
3643 static char *
3644 pdf_sprint_encrypted_obj(fz_context *ctx, char *buf, size_t cap, size_t *len, pdf_obj *obj, int tight, int ascii, pdf_crypt *crypt, int num, int gen, int *sep)
3645 {
3646 struct fmt fmt;
3647
3648 fmt.indent = 0;
3649 fmt.col = 0;
3650 fmt.sep = sep ? *sep : 0;
3651 fmt.last = 0;
3652
3653 if (!buf || cap == 0)
3654 {
3655 fmt.cap = 1024;
3656 fmt.buf = NULL;
3657 fmt.ptr = Memento_label(fz_malloc(ctx, fmt.cap), "fmt_buf");
3658 }
3659 else
3660 {
3661 fmt.cap = cap;
3662 fmt.buf = buf;
3663 fmt.ptr = buf;
3664 }
3665
3666 fmt.tight = tight;
3667 fmt.ascii = ascii;
3668 fmt.len = 0;
3669 fmt.crypt = crypt;
3670 fmt.num = num;
3671 fmt.gen = gen;
3672
3673 fz_try(ctx)
3674 {
3675 fmt_obj(ctx, &fmt, obj);
3676 if (sep)
3677 *sep = fmt.sep;
3678 fmt.sep = 0;
3679 fmt_putc(ctx, &fmt, 0);
3680 }
3681 fz_catch(ctx)
3682 {
3683 if (!buf || cap == 0)
3684 fz_free(ctx, fmt.ptr);
3685 fz_rethrow(ctx);
3686 }
3687
3688 return *len = fmt.len-1, fmt.ptr;
3689 }
3690
3691 char *
3692 pdf_sprint_obj(fz_context *ctx, char *buf, size_t cap, size_t *len, pdf_obj *obj, int tight, int ascii)
3693 {
3694 return pdf_sprint_encrypted_obj(ctx, buf, cap, len, obj, tight, ascii, NULL, 0, 0, NULL);
3695 }
3696
3697 void pdf_print_encrypted_obj(fz_context *ctx, fz_output *out, pdf_obj *obj, int tight, int ascii, pdf_crypt *crypt, int num, int gen, int *sep)
3698 {
3699 char buf[1024];
3700 char *ptr;
3701 size_t n;
3702
3703 ptr = pdf_sprint_encrypted_obj(ctx, buf, sizeof buf, &n, obj, tight, ascii, crypt, num, gen, sep);
3704 fz_try(ctx)
3705 fz_write_data(ctx, out, ptr, n);
3706 fz_always(ctx)
3707 if (ptr != buf)
3708 fz_free(ctx, ptr);
3709 fz_catch(ctx)
3710 fz_rethrow(ctx);
3711 }
3712
3713 void pdf_print_obj(fz_context *ctx, fz_output *out, pdf_obj *obj, int tight, int ascii)
3714 {
3715 pdf_print_encrypted_obj(ctx, out, obj, tight, ascii, NULL, 0, 0, NULL);
3716 }
3717
3718 void pdf_debug_obj(fz_context *ctx, pdf_obj *obj)
3719 {
3720 pdf_print_obj(ctx, fz_stddbg(ctx), pdf_resolve_indirect(ctx, obj), 0, 0);
3721 }
3722
3723 void pdf_debug_ref(fz_context *ctx, pdf_obj *obj)
3724 {
3725 fz_output *out = fz_stddbg(ctx);
3726 pdf_print_obj(ctx, out, obj, 0, 0);
3727 fz_write_byte(ctx, out, '\n');
3728 }
3729
3730 int pdf_obj_refs(fz_context *ctx, pdf_obj *obj)
3731 {
3732 if (obj < PDF_LIMIT)
3733 return 0;
3734 return obj->refs;
3735 }
3736
3737 /* Convenience functions */
3738
3739 /*
3740 Uses Floyd's cycle finding algorithm, modified to avoid starting
3741 the 'slow' pointer for a while.
3742
3743 https://www.geeksforgeeks.org/floyds-cycle-finding-algorithm/
3744 */
3745 pdf_obj *
3746 pdf_dict_get_inheritable(fz_context *ctx, pdf_obj *node, pdf_obj *key)
3747 {
3748 pdf_obj *slow = node;
3749 int halfbeat = 11; /* Don't start moving slow pointer for a while. */
3750
3751 while (node)
3752 {
3753 pdf_obj *val = pdf_dict_get(ctx, node, key);
3754 if (val)
3755 return val;
3756 node = pdf_dict_get(ctx, node, PDF_NAME(Parent));
3757 if (node == slow)
3758 fz_throw(ctx, FZ_ERROR_FORMAT, "cycle in resources");
3759 if (--halfbeat == 0)
3760 {
3761 slow = pdf_dict_get(ctx, slow, PDF_NAME(Parent));
3762 halfbeat = 2;
3763 }
3764 }
3765
3766 return NULL;
3767 }
3768
3769 pdf_obj *
3770 pdf_dict_getp_inheritable(fz_context *ctx, pdf_obj *node, const char *path)
3771 {
3772 pdf_obj *slow = node;
3773 int halfbeat = 11; /* Don't start moving slow pointer for a while. */
3774
3775 while (node)
3776 {
3777 pdf_obj *val = pdf_dict_getp(ctx, node, path);
3778 if (val)
3779 return val;
3780 node = pdf_dict_get(ctx, node, PDF_NAME(Parent));
3781 if (node == slow)
3782 fz_throw(ctx, FZ_ERROR_FORMAT, "cycle in resources");
3783 if (--halfbeat == 0)
3784 {
3785 slow = pdf_dict_get(ctx, slow, PDF_NAME(Parent));
3786 halfbeat = 2;
3787 }
3788 }
3789
3790 return NULL;
3791 }
3792
3793 pdf_obj *
3794 pdf_dict_gets_inheritable(fz_context *ctx, pdf_obj *node, const char *key)
3795 {
3796 pdf_obj *slow = node;
3797 int halfbeat = 11; /* Don't start moving slow pointer for a while. */
3798
3799 while (node)
3800 {
3801 pdf_obj *val = pdf_dict_gets(ctx, node, key);
3802 if (val)
3803 return val;
3804 node = pdf_dict_get(ctx, node, PDF_NAME(Parent));
3805 if (node == slow)
3806 fz_throw(ctx, FZ_ERROR_FORMAT, "cycle in resources");
3807 if (--halfbeat == 0)
3808 {
3809 slow = pdf_dict_get(ctx, slow, PDF_NAME(Parent));
3810 halfbeat = 2;
3811 }
3812 }
3813
3814 return NULL;
3815 }
3816
3817
3818 void pdf_dict_put_bool(fz_context *ctx, pdf_obj *dict, pdf_obj *key, int x)
3819 {
3820 pdf_dict_put(ctx, dict, key, x ? PDF_TRUE : PDF_FALSE);
3821 }
3822
3823 void pdf_dict_put_int(fz_context *ctx, pdf_obj *dict, pdf_obj *key, int64_t x)
3824 {
3825 pdf_dict_put_drop(ctx, dict, key, pdf_new_int(ctx, x));
3826 }
3827
3828 void pdf_dict_put_real(fz_context *ctx, pdf_obj *dict, pdf_obj *key, double x)
3829 {
3830 pdf_dict_put_drop(ctx, dict, key, pdf_new_real(ctx, x));
3831 }
3832
3833 void pdf_dict_put_name(fz_context *ctx, pdf_obj *dict, pdf_obj *key, const char *x)
3834 {
3835 pdf_dict_put_drop(ctx, dict, key, pdf_new_name(ctx, x));
3836 }
3837
3838 void pdf_dict_put_string(fz_context *ctx, pdf_obj *dict, pdf_obj *key, const char *x, size_t n)
3839 {
3840 pdf_dict_put_drop(ctx, dict, key, pdf_new_string(ctx, x, n));
3841 }
3842
3843 void pdf_dict_put_text_string(fz_context *ctx, pdf_obj *dict, pdf_obj *key, const char *x)
3844 {
3845 pdf_dict_put_drop(ctx, dict, key, pdf_new_text_string(ctx, x));
3846 }
3847
3848 void pdf_dict_put_indirect(fz_context *ctx, pdf_obj *dict, pdf_obj *key, int num)
3849 {
3850 pdf_dict_put_drop(ctx, dict, key, pdf_new_indirect(ctx, pdf_get_bound_document(ctx, dict), num, 0));
3851 }
3852
3853 void pdf_dict_put_point(fz_context *ctx, pdf_obj *dict, pdf_obj *key, fz_point x)
3854 {
3855 pdf_dict_put_drop(ctx, dict, key, pdf_new_point(ctx, pdf_get_bound_document(ctx, dict), x));
3856 }
3857
3858 void pdf_dict_put_rect(fz_context *ctx, pdf_obj *dict, pdf_obj *key, fz_rect x)
3859 {
3860 pdf_dict_put_drop(ctx, dict, key, pdf_new_rect(ctx, pdf_get_bound_document(ctx, dict), x));
3861 }
3862
3863 void pdf_dict_put_matrix(fz_context *ctx, pdf_obj *dict, pdf_obj *key, fz_matrix x)
3864 {
3865 pdf_dict_put_drop(ctx, dict, key, pdf_new_matrix(ctx, pdf_get_bound_document(ctx, dict), x));
3866 }
3867
3868 void pdf_dict_put_date(fz_context *ctx, pdf_obj *dict, pdf_obj *key, int64_t time)
3869 {
3870 pdf_dict_put_drop(ctx, dict, key, pdf_new_date(ctx, pdf_get_bound_document(ctx, dict), time));
3871 }
3872
3873 pdf_obj *pdf_dict_put_array(fz_context *ctx, pdf_obj *dict, pdf_obj *key, int initial)
3874 {
3875 pdf_obj *obj = pdf_new_array(ctx, pdf_get_bound_document(ctx, dict), initial);
3876 pdf_dict_put_drop(ctx, dict, key, obj);
3877 return obj;
3878 }
3879
3880 pdf_obj *pdf_dict_put_dict(fz_context *ctx, pdf_obj *dict, pdf_obj *key, int initial)
3881 {
3882 pdf_obj *obj = pdf_new_dict(ctx, pdf_get_bound_document(ctx, dict), initial);
3883 pdf_dict_put_drop(ctx, dict, key, obj);
3884 return obj;
3885 }
3886
3887 pdf_obj *pdf_dict_puts_dict(fz_context *ctx, pdf_obj *dict, const char *key, int initial)
3888 {
3889 pdf_obj *obj = pdf_new_dict(ctx, pdf_get_bound_document(ctx, dict), initial);
3890 pdf_dict_puts_drop(ctx, dict, key, obj);
3891 return obj;
3892 }
3893
3894 void pdf_array_push_bool(fz_context *ctx, pdf_obj *array, int x)
3895 {
3896 pdf_array_push(ctx, array, x ? PDF_TRUE : PDF_FALSE);
3897 }
3898
3899 void pdf_array_push_int(fz_context *ctx, pdf_obj *array, int64_t x)
3900 {
3901 pdf_array_push_drop(ctx, array, pdf_new_int(ctx, x));
3902 }
3903
3904 void pdf_array_push_real(fz_context *ctx, pdf_obj *array, double x)
3905 {
3906 pdf_array_push_drop(ctx, array, pdf_new_real(ctx, x));
3907 }
3908
3909 void pdf_array_push_name(fz_context *ctx, pdf_obj *array, const char *x)
3910 {
3911 pdf_array_push_drop(ctx, array, pdf_new_name(ctx, x));
3912 }
3913
3914 void pdf_array_push_string(fz_context *ctx, pdf_obj *array, const char *x, size_t n)
3915 {
3916 pdf_array_push_drop(ctx, array, pdf_new_string(ctx, x, n));
3917 }
3918
3919 void pdf_array_push_text_string(fz_context *ctx, pdf_obj *array, const char *x)
3920 {
3921 pdf_array_push_drop(ctx, array, pdf_new_text_string(ctx, x));
3922 }
3923
3924 pdf_obj *pdf_array_push_array(fz_context *ctx, pdf_obj *array, int initial)
3925 {
3926 pdf_obj *obj = pdf_new_array(ctx, pdf_get_bound_document(ctx, array), initial);
3927 pdf_array_push_drop(ctx, array, obj);
3928 return obj;
3929 }
3930
3931 pdf_obj *pdf_array_push_dict(fz_context *ctx, pdf_obj *array, int initial)
3932 {
3933 pdf_obj *obj = pdf_new_dict(ctx, pdf_get_bound_document(ctx, array), initial);
3934 pdf_array_push_drop(ctx, array, obj);
3935 return obj;
3936 }
3937
3938 void pdf_array_put_bool(fz_context *ctx, pdf_obj *array, int i, int x)
3939 {
3940 pdf_array_put(ctx, array, i, x ? PDF_TRUE : PDF_FALSE);
3941 }
3942
3943 void pdf_array_put_int(fz_context *ctx, pdf_obj *array, int i, int64_t x)
3944 {
3945 pdf_array_put_drop(ctx, array, i, pdf_new_int(ctx, x));
3946 }
3947
3948 void pdf_array_put_real(fz_context *ctx, pdf_obj *array, int i, double x)
3949 {
3950 pdf_array_put_drop(ctx, array, i, pdf_new_real(ctx, x));
3951 }
3952
3953 void pdf_array_put_name(fz_context *ctx, pdf_obj *array, int i, const char *x)
3954 {
3955 pdf_array_put_drop(ctx, array, i, pdf_new_name(ctx, x));
3956 }
3957
3958 void pdf_array_put_string(fz_context *ctx, pdf_obj *array, int i, const char *x, size_t n)
3959 {
3960 pdf_array_put_drop(ctx, array, i, pdf_new_string(ctx, x, n));
3961 }
3962
3963 void pdf_array_put_text_string(fz_context *ctx, pdf_obj *array, int i, const char *x)
3964 {
3965 pdf_array_put_drop(ctx, array, i, pdf_new_text_string(ctx, x));
3966 }
3967
3968 pdf_obj *pdf_array_put_array(fz_context *ctx, pdf_obj *array, int i, int initial)
3969 {
3970 pdf_obj *obj = pdf_new_array(ctx, pdf_get_bound_document(ctx, array), initial);
3971 pdf_array_put_drop(ctx, array, i, obj);
3972 return obj;
3973 }
3974
3975 pdf_obj *pdf_array_put_dict(fz_context *ctx, pdf_obj *array, int i, int initial)
3976 {
3977 pdf_obj *obj = pdf_new_dict(ctx, pdf_get_bound_document(ctx, array), initial);
3978 pdf_array_put_drop(ctx, array, i, obj);
3979 return obj;
3980 }
3981
3982 int pdf_dict_get_bool(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
3983 {
3984 return pdf_to_bool(ctx, pdf_dict_get(ctx, dict, key));
3985 }
3986
3987 int pdf_dict_get_bool_default(fz_context *ctx, pdf_obj *dict, pdf_obj *key, int def)
3988 {
3989 return pdf_to_bool_default(ctx, pdf_dict_get(ctx, dict, key), def);
3990 }
3991
3992 int pdf_dict_get_int(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
3993 {
3994 return pdf_to_int(ctx, pdf_dict_get(ctx, dict, key));
3995 }
3996
3997 int pdf_dict_get_int_default(fz_context *ctx, pdf_obj *dict, pdf_obj *key, int def)
3998 {
3999 return pdf_to_int_default(ctx, pdf_dict_get(ctx, dict, key), def);
4000 }
4001
4002 int64_t pdf_dict_get_int64(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
4003 {
4004 return pdf_to_int64(ctx, pdf_dict_get(ctx, dict, key));
4005 }
4006
4007 float pdf_dict_get_real(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
4008 {
4009 return pdf_to_real(ctx, pdf_dict_get(ctx, dict, key));
4010 }
4011
4012 float pdf_dict_get_real_default(fz_context *ctx, pdf_obj *dict, pdf_obj *key, float def)
4013 {
4014 return pdf_to_real_default(ctx, pdf_dict_get(ctx, dict, key), def);
4015 }
4016
4017 const char *pdf_dict_get_name(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
4018 {
4019 return pdf_to_name(ctx, pdf_dict_get(ctx, dict, key));
4020 }
4021
4022 const char *pdf_dict_get_string(fz_context *ctx, pdf_obj *dict, pdf_obj *key, size_t *sizep)
4023 {
4024 return pdf_to_string(ctx, pdf_dict_get(ctx, dict, key), sizep);
4025 }
4026
4027 const char *pdf_dict_get_text_string(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
4028 {
4029 return pdf_to_text_string(ctx, pdf_dict_get(ctx, dict, key));
4030 }
4031
4032 const char *pdf_dict_get_text_string_opt(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
4033 {
4034 pdf_obj *obj = pdf_dict_get(ctx, dict, key);
4035 if (!pdf_is_string(ctx, obj))
4036 return NULL;
4037 return pdf_to_text_string(ctx, obj);
4038 }
4039
4040 fz_point pdf_dict_get_point(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
4041 {
4042 return pdf_to_point(ctx, pdf_dict_get(ctx, dict, key), 0);
4043 }
4044
4045 fz_rect pdf_dict_get_rect(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
4046 {
4047 return pdf_to_rect(ctx, pdf_dict_get(ctx, dict, key));
4048 }
4049
4050 fz_matrix pdf_dict_get_matrix(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
4051 {
4052 return pdf_to_matrix(ctx, pdf_dict_get(ctx, dict, key));
4053 }
4054
4055 int pdf_dict_get_inheritable_bool(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
4056 {
4057 return pdf_to_bool(ctx, pdf_dict_get_inheritable(ctx, dict, key));
4058 }
4059
4060 int pdf_dict_get_inheritable_int(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
4061 {
4062 return pdf_to_int(ctx, pdf_dict_get_inheritable(ctx, dict, key));
4063 }
4064
4065 int64_t pdf_dict_get_inheritable_int64(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
4066 {
4067 return pdf_to_int64(ctx, pdf_dict_get_inheritable(ctx, dict, key));
4068 }
4069
4070 float pdf_dict_get_inheritable_real(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
4071 {
4072 return pdf_to_real(ctx, pdf_dict_get_inheritable(ctx, dict, key));
4073 }
4074
4075 const char *pdf_dict_get_inheritable_name(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
4076 {
4077 return pdf_to_name(ctx, pdf_dict_get_inheritable(ctx, dict, key));
4078 }
4079
4080 const char *pdf_dict_get_inheritable_string(fz_context *ctx, pdf_obj *dict, pdf_obj *key, size_t *sizep)
4081 {
4082 return pdf_to_string(ctx, pdf_dict_get_inheritable(ctx, dict, key), sizep);
4083 }
4084
4085 const char *pdf_dict_get_inheritable_text_string(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
4086 {
4087 return pdf_to_text_string(ctx, pdf_dict_get_inheritable(ctx, dict, key));
4088 }
4089
4090 fz_rect pdf_dict_get_inheritable_rect(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
4091 {
4092 return pdf_to_rect(ctx, pdf_dict_get_inheritable(ctx, dict, key));
4093 }
4094
4095 fz_matrix pdf_dict_get_inheritable_matrix(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
4096 {
4097 return pdf_to_matrix(ctx, pdf_dict_get_inheritable(ctx, dict, key));
4098 }
4099
4100 int64_t pdf_dict_get_inheritable_date(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
4101 {
4102 return pdf_to_date(ctx, pdf_dict_get_inheritable(ctx, dict, key));
4103 }
4104
4105 int64_t pdf_dict_get_date(fz_context *ctx, pdf_obj *dict, pdf_obj *key)
4106 {
4107 return pdf_to_date(ctx, pdf_dict_get(ctx, dict, key));
4108 }
4109
4110 int pdf_array_get_bool(fz_context *ctx, pdf_obj *array, int index)
4111 {
4112 return pdf_to_bool(ctx, pdf_array_get(ctx, array, index));
4113 }
4114
4115 int pdf_array_get_int(fz_context *ctx, pdf_obj *array, int index)
4116 {
4117 return pdf_to_int(ctx, pdf_array_get(ctx, array, index));
4118 }
4119
4120 float pdf_array_get_real(fz_context *ctx, pdf_obj *array, int index)
4121 {
4122 return pdf_to_real(ctx, pdf_array_get(ctx, array, index));
4123 }
4124
4125 const char *pdf_array_get_name(fz_context *ctx, pdf_obj *array, int index)
4126 {
4127 return pdf_to_name(ctx, pdf_array_get(ctx, array, index));
4128 }
4129
4130 const char *pdf_array_get_string(fz_context *ctx, pdf_obj *array, int index, size_t *sizep)
4131 {
4132 return pdf_to_string(ctx, pdf_array_get(ctx, array, index), sizep);
4133 }
4134
4135 const char *pdf_array_get_text_string(fz_context *ctx, pdf_obj *array, int index)
4136 {
4137 return pdf_to_text_string(ctx, pdf_array_get(ctx, array, index));
4138 }
4139
4140 fz_rect pdf_array_get_rect(fz_context *ctx, pdf_obj *array, int index)
4141 {
4142 return pdf_to_rect(ctx, pdf_array_get(ctx, array, index));
4143 }
4144
4145 fz_matrix pdf_array_get_matrix(fz_context *ctx, pdf_obj *array, int index)
4146 {
4147 return pdf_to_matrix(ctx, pdf_array_get(ctx, array, index));
4148 }
4149
4150 #ifndef NDEBUG
4151 void pdf_verify_name_table_sanity(void)
4152 {
4153 int i;
4154
4155 for (i = PDF_ENUM_FALSE+1; i < PDF_ENUM_LIMIT-1; i++)
4156 {
4157 assert(strcmp(PDF_NAME_LIST[i], PDF_NAME_LIST[i+1]) < 0);
4158 }
4159 }
4160 #endif