Coverage Report

Created: 2025-12-31 07:06

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